1. 为什么KeyarchOS需要单独折腾ipvsadm-1.31-6
先说结论:如果你在KeyarchOS上直接敲yum install ipvsadm,大概率装上的不是1.31-6这个版本,而且很可能是旧版1.28或者1.29。这本身不致命,但如果你做内核升级、做集群节点版本对齐、或者需要特定调度算法的新特性,版本差异就会变成隐性问题。
KeyarchOS作为服务器操作系统,它的软件仓库策略通常偏向“稳定优先”,也就是仓库里的软件版本往往滞后于上游。ipvsadm本身是个很轻量的工具,但它是LVS管理的前端命令行,背后对应的是内核里的IPVS模块。内核在变、IPVS模块在变,ipvsadm的版本就得跟上,否则会出现管理命令和内核模块能力不匹配的情况。最典型的就是新内核里支持了更多的调度算法(比如fo、ovf),旧版ipvsadm根本不知道该算法存在,你敲ipvsadm -A -t 1.2.3.4:80 -s fo它会直接报错。
所以我自己在做KeyarchOS上的负载均衡节点时,直接把ipvsadm从源码编译并打成rpm,锁死在1.31-6。这个过程踩了一些坑,也有一些经验值得记录下来,给接下来要被同样问题困扰的人少走点弯路。
需要说明的是,这篇文章不是简单告诉你“打几个make命令就搞定”,而是从为什么需要适配、环境怎么摸底、编译过程哪里容易翻车、验证怎么做、最后怎么变成rpm交付,完完整整过一遍。无论你是运维、系统工程师还是基础架构组的,只要你需要在KeyarchOS上把LVS管理工具控制在自己手里,这篇文章都适用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手之前的环境摸底:内核、编译器、依赖一个都不能漏
2.1 先搞清楚内核版本和IPVS模块状态
ipvsadm的本质是用户态客户端,它通过setsockopt向内核的IPVS模块下发配置。所以适配工作的第一件事不是解压源码,而是确认内核侧的状态。
先跑几条命令看看:
bash复制uname -r
cat /boot/config-$(uname -r) | grep -i ipvs
lsmod | grep ip_vs
我在一台干净的KeyarchOS测试机上看到的情况是:
- 内核版本:5.10.x(不同版本可能有差异)
- 内核配置里IPVS相关选项:CONFIG_IP_VS=y或=m
- 但lsmod看不到ip_vs相关的模块被加载
这非常正常,IPVS模块按需加载。你只要装了ipvsadm,或者手动modprobe ip_vs,它才会被拉起来。关键是内核编译配置里必须把IP_VS打开,否则后面一切免谈。如果grep ipvs /boot/config-$(uname -r)没有任何输出,说明当前内核没有开启IPVS支持,那适配ipvsadm就成了无根之木。
这里附加一步,确认具体支持了哪些调度算法:
bash复制cat /boot/config-$(uname -r) | grep IP_VS
你会看到类似CONFIG_IP_VS_RR=m、CONFIG_IP_VS_WRR=m这样一堆调度算法模块。确认里面有CONFIG_IP_VS_FO和CONFIG_IP_VS_OVF,这两项在内核4.x时代还属于比较新的算法,如果内核没有编译进去,就算ipvsadm 1.31支持也没用。
2.2 编译工具链和依赖库盘点
ipvsadm-1.31-6的源码编译依赖非常少,主依赖就一个libnl(Netlink库),剩下就是gcc、make这些基础工具。但依赖少不等于没坑,libnl的版本和开发头文件很容易出问题。
先检查基础工具链:
bash复制gcc --version
make --version
pkg-config --modversion libnl-3.0
KeyarchOS默认安装通常自带gcc和make,但系统头文件可能不完整。需要确保kernel-devel和kernel-headers已安装,因为编译过程中需要用到内核头文件:
bash复制yum install -y kernel-devel kernel-headers gcc make libnl3-devel libnl-devel
注意上面我同时装了libnl3-devel和libnl-devel,这是应对麻烦的老办法。实际编译时ipvsadm的Makefile对不同系统会做探测,如果两个版本的库同时存在,它可能会自动选一个。我在KeyarchOS上实测,默认它会选择最新的libnl3。
2.3 版本选择:直接拿上游源码还是用发行版SRPM
ipvsadm-1.31-6这个版本号本身有讲究。1.31是指ipvsadm本身的release版本,-6通常是发行版的打包修订号。从源码编译时,我建议直接去上游官网(Linux Virtual Server项目的官方发布站点)拉ipvsadm-1.31.tar.gz,而不是拿某个发行版的SRPM回来重新编译。
原因有几个:
- SRPM里往往打了发行版自己的一堆补丁,有的补丁是为了适配老内核,有的补丁是为了配合自家systemd脚本,这些补丁未必适合KeyarchOS的环境。
- 上游源码干净,依赖关系清晰,出现问题好排查。
- 1.31-6里的“-6”是发行版维护者打的第6个补丁版本,里面的补丁你未必需要。
所以我的建议是:拿上游1.31源码,如果需要也可以去找对应的发行版补丁列表看看他们改了啥,但别直接拿SRPM回来编。
3. ipvsadm-1.31源码编译实操:从解压到安装的细节
3.1 源码获取与完整性校验
到LVS官方站点或者可信的源码镜像站下载ipvsadm-1.31.tar.gz。下载完之后,建议先做一下校验:
bash复制wget http://www.linuxvirtualserver.org/software/kernel-2.6/ipvsadm-1.31.tar.gz
sha256sum ipvsadm-1.31.tar.gz
校验值和官方公布的SHA256对比一致再解压,这一步虽然显得较真,但生产环境里防止源码被篡改是基本功。
解压:
bash复制tar -xzf ipvsadm-1.31.tar.gz
cd ipvsadm-1.31
3.2 Makefile的改动:别看它简单,改错了直接编不过
解压完之后先别急着make,打开Makefile看一眼:
bash复制vim Makefile
重点看几个变量:
makefile复制PREFIX = /usr/local
SBIN_PATH = $(PREFIX)/sbin
MANDIR = $(PREFIX)/man
DOC_PATH = $(PREFIX)/share/doc
如果你只是在自己机器上测试,PREFIX=/usr/local没问题。但如果你是往一个系统里作为标准工具部署,我强烈建议把PREFIX改到/usr,这样二进制会被安装到/usr/sbin/ipvsadm,跟系统通过rpm安装的路径保持一致。
为什么这样强调?因为很多监控脚本、高可用脚本(比如keepalived的notify脚本)会硬编码调用/usr/sbin/ipvsadm,如果你的ipvsadm装到/usr/local/sbin/,脚本可能找不到它,到时候排查起来会疯掉。
另外一个关键点是Makefile里的LIBDIR和INCLUDE。1.31版本已经支持自动探测libnl的路径,但有些时候它探测不准。如果编译报错找不到<netlink/netlink.h>,你需要手动指定includes路径:
makefile复制INCLUDE = -I/usr/include/libnl3
我在KeyarchOS上实测,如果只装了libnl3-devel,这里不加-I/usr/include/libnl3会直接报错找不到netlink头文件。这个坑很典型,因为libnl3的头文件不是放在/usr/include/,而是放在/usr/include/libnl3/子目录下。
3.3 编译过程中可能遇到的三个常见报错
报错一:Makefile: No rule to make target 'libipvs.a'
这个是因为子模块libipvs/没有先编译好。需要先进入libipvs目录单独编译一次:
bash复制cd libipvs
make
cd ..
make
报错二:undefined reference to `nl_socket_alloc'
这个明显是链接到了老的libnl-1库。ipvsadm 1.31需要libnl3,但系统里可能同时存在libnl-1的兼容库。解决办法是在Makefile里强制指定libnl3的库路径和链接选项:
makefile复制LIBS += -lnl-3 -lnl-genl-3
LIBDIR = -L/usr/lib64/libnl3
报错三:/usr/bin/ld: cannot find -lnl
这个更直接,说明libnl的库文件没装全。检查一下:
bash复制ls /usr/lib64/ | grep libnl
如果没有libnl3.so这种软链接,需要确认libnl3-devel是否安装成功。KeyarchOS上有时缺省不会把开发库装上,尤其是纯最小化安装的情况下。
这三个报错我实际编译时都遇到过,每次都是看似小事但卡住好一会。总结一句话:编译之前把libnl相关的东西装齐,最好libnl、libnl3、libnl3-devel统统装上,能省很多麻烦。
3.4 安装时的文件布局选择
编译通过后,直接:
bash复制make install
如果PREFIX改成了/usr,文件会安装到:
/usr/sbin/ipvsadm/usr/sbin/ipvsadm-save/usr/sbin/ipvsadm-restore/usr/share/man/man8/ipvsadm.8
简单验证一下安装结果:
bash复制which ipvsadm
/usr/sbin/ipvsadm
ipvsadm -v
如果输出ipvsadm v1.31 2021/1/12 (with libnl5?)之类的版本信息,说明基础安装成功了。
这里想额外提一下:ipvsadm -v显示的编译日期和libnl版本信息,不是错误,是正常输出。有些新手看到with libnl5会以为装错了,其实这是告诉你你用什么版本的libnl编译的,仅此而已。
4. 适配过程中最容易被坑的四个隐蔽问题
4.1 内核头文件与运行内核版本不匹配
这是最阴的一个坑。
你编译的时候系统里可能有多个版本的内核头文件。比如/usr/src/kernels/下有两三个目录,对应的内核版本不一样。而Makefile里用了/usr/src/linux之类的软链接,这个软链接指向的可能不是你当前运行的内核版本。
如果头文件版本不匹配,编译能pass,但运行时可能莫名其妙出现ipvsadm: Can't initialize ipvs: Protocol not available或者干脆连接失败。
怎么排查?编译之前先确认:
bash复制ls -l /usr/src/linux
ls /usr/src/kernels/
uname -r
让/usr/src/linux指向你当前内核对应的目录。如果软链接不存在,手动建一个:
bash复制ln -s /usr/src/kernels/$(uname -r) /usr/src/linux
做完这个再重新编译一次,问题基本能规避。
4.2 libnl版本探测逻辑对KeyarchOS不友好
ipvsadm 1.31的Makefile里面有一段自动探测libnl的逻辑,它会执行类似pkg-config --exists libnl-3.0这样的操作。但KeyarchOS的pkg-config路径配置可能会有细微差别,导致探测失败后回退到老的libnl-1。
最终表现就是编译时用了libnl-1的接口,而libnl-1和libnl-3的API是不同的(比如nl_handle_alloc vs nl_socket_alloc)。
我的做法是直接绕开自动探测,手动改Makefile:
makefile复制ifeq (${LIBNL_NAME},)
LIBNL_NAME = libnl-3.0
endif
或者在编译时用环境变量:
bash复制make LIBNL_NAME=libnl-3.0
这样强制指定使用libnl3。这个经验是试出来的,KeyarchOS的默认pkg-config路径跟CentOS不完全一样,自动探测有时候不可靠。
4.3 systemd环境下ipvsadm-save/restore的行为差异
源码头包里自带了一份ipvsadm-save和ipvsadm-restore脚本,它们本质是做了个封装,方便把当前规则dump到文件,或者从文件恢复。但这两个脚本在新老版本上有个小差异:新版本默认输出到stdout,老版本默认强制输出到文件。
如果你写的systemd服务启动时执行ipvsadm-restore < /etc/ipvsadm.rules,要注意路径。1.31版本里restore脚本用的是ipvsadm -R参数,如果你的规则文件本身是ipvsadm-save生成的,那么格式兼容没问题。但如果你手动编辑了规则文件,格式跟save输出不完全一致,ipvsadm -R会卡住或者报错。
我的建议是:只信任ipvsadm-save生成的规则文件。手动改文件的人,往往会在某个字段上少了个空格什么的,然后恢复时各种奇怪问题。
4.4 跟keepalived的交互:别让系统ipvsadm覆盖你编译的版本
在很多使用场景里,ipvsadm不是单独存在的,而是被keepalived以库的方式调用,或者通过shell脚本调用。keepalived如果通过yum install安装,它会自动拉一个旧版ipvsadm作为依赖。
这时你编译的新版ipvsadm会被覆盖或冲突。我的处理方式是:
bash复制rpm -e ipvsadm --nodeps
然后手动安装自己编译好的版本。这样做的前提是你确信编译版本没问题。在生产环境,先在一台测试机上验证过再动刀。
另外,keepalived内部其实是调用libipvs的库函数,而不是直接调用/usr/sbin/ipvsadm二进制,所以它依赖的是libipvs库。如果你改了ipvsadm,但keepalived链接的libipvs还是老的,那么keepalived下发的规则还是走老逻辑。要彻底对齐,连libipvs一起编译安装。
这一步容易被忽略,但非常关键。
5. 功能验证:别只看版本号,把规则跑起来才算数
5.1 最小负载均衡场景搭建
编译安装完ipvsadm,不能光看ipvsadm -v输出就宣布“适配成功”。真正的适配要落到功能层面,验证这个工具在KeyarchOS上能正常创建Virtual Server、添加Real Server、查看连接状态、保存恢复规则。
我起了一个最小验证环境,用一台KeyarchOS作为LB节点:
- Load Balancer: 192.168.1.100
- Real Server 1: 192.168.1.101 (nginx)
- Real Server 2: 192.168.1.102 (nginx)
先加载模块:
bash复制modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
然后确认模块加载成功:
bash复制lsmod | grep ip_vs
创建虚拟服务和真实服务器:
bash复制ipvsadm -A -t 192.168.1.100:80 -s rr -p 600
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.101:80 -g
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.102:80 -g
解释一下参数:
-A:添加虚拟服务器-t:TCP协议,后面接VIP和端口-s rr:轮询调度算法-p 600:持久连接超时600秒,这步对会话保持很重要-a:在已有Virtual Server上添加Real Server-r:Real Server的地址和端口-g:网关模式(Direct Routing),另外两种是-m(NAT模式)和-t(隧道模式)
查看当前规则:
bash复制ipvsadm -L -n
输出像这样:
code复制IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.1.100:80 rr persistent 600
-> 192.168.1.101:80 Route 1 0 0
-> 192.168.1.102:80 Route 1 0 0
看到规则的Forward列是Route,说明DR模式配置成功。
5.2 实际流量验证:循环和包转发
配置完规则之后,真正的考验是数据通路。我在两台Real Server上配置了lo接口绑定VIP地址(DR模式的标配操作),然后从客户端发起请求。
记数统计验证:
bash复制ipvsadm -L -n --stats
这个命令会输出每个Real Server的累计连接数、包数、字节数。持续打流量之后,观察两台Real Server的TotalConn是否交替增长。如果只有一台在涨,说明调度算法没生效或者有一台Real Server的配置有问题。
连接追踪验证:
bash复制ipvsadm -L -n --timeout
这个查看的是TCP、TCP FIN、UDP各自的超时时间。如果做过调优,这里能看到自定义值。
5.3 保存恢复验证:适配最后一道关口
适配工作最重要的一个环节就是规则保存和恢复。实际生产里LB节点重启后需要自动拉起规则,如果save/restore环节出问题,重启就等于事故。
bash复制ipvsadm-save > /etc/sysconfig/ipvsadm
然后清空规则:
bash复制ipvsadm -C
确认规则清空:
bash复制ipvsadm -L -n
恢复:
bash复制ipvsadm-restore < /etc/sysconfig/ipvsadm
再查看:
bash复制ipvsadm -L -n
如果规则完整恢复,说明save/restore链路正常。
这里我特别建议验证一下带--persistent连接相关的规则保存恢复。因为持久连接规则里可能带有持久掩码(-M参数),某些版本在save/restore时对掩码的处理有bug,1.31版本相对成熟,但验证一下总没坏处。
6. 打包成rpm:把适配成果固化下来
6.1 为什么需要打包而不是散装源码
在很多团队里,编译安装一个软件不是终点。你要考虑版本统一、分发、回滚、移除。散装源码安装的问题在于:
- 别人不知道你装了什么版本、装到了哪里
- 升级时不知道要覆盖哪些文件
- 卸载时不知道要删哪些文件
- 团队其他机器没法复制同样的环境
所以我把适配好的ipvsadm打成了rpm,放到内部yum仓库里,其他机器一条yum install ipvsadm-1.31-6.keyarchos就搞定。
6.2 编写spec文件的几个关键点
打rpm需要在KeyarchOS上准备好rpmbuild环境:
bash复制yum install -y rpm-build rpmdevtools
rpmdev-setuptree
然后把源码放到~/rpmbuild/SOURCES/,写一份spec文件。
spec文件核心部分长这样:
spec复制Name: ipvsadm
Version: 1.31
Release: 6%{?dist}
Summary: Utility to administer the Linux kernel virtual server
License: GPLv2+
Source0: %{name}-%{version}.tar.gz
BuildRequires: gcc make libnl3-devel kernel-devel
Requires: libnl3
%description
ipvsadm is used to set up, maintain or inspect the virtual server
table in the Linux kernel.
%prep
%setup -q
%build
# 强制指定libnl3
make LIBNL_NAME=libnl-3.0
%install
make install PREFIX=/usr DESTDIR=%{buildroot}
%files
/usr/sbin/ipvsadm
/usr/sbin/ipvsadm-restore
/usr/sbin/ipvsadm-save
/usr/share/man/man8/ipvsadm.8.gz
%post
# 加载IPVS内核模块
modprobe ip_vs 2>/dev/null || true
这里有几个点要说明:
BuildRequires里必须写kernel-devel,否则编译时找不到内核头文件make install时要带DESTDIR=%{buildroot},这是rpm打包的金科玉律,不带的话文件会装到宿主机而不是打包根目录%post脚本里顺手modprobe ip_vs,保证安装完模块能加载。加|| true是为了防止没有权限的场景下安装失败
6.3 打包过程中的一个典型报错:路径被压缩
rpmbuild的时候偶尔会遇到File must begin with "/"或者文件丢失的错误。这通常是%install阶段没有正确使用DESTDIR导致的。另一个典型问题是man page被gzip成.gz后,%files里写的路径不一致。
解决办法是%files里两种路径都处理,或者在make install后手动检查:
bash复制find %{buildroot} -type f
看每个文件实际装到了什么位置,再对着写%files。
我实际打包时还遇到一个情况:make install会把文档也装进去,%files如果不写%doc相关路径,rpmbuild会报错说“未打包的文件”。解决办法是照抄:
spec复制%doc README
%doc ChangeLog
或者干脆在%install里删掉多余的文档:
bash复制rm -rf %{buildroot}/usr/share/doc
6.4 与系统自带版本的共存与替换策略
打包完成后,安装时有个选择:替换系统自带版本,还是共存。
如果你需要兼容,可以考虑对rpm包名做后缀区分,比如把包名改成ipvsadm-keyarchos,但这样会导致keepalived的依赖检查过不去,因为你没有提供ipvsadm这个包名。
更平滑的做法是直接替换系统自带版本。先卸载旧版:
bash复制rpm -e ipvsadm --nodeps
再安装新版:
bash复制rpm -ivh ipvsadm-1.31-6.el8.x86_64.rpm
验证:
bash复制rpm -q ipvsadm
ipvsadm -v
这里再提醒一句,如果keepalived是编译安装且静态链接了libipvs,那你替换系统的ipvsadm rpm并不影响keepalived的实际行为。这时候要想清楚你到底在替换什么,是替换命令行工具还是替换整个LVS管理链路。如果是后者,需要连keepalived一起重新编译。
7. 生产环境落地后的补充建议
适配和打包只是开始,真正要稳,还得把几个收尾的事情做掉。
第一,把IPVS模块开机自动加载。写一个/etc/modules-load.d/ipvs.conf,内容写上需要用到的模块名:
code复制ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
ip_vs_fo
ip_vs_ovf
这样系统启动时内核会自动加载这些模块,避免服务启动时才发现模块没加载。
第二,把规则恢复做成开机自动执行。最简单的方式是编写systemd service:
ini复制[Unit]
Description=Restore IPVS rules
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/ipvsadm-restore < /etc/sysconfig/ipvsadm
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
启用:
bash复制systemctl enable ipvsadm-restore.service
第三,配置监控。LVS本身不主动上报状态,你需要用ipvsadm -L -n --stats定时采集数据。可以写一个简单的shell脚本收集连接数变化,推给监控系统。核心代码大概长这样:
bash复制#!/bin/bash
ipvsadm -L -n --stats | awk '/^TCP/{print $2, $4, $5, $6}'
我这里就不展开了,具体怎么推要看你们监控体系的支持方式。
第四,内核参数调优。这个跟ipvsadm本身关系不大,但对LVS性能影响很大。重点看/etc/sysctl.conf里的这几个参数:
code复制net.ipv4.ip_forward = 1
net.ipv4.vs.expire_nodest_conn = 0
net.ipv4.vs.expire_quiescent_template = 0
net.ipv4.tcp_tw_reuse = 1
expire_quiescent_template这个参数很多人不知道,它控制在Real Server被标记为down之后,持久连接模板是否还保留。默认值为0,也就是保留。如果设置为1,连接模板会过期清理,可能导致转发不稳定。
适配ipvsadm-1.31-6这件事,本身工作量不大,但涉及的关联因素不少——内核模块、libnl版本、keepalived协同、rpm打包、开机恢复,每个环节都可能成为坑。希望这篇文章把我在KeyarchOS上趟过的路说清楚了,你有类似需求时可以少走点弯路。
