ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

生产级K8s高可用流量入口:haproxy+pacemaker负载均衡集群详解

生产级K8s高可用流量入口:haproxy+pacemaker负载均衡集群详解

把Kubernetes高可用集群里的“流量入口”这件事彻底讲透。之前不少朋友在搭生产级K8s时,习惯先把三个master节点装好,再在每台master前面挂一个Nginx或者用云厂商的SLB,结果要么单点还在,要么云上LB的会话保持和健康检查调得头疼。今天要说的这套方案,是我在多套生产环境里实际用过的组合:haproxy做四层负载均衡,pacemaker做高可用仲裁,两者合起来就是一套不依赖云厂商、纯自建的k8s高可用负载均衡集群。它解决的核心问题很明确:让多个kube-apiserver实例有一个统一、稳定、自动故障转移的接入入口,同时也适合那些机房自建、没有现成云LB的场景。

这套方案适合谁看?如果你正在规划生产级K8s集群,或者你已经有了多master节点但入口还挂在单台LB上,又或者你在对比keepalived和pacemaker的差异,那这篇文章值得认真读完。我不会只贴命令,还会把为什么这么配置、踩过的坑、验证方法都讲清楚,照着搭即可直接用。

1. 整体设计与需求拆解

1.1 高可用负载均衡集群在K8s架构中的位置

Kubernetes控制平面由多个组件组成,其中kube-apiserver是所有请求的必经入口。无论是kubectl、kubelet、controller-manager、scheduler,还是集群内组件,最终都要通过apiserver完成资源读写。生产环境为了保证控制平面不挂,通常会部署三个甚至更多master节点,每个节点上跑一个apiserver。

但这里有个问题:你总不能把三个apiserver地址都填到kubectl配置里,也没法让每个kubelet分别连不同地址。需要一个前置的统一入口,把这多个apiserver合并成一个虚拟地址对外提供服务。这个入口就是负载均衡层。K8s官方架构图里也明确画出了这一层,通常叫Load Balancer,但官方没有规定你必须用什么实现,所以给了我们自建的发挥空间。

这套方案中的haproxy承担的就是这样的角色:它监听一个虚拟IP(VIP),把来自客户端(kubectl、kubelet、controller-manager等)的6443端口请求,按照轮询算法转发到后端三个apiserver节点。而pacemaker负责两件事:一是保证这个VIP永远只在一台节点上生效,节点挂了自动漂移到另一台;二是保证haproxy进程宕了也能自动拉起,或者把VIP一并带走,让后端的故障对客户端完全透明。

1.2 为什么不直接用Nginx或Keepalived

很多人第一反应是“负载均衡用Nginx不就行了吗”,或者“高可用用Keepalived不挺简单吗”。这两种方案在小型环境确实能用,但放在K8s控平接入层这个具体场景里,各有各的别扭。

Nginx在做TCP四层转发时,upstream配置本身需要手动维护,对后端节点的健康检查能力和动态调整能力不如haproxy灵活。更重要的是,Nginx本身不解决“谁持有VIP、故障了怎么切换”的问题,你得再叠一层Keepalived,这就等于要维护两套配置体系。而haproxy自带的就是专门做四层负载均衡的工具,配置语义清晰,统计页面强大,排障起来非常直观。

再说Keepalived,它实现HA的核心是VRRP协议,本质上是“主备抢占”模型。而pacemaker是基于集群资源管理的成熟方案,支持的不只是IP漂移,还有服务、文件系统、虚拟IP等多种资源的统一编排。在K8s场景里,我们需要的是“VIP + haproxy服务”作为一个资源组统一管理、按顺序启动、按约束调度,这正是pacemaker的强项。换句话说,Keepalived把VIP切过去了,haproxy是否启动、启动顺序、谁先谁后这些都得靠脚本自己折腾;pacemaker则用一个resource group就把这些事标准化了。

1.3 方案适用场景与边界

这套haproxy+pacemaker组合最适合的场景是:物理机或自建机房的K8s集群,网络环境可控,且没有现成的云负载均衡产品可用。比如内网IDC环境、政务云裸金属区、混合云自建区域,这些东西经常没有云LB或者云LB要收费且限制多,用本文方案最踏实。

不适用的场景也有:如果你的环境就是公有云且云LB已经做得很好(比如健康检查、多可用区容灾都成熟),那直接用云LB更省事;如果只有两个master节点且业务量不大,可以用更轻量的keepalived;如果集群规模极大、后端节点几十个,可能要考虑专门的四层负载均衡硬件或者云LB,毕竟haproxy单机性能总有上限。

提示:本文所有配置基于haproxy 2.x版本和pacemaker 2.0/PCS版本,CentOS 7/8或Rocky Linux 8/9均适用。不同发行版命令略有差异,我会在对应位置标注。

2. 关键原理拆解:haproxy与pacemaker各自干了什么

2.1 haproxy的四层负载均衡是如何工作的

haproxy支持两种核心代理模式:七层HTTP模式和四层TCP模式。K8s的apiserver走HTTPS协议,我们这里不需要做HTTP头解析、URL路由这些,只需要把TCP流量原封不动地从客户端搬到apiserver,所以选用四层模式。在haproxy里,这个能力叫TCP proxy,配置段写在一组listen或backend里,mode那行填tcp。

四层转发意味着haproxy根本不关心HTTP请求内容,它只维护一条TCP连接和数据流的双向搬运。这对性能极其友好,因为不需要读写和解析应用层协议,CPU开销小、延迟低。从内核角度看,haproxy做的是用户态代理,但配合epoll事件驱动模型,在普通物理机上处理几万并发毫无压力,对于K8s客户端数量来说绰绰有余。

haproxy的核心优势之一是内置健康检查。对于TCP后端,可以配置tcp-check,定期向6443端口发起TCP连接尝试,甚至可以通过配置发送一段数据、期望一段响应来确认apiserver真的活着。这样比单纯探测端口活性更可靠——端口通不代表进程健康,但apiserver的HTTPS握手能完成,基本能确认它处于可服务状态。

2.2 pacemaker的集群资源管理与故障转移逻辑

pacemaker是一个分布式资源管理器,它运行在多个节点上组成一个集群,通过选举出一个Designated Coordinator(DC)来决策资源应该运行在哪个节点。它管理的最小单位叫“资源”(resource),每类资源有对应的Resource Agent,也就是一个脚本或程序,用来执行start、stop、monitor等操作。

在我们的场景里,需要管理两类资源:一个是IPaddr2类型的虚拟IP资源,负责在节点上配置/解除VIP;另一个是systemd类型的haproxy服务资源,负责启动、停止、监控haproxy服务。pacemaker把这两个资源放在同一个资源组里,确保它们要么在同一台节点上一同运行,要么一同停止,不存在VIP在A节点、服务在B节点的错位情况。

故障转移的逻辑是这样的:pacemaker通过corosync进行节点间心跳通信,默认情况下所有节点互相通信并同步集群状态。当某个节点的haproxy进程挂了,pacemarker的本机资源监控会立刻检测到monitor失败,先把整个资源组在当前节点停止,然后根据资源约束和节点得分,把资源组调度到另一台健康节点上启动。同样的,如果整台节点宕机断电,corosync会在指定超时(通常几秒)后判定节点失联,其他节点把资源组接管过来。

这里有个关键概念叫fencing(隔离)。虚拟IP这种资源有个特性:如果原来持有VIP的节点只是网络分区但进程还在,而另一台节点又把这个VIP绑定到自己的网卡上,就可能出现IP冲突、两边同时持有VIP,后果就是流量进入两个后端,状态错乱。所以生产环境必须配置stonith设备,通常是带外管理控制器(比如服务器的BMC/IPMI)或者专门的PDU电源管理,在故障节点被剔除前先把它强制断电或重启,保证同一时刻只有一个节点能持有VIP。这也是pacemaker设计上比keepalived更严谨的地方——它不允许“脑裂”这种情况存在。

2.3 为什么VIP漂移能对K8s客户端透明

对K8s组件来说,它们访问的地址就是haproxy上的VIP加6443端口。VIP本质是一个额外的IP地址,绑定在某块网卡上。正常情况下A节点持有VIP,所有流量都进A节点的haproxy。当A节点故障,pacemaker做的动作是:在A节点上删除VIP(ifconfig/ip addr del),在B节点上添加VIP(ip addr add),然后B节点的haproxy立刻开始对外服务。

从客户端视角看,它访问的IP没变、端口没变,只是背后的服务器变了,所以没有任何感知。K8s各组件的连接池可能会有短时间的连接重置,但apiserver客户端库通常都有重试机制,下一次请求就恢复正常了。这也是为什么给K8s用的LB必须做长连接友好和重试容忍,haproxy的纯TCP代理天然满足这一点。

3. 实操落地全过程:从零搭建haproxy+pacemaker集群

3.1 架构规划与节点准备

以三台LB节点为例,设置如下。为了措辞方便,节点代号我直接用lb01、lb02、lb03代替,域名部分使用内部测试域,实际操作时替换为你的环境变量。

节点IP地址角色
lb01192.168.100.21haproxy+pacemaker
lb02192.168.100.22haproxy+pacemaker
lb03192.168.100.23haproxy+pacemaker
VIP192.168.100.20集群对外服务IP

后端K8s master节点(假设三台):

节点对应apiserver地址
master01192.168.100.11:6443
master02192.168.100.12:6443
master03192.168.100.13:6443

此外,每台节点必须满足几个前置条件:

  • 操作系统推荐Rocky Linux 8.6以上或CentOS 7.9均可,内核版本无特殊要求。
  • 节点之间能通过内网IP互通,且开放corosync所需的组播/单播端口(默认5404、5405,单播模式用5405即可)。
  • 时间必须同步。重要程度不高,你可以在pacemaker集群里配置timeout甩锅,但更稳妥的做法是每台节点装chrony,保证时间偏差在100毫秒内。
  • 如果使用防火墙,需要放行以下端口:6443(对客户端开放,通过VIP)、5410(haproxy统计页面端口)、5405(corosync集群通信)、2224/3121(pcsd集群管理端口)。
  • 每台节点的主机名要唯一,且能互相解析。建议在/etc/hosts里直接配好。

3.2 haproxy安装与配置详解

在三台节点上分别安装haproxy:

# Rocky Linux 8/9 dnf install -y haproxy # CentOS 7 yum install -y haproxy

haproxy的主配置文件在/etc/haproxy/haproxy.cfg。默认配置有global和defaults段,我们需要在后面自定义前端和后端,但在K8s场景里直接用listen块最简单,把监听、转发、健康检查都写在同一个块里。下面是一份可以照抄的配置,注意根据实际情况修改IP。

vim /etc/haproxy/haproxy.cfg
global log 127.0.0.1 local2 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode tcp log global option tcplog option dontlognull option redispatch retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout check 10s maxconn 3000 # 前端入口:监听VIP的6443端口 frontend k8s-api bind *:6443 default_backend k8s-apiserver # 后端:三个apiserver节点 backend k8s-apiserver balance roundrobin option tcp-check tcp-check connect tcp-check send-proxy server master01 192.168.100.11:6443 check inter 3s fall 3 rise 2 weight 1 server master02 192.168.100.12:6443 check inter 3s fall 3 rise 2 weight 1 server master03 192.168.100.13:6443 check inter 3s fall 3 rise 2 weight 1 # 统计页面便于排障 listen stats bind *:5410 mode http stats enable stats uri /stats stats refresh 10s stats auth admin:admin123

这里有几个细节要说明。

第一,balance roundrobin是轮询算法,对apiserver这种无状态应用很合适,能均匀分配请求。老版本haproxy里roundrobin在服务器动态增删时会暂时不平滑,但2.x版本已经修复,可以放心。

第二,tcp-check send-proxy这句话要特别注意。它表示haproxy会向后端发送一个代理协议头。Kubernetes的apiserver默认支持代理协议吗?其实是支持的,如果apiserver启动参数里配置了对应处理。但如果你的apiserver没有启用--proxy-protocol设置,这里会导致后端拒绝连接。

注意:如果apiserver没有启用PROXY protocol,请不要写tcp-check send-proxy这一行。多数场景下我们其实不需要它,直接把这一行删掉,仅保留tcp-check connect就够了。我在生产里习惯只做TCP拨号检查,简单可靠。

第三,check inter 3s fall 3 rise 2表示每3秒探测一次,连续3次失败判定为down,连续2次成功判定为up。对K8s控平这种组件,这个频率够用,不会给master节点带来额外压力。

配置写好后,启动haproxy并设置开机自启:

systemctl enable --now haproxy systemctl status haproxy curl http://127.0.0.1:5410/stats

用浏览器或curl访问统计页面,能看到后端三个server的健康状态。此时还没配VIP,haproxy监听的是节点的所有网卡地址,测试可以直接用节点IP加6443访问,确认转发链路是好的。

3.3 corosync与pacemaker集群初始化

先安装集群相关软件包。三台节点都要执行:

# Rocky Linux 8/9 dnf install -y pacemaker pcs fence-agents-all # CentOS 7 yum install -y pacemaker pcs fence-agents-all

设置pcs管理用户的口令(默认用户是hacluster):

passwd hacluster

启动pcsd服务并设置开机自启:

systemctl enable --now pcsd

接下来在任意一台节点上(这里选lb01)创建集群并加入另外两台。注意三台节点的主机名要能互相解析,前面提过的hosts配置必须完成。

pcs host auth lb01 lb02 lb03 -u hacluster -p '你的口令' pcs cluster setup k8s-lb-cluster lb01 lb02 lb03 --transport udp --force

这条命令会生成corosync的配置文件(/etc/corosync/corosync.conf),默认使用UDP组播方式通信。如果你的网络环境禁用了组播(很多云VPC禁组播),就需要改成单播模式,在setup参数里加上:

pcs cluster setup k8s-lb-cluster lb01 lb02 lb03 --transport udp --force --token 5000

单播模式下corosync会从配置中读取interface段里的member IP列表进行点对点通信,同样能工作,只是多了一条静态对端配置。

启动集群并设置开机自启:

pcs cluster start --all pcs cluster enable --all

检查集群状态:

pcs status

正常情况下应该看到三台节点都在线,集群没有配置任何资源,也没有stonith设备报错。此时如果pcs status里出现资源相关的红色警告没关系,因为还没有资源。

3.4 配置资源组:VIP + haproxy服务

现在核心操作来了。我们需要定义两个资源,然后把它们放进同一个组里,让pacemaker统一管理。

先创建虚拟IP资源:

pcs resource create vip_k8s ocf:heartbeat:IPaddr2 ip=192.168.100.20 cidr_netmask=24 nic=ens192 op monitor interval=10s timeout=20s

参数说明:ocf:heartbeat:IPaddr2是pacemaker自带的IP地址资源脚本,ip就是我们的VIP,cidr_netmask按实际子网填写,nic换成实际网卡名(用ip addr查看),op monitor interval=10s表示每10秒检查一次IP是否还在。如果你的网络环境需要指定广播地址,可以加broadcast=192.168.100.255。

再创建haproxy服务资源:

pcs resource create haproxy_svc systemd:haproxy op monitor interval=10s timeout=20s on-fail=restart

systemd:haproxy表示pacemaker通过systemd管理haproxy服务,on-fail=restart是重要参数:如果haproxy进程异常退出,pacemaker会在当前节点直接重启该服务,而不是立刻把资源组漂移到另一台节点。这个策略适合进程级故障——先尝试快速恢复,如果反复失败,再考虑切节点。生产实际中,haproxy进程本身很稳定,偶发崩溃时原地拉起比重启VIP更快更平滑。

把两个资源放进同一个资源组:

pcs resource group add k8s-ha-group vip_k8s haproxy_svc

资源组的意义在于:VIP和haproxy永远在同一节点上,启动顺序是按组内顺序先vip后haproxy,停止顺序相反。这样不会出现haproxy起来了但VIP还没绑定的空窗期,也不会有VIP绑定了但haproxy没起的摆设状态。

查看资源运行情况:

pcs status

正常会输出类似:

Resource Group: k8s-ha-group vip_k8s (ocf::heartbeat:IPaddr2): Started lb01 haproxy_svc (systemd:haproxy): Started lb01

