尧图精选

Keepalived+HAProxy高可用负载均衡:原理、配置与故障转移实战

🕒 发布时间:2026/10/2 18:44:58 📁 来源:尧图网络
凌晨两点半值班手机震了。“你们的服务是不是挂了”我揉着眼睛登录跳板机看了一眼后端某台机器其实已经宕机快两小时了入口请求却一直正常用户完全无感。那一瞬间我就知道之前搭的那套高可用负载均衡起作用了。这套东西在我的运维笔记里编号是2.24标题就叫“高可用负载均衡”。简单说就是让流量入口不依赖任何一台单机即使某一层崩了请求还能自动绕到健康节点上继续跑。这篇文章不聊云厂商一键购买的那种托管LB而是从原理到底层配置完整走一遍Keepalived HAProxy搭建主备高可用负载均衡的全过程。内容主要面向刚接手后端服务运维、或者想搞明白“负载均衡到底怎么保证自己高可用”的读者我会把选型逻辑、核心机制、配置文件和踩坑排错一起讲透。1. 为什么说“负载均衡本身才是最危险的单点”1.1 没有负载均衡时故障是怎么扩散的很多人第一次接触负载均衡是从“多台后端机器需要统一入口”这个需求开始的。传统架构里域名DNS直接解析到某一台应用服务器的IP用户请求全部打到这台机器上。问题很明显这台机器挂了整个服务就废了。更麻烦的是DNS缓存。哪怕你立刻把DNS解析改到另一台机器由于各地运营商、浏览器、系统解析器都有缓存TTL没到之前大量用户依然会访问那个已经宕机的IP。我见过不少小团队因为这个原因故障恢复时间干到一两个小时以上不是服务起不来而是“老地址”的用户进不来。所以在后端前面加一层负载均衡是第一步让后端多台机器藏在一个虚拟入口后面由负载均衡器负责转发流量。后端任何一台挂了它会被自动摘掉请求分给其他健康节点。这一步解决的是“后端多机冗余”的问题。1.2 负载均衡器自己挂了怎么办问题来了——流量入口从“后端多台机器”收敛到了“一台负载均衡器”。以前是后端某一台挂影响一部分现在是负载均衡器挂影响全部。这就是典型的风险集中你为了解决单点自己又造了一个新的、更危险的单点。我见过最典型的场景后端Web层做了两台Nginx前面挂了一台HAProxy做转发大家觉得高可用已经搞定了。结果某个深夜HAProxy所在机器内存故障重启整站瘫痪。原因很简单——负载均衡器本身没有做任何冗余。高可用负载均衡的核心从来不是“我有多台后端”而是“入口这一层也必须有多台机器冗余并且它们之间要能自动切换”。多台负载均衡器共享一个虚拟IPVIP平时只有一台在干活它出问题后另一台立刻顶上整个切换过程对用户来说几乎无感知。这套机制就是主备高可用。1.3 “高可用”落到数字上是什么概念聊高可用不能只说“不挂”。业内通常用SLA来衡量99.9%可用性意味着一年最多宕机8.76小时99.99%可用性一年最多52.56分钟。负载均衡作为流量入口它本身的可用性指标必须比后端更高否则整个服务的SLA就卡死在入口这一层。落到实际参数上你的故障转移时间由几个环节决定VRRP通告间隔主备节点之间心跳报文的发送频率默认1秒健康检查判定时间负载均衡发现后端故障的速度由探测间隔和失败次数共同决定脚本检测执行周期检测HAProxy进程的脚本多久跑一次。这几个值加起来就是一次故障转移的“理论最短时间”。在我的经验里尽量把整个切换控制在3~5秒以内才能谈“高可用”。如果切换要花几十秒那跟直接宕机其实也没太大区别——用户早就刷新页面失败开始骂人了。2. 三种主流选型Nginx、HAProxy、LVS怎么挑2.1 先分清四层与七层负载均衡选型之前必须理解一个基础概念四层和七层。四层负载均衡工作在传输层核心是IP 端口转发。它不关心传输的内容是什么拿到数据包就按规则转发性能很高。七层负载均衡工作在应用层可以解析HTTP协议内容能根据URL路径、域名、请求头、Cookie等做精细化路由。比如同一个入口/api的请求转发到后端A/static的请求转发到后端B只有七层负载均衡能实现。这里有个常见误区有人觉得Nginx只能做七层不能用做四层。其实Nginx有stream模块完全可以做四层TCP/UDP转发只是很多人没用到。HAProxy原生就同时支持四层和七层。LVS是个纯粹的流量转发内核模块主要工作在四层。2.2 三款软件负载均衡的核心差异我把经常拿来对比的三款主流软件负载均衡器按实际使用中的关键维度整理一下对比项NginxHAProxyLVS工作层级七层为主stream模块支持四层四层和七层都支持纯四层性能足够高单机轻松扛数万QPS极高事件驱动模型连接管理优秀最高基于内核转发配置复杂度熟悉但内置变量/模块较多简单直观配置格式统一相对复杂要理解DR/NAT/TUN模式健康检查HTTP健康检查强大TCP/HTTP都强大参数精细需要配合脚本或其他工具会话保持ip_hash、sticky模块cookie、source等多样化本身不擅长动态摘流需要reload或脚本改配置支持命令行/接口动态调整服务器状态要改ipvsadm规则实际选型的结论是追求极致性能、场景是海量四层转发选LVS团队对Nginx最熟、业务以HTTP为主选Nginx需要精细的连接管理、四层七层混合场景、丰富的健康检查策略选HAProxy非常顺手。2.3 高可用组合怎么搭光有负载均衡器还不够还得让多台负载均衡器完成自动主备切换。最常见的高可用组合有四种Keepalived Nginx经典Web架构组合Nginx扛七层转发Keepalived负责VIP漂移Keepalived HAProxy中间件代理、数据库负载均衡、混合流量场景很合适HAProxy提供灵活的转发和健康检查Keepalived LVS大流量四层入口的标配云厂商托管LB云上环境优先考虑但原理我仍然建议搞懂。我这次选择的是Keepalived HAProxy主要原因是场景比较杂既有HTTP服务又有一些TCP协议的中间件需要做端口转发。HAProxy一套配置就能覆盖四层和七层健康检查的探测逻辑非常细而且自带的统计页面在排查问题时极其好用。2.4 为什么不用DNS轮询或者K8s Service替代有人会问既然DNS解析可以做多IP轮询为什么不直接用DNS做高可用因为DNS缓存是最大的敌人。你把同一个域名解析成多个IP客户端访问时确实会轮换但一旦某个IP的机器故障已经解析到那个IP的客户端还会继续尝试访问直到缓存过期。切换速度完全不可控达不到秒级。至于Kubernetes里内置的Service负载均衡确实解决了容器场景下的流量分发问题。但如果你维护的是传统虚拟机或物理机架构或者业务还没容器化Keepalived配合软件负载均衡依然是低成本、高可控、经过大量生产验证的成熟方案。3. VIP漂移、健康检查与等开销调度三个核心机制讲透3.1 VIP与VRRP两个节点共享一个“门牌号”高可用负载均衡最底层的机制是虚拟IP VRRP协议。虚拟IPVIP是一个不固定绑定在某一台物理机器上的IP地址。两台负载均衡机器组成一个VRRP组通过优先级选出一台作为MasterVIP由Master持有Backup节点持续监听Master的心跳报文。打个比方小区只有一个门牌号但门卫室有两间房。平时A房间值班B房间待命A房间每隔一秒往公共走廊喊一声“我还在”。一旦A房间没声音了B房间立刻出来接替值班把门牌号挂到自己房间门口。这个过程就是VIP漂移。关键细节在于VIP漂移不只是“IP地址换了一台机器”新Master接管VIP后还会发送免费ARP报文通知交换机“这个IP对应的MAC地址变了”。否则交换机缓存里还记录着VIP指向旧Master的MAC流量依然会送错地方。这个细节后面排障还会再提到。VRRP还有几个参数值得理解priority是选举权重范围1~255值越大越优先advert_int是心跳报文间隔默认1秒preempt决定备节点在恢复后是否主动抢回Master身份。3.2 健康检查你以为的“活着”和真正的“活着”是两回事高可用系统里健康检查是连接负载均衡器和后端服务的关键桥梁。它解决的问题只有一个怎么判断一台后端机器真的能处理新请求。最低级的健康检查是TCP端口探测只要端口能连上就算健康。但这个方法有盲区——进程还在端口还开着不代表服务真的正常。典型场景是应用假死Nginx进程没退出但工作进程全卡住了端口能连上请求却处理不了。更可靠的做法是HTTP业务探测负载均衡定期请求一个专门设计的内置接口比如GET /healthz接口内部会检查依赖的数据库连接池、缓存、核心线程池状态全部正常才返回200。这样“端口活着”和“业务活着”就被区分开了。健康检查的参数同样重要主要看三个interval探测间隔、fall连续失败多少次判死、rise连续成功多少次复活。参数设得太激进后端一有抖动就被摘掉设得太保守故障发现太慢。我的建议是interval设为3~5秒fall设为2~3次rise设为2次这样单次抖动不会误杀节点真实故障也能在10秒内被识别。3.3 等开销调度策略让后端机器干差不多的活“等开销负载均衡”这个词近段时间讨论得比较多它的核心思想容易被人误解。很多新人以为负载均衡就是把请求平均分给每台机器一人一个公平合理。实际上“等开销”追求的不是请求数量相等而是每台后端机器承担的开销和压力大致相当。举个例子两台后端机器一台4核一台8核。如果按请求数平均分4核机器很快被打满8核机器还有大量余量整体资源利用率反而不均衡。这时候应该设置权重为1:2让强机器多吃流量才是真正的“等开销”。HAProxy提供了多种调度策略最常用的有几种roundrobin加权轮询按权重轮流分配请求。适合请求处理时间相近、短平快的Web接口这是最常用的默认策略leastconn最少连接优先把新请求分给当前连接数最少的后端。适合长连接、WebSocket、数据库代理这种连接维持时间长的场景因为它能动态避免某台机器堆积大量连接source对源IP做哈希同一个客户端IP总是分到同一台后端。可以天然实现会话保持但缺点是负载均衡效果受源IP分布影响。我的选择经验是如果后端接口都是毫秒级短请求roundrobin就很好了一旦有慢请求或长连接混进来leastconn明显更接近“等开销”的目标。你可以在HAProxy统计页面上观察每台后端的连接数和队列深度对比两种策略的效果数据会告诉你答案。4. 主备架构落地Keepalived HAProxy 完整配置与切换验证4.1 拓扑与IP规划动手配置之前先把架构画清楚。这里我以两台负载均衡器、两台后端应用服务器的经典拓扑为例角色主机名IP地址说明负载均衡主节点lb-01192.168.10.10平时持有VIP承担流量转发负载均衡备节点lb-02192.168.10.11监听心跳主节点故障时接管VIP虚拟IPVIP192.168.10.20对客户端暴露的唯一入口后端Web节点Aweb-01192.168.10.30实际处理业务请求后端Web节点Bweb-02192.168.10.31实际处理业务请求客户端访问的地址是http://192.168.10.20这个VIP平时绑定在lb-01上。lb-01挂了VIP自动漂移到lb-02。后端两台Web节点则通过HAProxy的健康检查自动摘除或恢复。4.2 Keepalived配置主节点与备节点先安装基础组件CentOS/RHEL系和Ubuntu系命令略有差异但安装包名称基本一致# RHEL/CentOS系 yum install -y keepalived haproxy # Ubuntu/Debian系 apt install -y keepalived haproxy主节点lb-01的Keepalived配置如下路径是/etc/keepalived/keepalived.confglobal_defs { router_id LB_MASTER vrrp_skip_check_adv_addr script_user root enable_script_security } vrrp_script check_haproxy { script /etc/keepalived/check_haproxy.sh interval 2 fall 2 rise 2 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 150 advert_int 1 authentication { auth_type PASS auth_pass 4a2c9f7b } virtual_ipaddress { 192.168.10.20/24 dev eth0 } track_script { check_haproxy } }备节点lb-02的配置基本相似差异主要有两处state改为BACKUPpriority改为100。注意virtual_router_id两边必须一致它是VRRP分组的标识不一致就无法组成一个主备组。这里解释一个关键点vrrp_script check_haproxy段的作用是——Keepalived每隔2秒执行一次检查脚本如果脚本返回非0表示HAProxy进程异常本机优先级自动降低从而触发VIP切换到另一台节点。这个脚本是所有高可用配置里最容易被遗漏、却又最关键的一环后面踩坑部分我还会展开讲。配套的健康检查脚本/etc/keepalived/check_haproxy.sh内容如下#!/bin/bash # 检查haproxy进程是否存活 if pgrep -x haproxy /dev/null; then exit 0 else exit 1 fi记得给脚本加执行权限chmod x /etc/keepalived/check_haproxy.sh4.3 HAProxy配置前端入口与后端分发策略HAProxy配置文件路径是/etc/haproxy/haproxy.cfg完整配置如下global log /dev/log local0 log /dev/log local1 notice maxconn 50000 user haproxy group haproxy stats socket /var/run/haproxy.sock mode 660 level admin daemon defaults log global mode http option httplog option dontlognull retries 3 timeout connect 5s timeout client 30s timeout server 30s maxconn 3000 frontend web_front bind *:80 default_backend web_servers backend web_servers mode http balance roundrobin option httpchk GET /healthz server web-01 192.168.10.30:8080 weight 1 check inter 3s fall 3 rise 2 server web-02 192.168.10.31:8080 weight 2 check inter 3s fall 3 rise 2 listen stats bind *:8404 mode http stats enable stats uri /stats stats refresh 5s stats auth admin:admin123几个关键参数逐一说一下为什么这么配balance roundrobin加权轮询适合大多数HTTP短请求场景。web-01和web-02权重设置成1:2模拟两台机器性能不同的情况option httpchk GET /healthz健康检查探测的是/healthz接口而不是只测端口。这个接口需要你在后端Web服务里提前实现返回200表示健康非200表示不健康inter 3s fall 3每3秒探测一次连续3次失败把节点标记为不可用weight 2web-02权重更高正常状态下会承担更多流量这正好呼应前面说的“等开销”思路——让性能强的机器多干活listen stats暴露一个统计页面后面压测和观察请求分布全靠它。如果你的后端混合了TCP协议服务比如要转发Redis、MySQL等流量可以在配置里加一个四层转发段frontend redis_front bind *:6379 mode tcp default_backend redis_servers backend redis_servers mode tcp balance leastconn server redis-01 192.168.10.40:6379 check inter 3s fall 3 rise 2 server redis-02 192.168.10.41:6379 check inter 3s fall 3 rise 24.4 启动、开机自启与VIP切换验证配置完成后启动服务并设置开机自启# 两台机器都要执行 systemctl enable keepalived haproxy systemctl start keepalived systemctl start haproxy然后在lb-01上查看VIP绑定情况ip addr show eth0正常情况下192.168.10.20应该出现在eth0网卡上并标记为eth0:0或直接附加在eth0上。再用ip addr确认一次Master角色正常。接着做一次简单的切换验证在lb-01上停掉keepalived服务模拟主节点故障。systemctl stop keepalived几秒后再看lb-02的网络接口VIP应该已经漂移过来了ip addr show eth0此时用curl http://192.168.10.20/healthz依然能正常返回说明客户端无感知切换完成。最后重启lb-01上的keepalived观察VIP是否自动抢回这取决于preempt策略默认是抢占模式Master恢复后会重新抢回VIP。4.5 双主架构的扩展思路主备架构的缺点是备节点在正常情况下完全不承担流量资源利用率只有50%。流量规模更大的团队可以做双主方案两条VIPlb-01持有VIP1lb-02持有VIP2两个VIP分别带一部分后端节点同时两者互为备份。lb-01挂了VIP1漂移到lb-02lb-02同时服务两条VIPlb-02挂了同理。域名解析或云解析层面把两个VIP都配上做轮询流量自然分摊到两台负载均衡器。这套方案把高可用和扩展性结合在了一起成本只是多算一个VIP非常值得在架构设计阶段就考虑进去。5. 故障演练与压测观察验证高可用的四个动作5.1 故障演练清单配置完成不代表高可用生效必须通过故障演练验证。我维护的每套高可用负载均衡环境上线前都会做一组模拟故障测试测试项和预期结果如下故障动作预期现象验证方式停掉web-01的Nginx服务web-01被健康检查标记为DOWN流量全部转发到web-02访问VIP请求全部成功HAProxy统计页看web-01状态为DOWN停掉lb-01的HAProxy进程vrrp_script检测到HAProxy异常VIP漂移到lb-02lb-02上ip addr能看到VIP访问VIP正常直接关机lb-01VIP漂移到lb-02整个入口不中断备用机上ip addr确认VIP接管在后端机器模拟接口变慢人为延迟健康检查超时节点被自动摘除恢复后自动加回HAProxy统计页观察状态变化访问流量始终成功每次演练之后我都会把实际观察到的切换时间记录下来。从触发故障到VIP完成漂移理想状态应该在3~5秒内完成超过这个范围就需要回头检查健康检查参数和VRRP通告间隔。5.2 压测看调度策略差异故障演练验证的是“坏节点会被摘掉”压测则是验证“活着的节点确实在按预期分摊流量”。压测工具我用的是wrk比较轻量也可以直接用ab# 压测入口VIP100并发总共5万请求 wrk -t4 -c100 -d30s http://192.168.10.20/压测过程中打开HAProxy统计页面http://192.168.10.20:8404/stats可以看到web-01和web-02的请求数、当前连接数、队列深度。如果配置的是roundrobin且权重1:2压测结束后两台后端的请求数比例应该接近1:2——web-01约1.7万web-02约3.3万。再用混合长短请求对比一下把后端一个接口人为加入2秒延迟压测场景里你会看到leastconn策略下慢接口所在的机器连接数会被控制住而roundrobin会把大量请求堆到慢机器上导致响应时间整体飙升。这个对比很容易直观地感受到“等开销”和“平均分”的差别。5.3 日常发布优雅摘流别让流量在发布瞬间“硬切”高可用负载均衡不只是故障时的保护伞日常发布变更时它也是好用的工具。后端服务要重启、发布新版本时直接在HAProxy里摘掉该节点等流量归零后再操作这是标准做法。传统做法是disable server把节点立即标记为下线已经建立的连接会被强制断开。对于长连接场景这会造成用户侧请求中断。更好的做法是HAProxy 2.2版本开始支持的drain模式——不再向该节点分配新连接但允许存量连接自然结束。# 通过stats socket进入管理接口 echo set server web_servers/web-02 state drain | socat stdio /var/run/haproxy.sock # 等存量连接结束后查看节点连接数归零 echo show servers state | socat stdio /var/run/haproxy.sock # 发布完成后恢复节点 echo set server web_servers/web-02 state ready | socat stdio /var/run/haproxy.sock这套操作流程的意义在于流量切换不是在瞬间强制切断而是平滑过渡配合权重和drain模式可以让发布过程的流量变化非常平滑。6. 生产环境四个坑与完整排错思路6.1 坑一VIP漂移成功了流量就是不通——ARP缓存问题现象手动停掉主节点Keepalived后备节点ip addr确实看到了VIP但客户端访问VIP全部超时。排查链路先确认VIP漂移 → 在备节点上检查VIP是否绑定成功 → 在网关或客户端机器上执行arp -a | grep 192.168.10.20结果发现ARP缓存里记录的MAC地址依然是主节点的网卡MAC。问题本质VRRP切换后新主节点会发送免费ARP来更新交换机的MAC表。但如果网络设备开启了ARP严格学习或MAC表老化时间较长免费ARP可能没有被正确处理旧表项迟迟不更新流量继续被转发到已经下线的旧主节点。解决办法配置Keepalived时在virtual_ipaddress块中显式启用nopreempt配合garp_master_delay参数或在交换机上手动清一次ARP表。我个人的习惯是在做VIP漂移验证前先在交换机上执行clear arp再观察Keepalived是否自动发送免费ARP报文这样能快速判断问题方向。6.2 坑二健康检查把“慢而健在”的节点误杀现象某天业务高峰期收到后端响应变慢的报警。登录HAProxy统计页一看一台后端节点被标记成DOWN了但登录机器检查CPU、内存都正常服务进程也没挂。排查链路先看后端进程状态 → 手动在负载均衡机器上curl http://192.168.10.30/healthz发现响应花了4秒多才返回 → 再看HAProxy健康检查参数inter 3s fall 3也就是说连续3次探测都超过3秒没响应节点就被判死了。问题本质健康检查的超时时间取决于timeout connect和timeout server而探测间隔是3秒。后端接口一旦偶发慢查询连续几次都卡在3秒以上就会被误杀。这个“慢而健在”的节点被摘掉后流量全部压到另一台反而把另一台也拖慢了。解决办法把健康检查超时调宽到5~8秒fall调大到4~5次给后端足够的抖动容忍度。同时优化/healthz接口本身的查询逻辑让它只检查核心依赖绝不执行重查询。健康检查的接口必须“轻”它只回答一个二进制问题现在能不能接流量。6.3 坑三脑裂——两台机器同时持有VIP现象某次网络调整之后发现VIP在lb-01和lb-02上同时出现了两边都在对外提供服务导致后端出现连接混乱、请求超时、日志里出现大量异常的MAC漂移记录。排查链路抓包看VRRP报文是否正常到达对端 → 在lb-02上执行tcpdump -i eth0 vrrp结果20秒内一个VRRP报文都没收到 → 检查网络路径发现交换机上配置的ACL把VRRP使用的组播地址和协议号拦截了。问题本质VRRP依赖心跳报文来维持“主我来当备你待命”的默契。心跳被防火墙或网络策略阻断后双方都认为对方已经死亡于是同时进入Master状态都绑定VIP脑裂就发生了。解决办法开源Keepalived支持单播VRRP模式绕过组播的网络限制。在vrrp_instance里配置unicast_peer指向对端IP同时确保防火墙放行单播报文vrrp_instance VI_1 { state BACKUP interface eth0 virtual_router_id 51 priority 100 advert_int 1 unicast_src_ip 192.168.10.11 unicast_peer { 192.168.10.10 } authentication { auth_type PASS auth_pass 4a2c9f7b } virtual_ipaddress { 192.168.10.20/24 dev eth0 } }单播模式比组播模式更可控排障时也更容易抓包确认TCPDump过滤对端IP就能看到心跳是否送达。脑裂的最终防线是监控告警脚本定期检测VIP是否在预期节点上如果两台同时持有立即人工介入。6.4 坑四Keepalived根本不知道HAProxy已经死了这个坑我认为是最隐蔽、也最致命的。现象某次主节点HAProxy进程因为配置语法错误退出了但VIP还稳稳地绑定在lb-01上。所有请求都到达了lb-01却没有任何进程处理转发整站直接瘫痪。检查lb-01Keepalived进程活着VIP也在心跳正常备节点完全没有触发切换。问题本质默认的Keepalived只负责VRRP心跳和VIP管理它压根不关心HAProxy进程是否存活。HAProxy虽然死了但Keepalived自身很健康它会继续发送“我还活着”的心跳VIP自然也不会飘走。解决办法这正是4.2节里配置vrrp_script check_haproxy的原因。检查脚本确保HAProxy进程异常时本机VRRP优先级自动降低强制触发VIP切换。这个脚本必须配合track_script一起使用两者缺一不可。还要注意脚本里不能只写进程检查更完善的做法是同时用haproxy -c -f /etc/haproxy/haproxy.cfg校验配置或者用socat请求HAProxy的socket接口做一次真实探活。到这里这套高可用负载均衡体系的原理、落地、验证和坑点基本都覆盖了。我自己的习惯是每次新环境搭完都会重新读一遍这几页笔记逐个坑对照检查一遍再交付。如果只留三条最核心的建议我会说先写好vrrp_script再谈高可用健康检查宁可宽容不要激进任何变更之后都跑一遍故障演练——这三点做到了负载均衡这一层基本就稳了。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →