zhangguanzhang's Blog

keepalived Locking pid file error 22 - Invalid argument

字数统计: 752阅读时长: 3 min
2025/10/17

由来

现场部署业务后发现访问有问题,排查后发现业务网关访问 etcd 的 keepalived IPVS svc 不通

排查

基础排查

1
2
$ curl 169.254.20.4:12379
curl: (7) Failed to connect to 169.254.20.4 port 12379: Connection refused

这个是我们 etcd 的 svc 的,curl real server 正常:

1
$ curl <本机IP>:12379

然后看了下 iptables -t filter -S 无额外规则,怕客户安全加固啥的前面 insert 了规则影响。

1
2
3
$ sysctl --all |& grep vs.conn
net.ipv4.vs.conn_reuse_mode = 1
net.ipv4.vs.conntrack = 1

vs.conntrack 正常,不是 0,查看下 ipvs 规则:

1
$ docker exec keepalived-ipvs ipvsadm -ln | grep -A3 169.254.20.4

现场说为空,让去掉 grep 直接看也是为空:

1
2
3
4
$ docker exec keepalived-ipvs ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn

让看下 keepalived 日志,发现有个错误:

1
2
3
4
5
daemon is already running
Locking pid file /run/keepalived.pid error 22 - Invalid argument
Opening file '/etc/keepalived/conf.d/xxx.conf'.
Opening file '/etc/keepalived/conf.d/xxx2.conf'.
...

源码

因为 keepalived 容器需要操作 lvs 规则,所以是有 privileged: true 的,感觉还是内核相关问题导致的,那就看源码了。我们用的 keepalived 版本是最新的 v2.3.4 ,搜索源码 Locking pid file 搜到:

1
2
3
4
5
6
7
8
9
10
11
12
13
// https://github.com/acassen/keepalived/blob/v2.3.4/keepalived/core/pidfile.c#L183-L194
#if HAVE_DECL_F_OFD_SETLK == 1
fl.l_pid = 0;
while ((ret = fcntl(pidf->fd, F_OFD_SETLK, &fl)) && errno == EINTR);
if (ret) {
if (errno == EAGAIN)
log_message(LOG_INFO, "Another process has pid file %s locked", pidf->path);
else
log_message(LOG_INFO, "Locking pid file %s error %d - %m", pidf->path, errno);

break;
}
#endif

看这个宏定义内的代码就是 fcntl 使用 F_OFD_SETLK 针对 Pid 文件上锁,这个特性是内核特性,现场是 CentOS 7.4 ,内核版本 3.10.0-693.el7.x86_64,想看下这块提交,于是下载源码后找到下面俩个 commmit:

第一个 commit 是增加 Pid 锁,第二个是允许宏定义不用这个特性在老系统上构建,利用 autoconf 检测支持 F_OFD_SETLK 不。

编译

因为我们使用的是容器化部署,基础镜像换成欧拉了,而欧拉的包管理安装的 keepalived 版本太低,所以我是 Dockerfile 内编译安装的 keepalived,而构建这个镜像的机器的内核比较高,只能构建的时候传递选项关闭了。

影响面也不需要关注,因为是容器,并且镜像的启动脚本内我在启动的时候先删除 pid 文件的,无脑编译关闭即可,不要 hack 修改代码,autoconf 啥的都是大家遵守的规范,主要是 configure.ac 下面的:

1
2
3
4
5
6
7
8
9
10
11
12
dnl -- Linux 3.15
AC_CHECK_DECLS([F_OFD_SETLK], [], [],
[[
#include <unistd.h>
#include <fcntl.h>
]])
for flag in F_OFD_SETLK; do
AS_VAR_COPY([decl_var], [ac_cv_have_decl_$flag])
if test ${decl_var} = yes; then
add_system_opt[${flag}]
fi
done

看下面逻辑,"ac_cv_have_decl_"+"F_OFD_SETLK"="yes" ,在 ./configure 后面加就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
./autogen.sh; \
./configure ac_cv_have_decl_F_OFD_SETLK=no \
--disable-dynamic-linking \
--prefix=/usr \
--exec-prefix=/usr \
--bindir=/usr/bin \
--sbindir=/usr/sbin \
--sysconfdir=/etc \
--enable-nftables \
--enable-regex \
--disable-systemd \
; \

编译打包镜像后测试没问题。

CATALOG
  1. 1. 由来
  2. 2. 排查
    1. 2.1. 基础排查
    2. 2.2. 源码
  3. 3. 编译