此时在客户端测试:用任意节点上的kubectl把server指向https://192.168.100.20:6443,应该能正常访问apiserver,并发现请求被轮询到不同的master后端。

3.5 配置stonith隔离设备

生产环境强制要求配置stonith。没有fence设备,pacemaker会拒绝在故障节点上启动某些资源或者给出严重警告,更重要的是无法安全处理节点失联。最常用的fence设备是基于IPMI的fence_ipmilan。

我的建议是先用fence_ipmilan,前提是你的服务器BMC支持IPMI 2.0。每台节点分别定义一个对应的fence设备:

pcs stonith create fence_lb01 fence_ipmilan pcmk_host_list=lb01 ipaddr=192.168.100.201 login=root passwd=你的BMC密码 lanplus=1 pcmk_reboot_action=off

这里ipaddr是lb01的BMC管理口IP(通常跟业务网段隔离),lanplus=1表示用IPMI 2.0加密通道。pcmk_reboot_action=off是个人偏好:pacemaker隔离时直接断电(off),比reboot更彻底,能避免一些键鼠或固件层面的意外。如果你的设备只支持reboot,也可以改成reboot,但相比off,reboot后节点有概率再次加入集群,打架风险略高。

配好后检查:

pcs stonith show pcs status

如果暂时没有真实BMC设备,在测试环境可以用fence_pcmk配合pcmk_host_list做一个空操作模拟,但这是临时方案,线上绝对不能这么干。

3.6 资源约束:让VIP按预期漂移

刚才三个节点的资源组如果全部健康,它会自动选择一个节点运行。默认策略是偏向于得分高的节点,在没有额外约束时,首个成功启动资源的节点得分较高,通常就是lb01。但我们往往希望控制它的倾向,比如让优先级最高的节点先持有VIP。

用pcs constraint location命令可以设置资源优先在某节点运行。比如希望资源组尽量在lb01和lb02之间切换,避免跑到lb03:

pcs constraint location k8s-ha-group prefers lb01=100 pcs constraint location k8s-ha-group prefers lb02=50

数字越大优先级越高。当lb01在线时资源组一定在lb01,lb01故障退避到lb02,lb02也挂了才轮到lb03。这种约束对流量路径稳定很有帮助,减少VIP乱跳带来的网络设备MAC缓存刷新。

另一个必要约束是“资源不要同时跑在哪些节点”这种反亲和性,但在我们的场景里只有一个VIP一份haproxy资源,不存在同时运行的问题,所以不需要配置。如果以后要扩展成多个LB组同时服务不同端口,再考虑pcs constraint colocation之类的配置。

3.7 验证集群故障转移

配完所有东西,必须做故障注入验证,不然等于白配。

第一步,验证VIP访问:

while true; do curl -k https://192.168.100.20:6443/healthz; sleep 1; done

正常会一直返回ok。如果有负载均衡轮询的痕迹,能看到请求被打到不同apiserver,但响应一致。

第二步,手动停掉当前持有VIP节点的haproxy:

systemctl stop haproxy # 在lb01上执行 pcs status

因为配置了on-fail=restart,你会发现pacemaker立刻把haproxy拉起来了,VIP还在lb01上,几乎无感知。

第三步,模拟整机故障:直接拔掉lb01的网线,或者systemctl stop corosync.service,模拟节点失联。此时pacemaker会发现lb01心跳丢失,触发stonith,对lb01断电,同时资源组在lb02上启动。vip_k8s和haproxy_svc都会变成Started lb02。

这一步最能检验配置是否合格。执行后观察pcs status输出,重点看Fencing是否被记录:

Failed Actions: * lb01_reboot_0000: Master/Slave fencing...

以及资源是否成功迁移到lb02。整个切换过程通常10秒内完成,客户端如果有少量重试完全无所谓。

4. 常见问题与排查技巧实录

4.1 资源组一直报错:haproxy_svc启动失败

这个问题出现的概率极高。典型报错是Failed to start haproxy_svc,原因往往是haproxy配置语法错误,导致systemd起不来。排查思路:

先手动执行一次启动,看具体错误:

systemctl start haproxy systemctl status haproxy journalctl -u haproxy -n 50

确认配置无误后,在pacemaker里确认资源定义是否有问题:

pcs resource config haproxy_svc pcs resource debug-start haproxy_svc

pcs resource debug-start可以在命令行直接启动资源代理脚本并打印输出,这是排查资源脚本问题的利器,比看pcs status的闪烁提示高效得多。

一个隐蔽的坑是:haproxy在systemd环境下启动时,默认会读取/etc/sysconfig/haproxy里的额外参数。如果这个文件里残留了旧的启动参数,比如OPTIONS="-f /etc/haproxy/haproxy.cfg",可能跟默认配置冲突。出现异常时把该文件里多余配置删掉,再重启服务。

4.2 后端apiserver健康检查一直DOWN

haproxy统计页面上后端server状态不稳定,一会儿UP一会儿DOWN,或者始终DOWN。这种问题要从三个层面排查。

第一,端口通不通:

telnet 192.168.100.11 6443

第二,检查方法是不是太苛刻。前面提过tcp-check send-proxy的坑,如果误加了代理协议头,apiserver又没有开启代理协议支持,后端就会拒绝连接,健康检查必然失败。把发代理协议那行去掉,只保留tcp-check connect,问题立即消失。

第三,注意apiserver的证书时间。如果TLS证书过期或时间不准,即使TCP能连上,TLS握手也会失败,haproxy的tcp-check如果配置成了send-proxy后接expect字符串,就更容易暴露这个问题。可以通过openssl命令验证:

openssl s_client -connect 192.168.100.11:6443 -brief

如果输出CONNECTION ESTABLISHED,说明TLS握手正常,证书时间没问题。

4.3 VIP漂移后客户端连不上了

切换完成后集群状态正常,VIP已经漂移到新节点,但客户端访问超时。这种情况大概率不是pacemaker问题,而是网络层面的问题:交换机上原端口的ARP表还残留旧MAC,客户端缓存的ARP条目没有刷新。

解决思路有两种。第一种是让客户端主动刷新ARP,但这不现实,不能要求所有客户端都手动清。第二种是pacemaker的IPaddr2资源默认会发送免费ARP(gratuitous ARP)通告,新节点绑定VIP后立刻广播“这个IP对应的MAC是我”,交换机收到后刷新端口映射。如果这种现象频繁出现,需要检查网卡配置是否启用了arp_ignore、arp_announce等内核参数,以及交换机端口是否开启了portfast之类的特性,延迟了MAC学习。

另外,如果VIP绑定在bond网卡上,IPaddr2的nic参数务必写上bond设备名,不要写物理网卡。否则MAC地址不对,免费ARP发出去也无效。

4.4 stonith设备配置后仍提示未被fence

这是我没少遇到过的问题。pcs status显示节点处于unclean状态,但stonith设备没真正生效。敲pcs stonith show看设备状态正常,但触发隔离失败。

排查顺序:

  • 检查BMC IP是否可达:从存活节点上ping 192.168.100.201,不通就说明管理网段问题。
  • 检查IPMI凭据:用命令在存活节点上手动执行一次fence测试,比如验证lb01的BMC是否正确响应。更安全的方式是ipmitool -I lanplus -H 192.168.100.201 -U root -P 密码 chassis status,看能否返回电源状态。
  • 确认pcmk_host_list里的主机名跟pacemaker节点名完全一致,区分大小写。不一致时fence命令会匹配不到目标,直接跳过。
  • 如果BMC IP和业务IP在同一网段,注意BMC网卡是否和业务网卡冲突,有时IPMI响应慢,导致fence超时,可以适当调大pcmk_reboot_timeout。

4.5 常见问题速查表

症状原因解决办法
pcs resource debug-start没有输出且服务状态不对resource agent脚本路径不对检查ocf:heartbeat:IPaddr2里的heartbeat路径是否存在,是否缺少ocf包
corosync通信超时,节点反复加入/离开防火墙未放行5405/UDP放行corosync端口,或者改用单播模式
VIP ping不通IPaddr2资源没有绑定,或绑了但网卡名写错检查pcs status中vip_k8s是否Started,以及ip addr看VIP是否生效
haproxy日志刷屏连接超时后端地址写错、防火墙拦截逐条telnet检查后端6443
pacemaker提示资源冲突、资源找不到资源ID重复或资源未定义到组pcs resource config核对ID,删除多余资源后重新定义

5. 扩展思考与进阶优化

5.1 是否可以把haproxy换成其他LB组件

从架构上看,haproxy这层可以被任何四层LB替换,但换成别的方案要考虑跟pacemaker的兼容性。

比如换成nginx四层代理,资源代理从systemd:haproxy改成systemd:nginx即可,其他逻辑完全一致。比较少见但可行。换成envoy的话,配置复杂度会明显上升,没必要为这个场景引入重武器。而如果用云厂商的SLB,那就直接放弃pacemaker了,因为SLB本身完成了高可用,VIP由云平台接管,不再需要自管IP资源。

我的观点是:在自建环境里,haproxy+pacemaker是控制平面入口的性价比最优解。它足够简单、足够可靠、社区资料也足够多。

5.2 与Keepalived的对比再深入一层

很多人纠结keepalived还是pacemaker。说一个个人观察:keepalived适合“我只需要一个VIP来回飘”的轻量场景;pacemaker适合“我把VIP和服务绑在一起做调度、有依赖关系、需要复杂约束”的场景。K8s接入层表面上看只需要VIP飘,但实际上VIP必须伴随着haproxy服务,haproxy挂了不能让VIP留在原节点变成黑洞。这个“把服务和IP放在一起”的需求,用pacemaker资源组表达最清晰。

在可观测性上,pacemaker的pcs status、pcs constraint、历史告警日志都更丰富,出了问题容易复盘。而keepalived的状态要么看进程、要么看日志,很多情况下得靠抓包确认VRRP状态,排障成本高一些。

5.3 后续还能往哪些方向扩展

这套LB集群投入使用后,有几个方向可以继续优化。

第一,把haproxy的统计页面接入监控系统(比如Prometheus通过haproxy_exporter采集),这样LB层本身的状态就能做到可视化。

第二,如果apiserver的流量有明显的读写不对称,比如写操作多、读缓存命中率低,可以考虑给haproxy加option tcp-smart-accept和option tcp-smart-connect这类内核优化参数,提升连接处理效率。

第三,pacemaker本身就是通用集群管理框架,这套节点以后如果想复用,比如再加一套MySQL高可用虚拟IP,也可以往同一个集群里加新的资源组,不用另起炉灶。

写在最后的一点心得

这套方案我在生产环境用了快两年,印象最深的一件事是第一次做故障演练:直接断电一台LB,客户端只出现了一次TCP连接重置,下一个请求就自动恢复了,kubectl命令全程没感觉到后端切换。那一刻你会觉得,之前花时间把haproxy配置、pacemaker约束、fence设备挨个打磨到位是值得的。

我个人实操中最想提醒你的是三个点。第一,haproxy的配置一定要用haproxy -c提前做语法检查,别等到pacemaker资源报错了才回头改。第二,fence设备必须真实验证过一次,我自己就见过配置了BMC但密码错误导致fence失败的事故,这种一旦遇到节点故障,资源就卡在unclean状态,整个集群都受影响。第三,定期做切换演练,不要上线了就不管,把“拔网线测试”变成季度例行操作,你会发现很多隐藏的网络问题是靠演练才暴露出来的。

另外,在生产环境里千万不要为了省事跳过stonith。pacemaker在未配置stonith时根本不允许资源正常启动,这个设计是有道理的。VIP资源最大的风险就是脑裂,fence是唯一能安全止血的手段。宁可多花点时间把BMC调通,也别拿集群的可用性冒险。

如果你打算在云环境里复刻这套方案,记得把组播改成单播,同时确认云厂商的安全组是否允许IPMI类带外连接——大多数公有云裸金属并不开放这个能力,这时候可以评估是不是用云厂商的LB更实际。如果只是在公司内部IDC环境,这套方案可以放心落地。

有任何配置上的问题,欢迎照着文章里的命令逐条核对。多踩几个坑,自然就熟了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进