1. 高可用架构的三种典型落地路径拆解
线上业务跑得越久,越能体会到一个朴素的道理:单点就是定时炸弹。不管是电商大促、内部OA、还是API网关,只要某台机器一挂,业务方电话立刻打过来。高可用这个话题说了很多年,但真正落地的时候,方案选型往往决定了后面运维的幸福感。今天想聊的这套组合——Keepalived热备、Keepalived+LVS、HAproxy——基本覆盖了中小规模到中大规模场景下最常见的高可用需求。它们不是互斥的,很多时候是搭配使用的,但每一种都有自己最擅长的位置。
先把这三个东西的定位说清楚,不然后面容易混。Keepalived本质上是一个VRRP协议的实现,用来做IP漂移和状态检测,它解决的是"谁来接管"的问题;LVS是内核层面的四层负载均衡,性能极高,解决的是"流量怎么分发"的问题;HAproxy是用户态的四层/七层负载均衡,功能丰富,解决的是"流量怎么智能分发"的问题。三者组合起来,就形成了从接入层到应用层的完整高可用链路。
这篇文章适合谁看?如果你正在搭建一套对外服务,或者内部系统要求7x24不能断,又或者你已经在用Nginx做负载但发现健康检查不够灵活,那这篇内容应该能给你一些直接能抄的配置和踩坑经验。我会从架构思路、核心原理、实操配置、故障排查四个维度展开,每个方案都给出可复现的步骤和参数计算过程。不堆概念,只讲能落地的东西。
2. Keepalived热备:最朴素也最可靠的双机方案
2.1 为什么选Keepalived而不是自己写脚本
很多人第一反应是:我写个crontab定时ping一下,不通就改IP不就行了?理论上可行,但实际生产环境里这么干的人,最后基本都换成了Keepalived。原因有几个:第一,自己写的脚本很难处理脑裂场景,两台机器互相觉得对方挂了,同时抢IP,结果IP冲突导致服务彻底不可用;第二,VRRP协议本身有优先级、抢占延迟、认证机制,这些都是经过大量验证的;第三,Keepalived还能顺带做服务健康检查,不只是检查主机存活,还能检查Nginx、MySQL这些进程是否正常。
Keepalived的核心工作模型是这样的:两台机器组成一个VRRP组,一个MASTER一个BACKUP,共享一个虚拟IP(VIP)。MASTER定期发送VRRP通告包,BACKUP收不到就认为MASTER挂了,自己升为MASTER并接管VIP。整个过程通常在1到3秒内完成,对上层业务基本透明。
2.2 双机热备的完整配置实操
假设我们有两台服务器,IP分别是192.168.10.11和192.168.10.12,VIP规划为192.168.10.100。操作系统以常见的Linux发行版为例,安装Keepalived直接包管理器搞定:
# Debian/Ubuntu系 apt install keepalived -y # RHEL/CentOS系 yum install keepalived -yMASTER节点的配置文件/etc/keepalived/keepalived.conf:
global_defs { router_id node_master script_user root enable_script_security } vrrp_script chk_nginx { script "/usr/bin/killall -0 nginx" interval 2 weight -20 fall 2 rise 2 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass MySecurePass123 } virtual_ipaddress { 192.168.10.100/24 dev eth0 } track_script { chk_nginx } }BACKUP节点只需要改三处:router_id改成node_backup,state改成BACKUP,priority改成90。其他保持一致。
这里有几个参数值得展开说。virtual_router_id在同一个网段内必须唯一,如果你们内网有多组Keepalived,这个值冲突了会出现莫名其妙的问题,我见过有人图省事全用51,结果两组集群互相干扰。advert_int是通告间隔,默认1秒,意味着故障切换最快1秒左右,如果业务对切换时间更敏感可以调到0.5,但会增加网络开销。weight -20配合priority 100,意思是当Nginx检查失败时,优先级降到80,低于BACKUP的90,从而触发切换。
2.3 健康检查脚本的坑与技巧
vrrp_script里的检查脚本是整个方案里最容易出问题的地方。上面用的killall -0 nginx只是检查进程是否存在,但进程在不代表服务正常。更靠谱的做法是检查端口或者发一个HTTP请求:
#!/bin/bash # /etc/keepalived/check_nginx.sh count=$(ss -lntp | grep -c ':80 ') if [ "$count" -eq 0 ]; then exit 1 fi exit 0注意脚本必须有可执行权限,而且script_user要配置正确,否则Keepalived以非root身份跑脚本时可能权限不足。另外fall和rise这两个参数很关键,fall 2表示连续失败2次才认为服务挂了,rise 2表示连续成功2次才恢复。设置太小容易误判,设置太大切换又慢,一般2到3是比较平衡的值。
实操心得:健康检查脚本里千万不要做耗时操作,比如curl一个慢接口。Keepalived的脚本执行是阻塞的,脚本卡住会导致整个检查机制失效。如果确实需要检查HTTP接口,务必加上
--max-time 2这类超时参数。
3. Keepalived+LVS:四层负载均衡的高性能组合
3.1 LVS的三种转发模式怎么选
LVS有三种工作模式:NAT、DR、TUN。NAT模式配置简单但性能瓶颈在调度器,因为请求和响应都要经过它;TUN模式支持跨机房但需要隧道支持,配置复杂;DR模式是生产环境用得最多的,调度器只改MAC地址,响应直接由后端服务器返回给客户端,性能几乎线性扩展。
DR模式有个硬性要求:调度器和后端Real Server必须在同一个二层网络里,因为要改MAC。另外Real Server上需要配置VIP到lo接口,并且要抑制ARP响应,否则VIP会冲突。这两点是新手最容易翻车的地方。
3.2 DR模式完整搭建流程
环境规划:调度器两台做Keepalived主备(192.168.10.11/12),VIP为192.168.10.100,后端两台Real Server为192.168.10.21/22。
调度器上安装ipvsadm和keepalived:
yum install ipvsadm keepalived -yKeepalived配置在双机热备基础上增加virtual_server段:
virtual_server 192.168.10.100 80 { delay_loop 6 lb_algo wrr lb_kind DR persistence_timeout 50 protocol TCP real_server 192.168.10.21 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 connect_port 80 } } real_server 192.168.10.22 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 connect_port 80 } } }lb_algo wrr是加权轮询,如果后端机器配置不同可以调整weight。persistence_timeout 50表示50秒内同一客户端请求打到同一台后端,对有session的业务很重要,但如果是无状态服务建议设为0,让负载更均匀。
Real Server上需要执行以下配置:
# 配置VIP到lo接口 ip addr add 192.168.10.100/32 dev lo # 抑制ARP响应 echo "1" > /proc/sys/net/ipv4/conf/lo/arp_ignore echo "2" > /proc/sys/net/ipv4/conf/lo/arp_announce echo "1" > /proc/sys/net/ipv4/conf/all/arp_ignore echo "2" > /proc/sys/net/ipv4/conf/all/arp_announce这四个ARP参数的含义要理解清楚:arp_ignore=1表示只回答目标IP是本机接口IP的ARP请求,arp_announce=2表示总是使用最佳本地地址发送ARP。不配置这两个,Real Server会响应VIP的ARP请求,导致客户端直接连到Real Server,绕过调度器。
3.3 调度算法与参数调优
LVS支持的调度算法有十几种,常用的就几种:
| 算法 | 适用场景 | 特点 |
|---|---|---|
| rr | 后端性能一致 | 简单轮询 |
| wrr | 后端性能有差异 | 按权重分配 |
| lc | 长连接场景 | 连接数少的优先 |
| wlc | 通用场景 | 加权最小连接,默认推荐 |
| sh | 需要会话保持 | 源地址哈希 |
生产环境我一般推荐wlc,它综合考虑了连接数和权重,比单纯轮询更智能。如果业务有状态且不想用persistence_timeout,可以用sh做源地址哈希,但缺点是负载可能不均。
注意事项:DR模式下Real Server的网关不需要指向调度器,它直接用自己的默认网关返回响应。如果Real Server和客户端不在同一网段,要确保路由正确,否则会出现"请求通了但响应回不去"的经典问题。
4. HAproxy:七层负载均衡的灵活之选
4.1 什么时候该用HAproxy而不是LVS
LVS性能确实强,但它是四层的,只能看IP和端口,没法根据URL、Header、Cookie做转发决策。如果你的业务需要按域名分流、按路径转发、做SSL终止、或者需要更精细的健康检查,那HAproxy就是更合适的选择。HAproxy的性能虽然不如LVS内核级转发,但单机跑几万并发连接毫无压力,对绝大多数业务来说完全够用。
HAproxy的另一个优势是配置直观,日志详细,出问题好排查。LVS出问题的时候你只能看ipvsadm的连接数,HAproxy可以直接看日志知道哪个后端挂了、为什么挂。
4.2 HAproxy核心配置详解
安装同样简单:
yum install haproxy -y一个典型的生产配置:
global log 127.0.0.1 local0 info maxconn 65535 daemon nbproc 4 cpu-map 1 0 cpu-map 2 1 cpu-map 3 2 cpu-map 4 3 defaults log global mode http option httplog option dontlognull option http-server-close option forwardfor except 127.0.0.0/8 retries 3 timeout connect 5s timeout client 30s timeout server 30s timeout check 3s frontend web_front bind *:80 bind *:443 ssl crt /etc/haproxy/certs/site.pem acl is_api path_beg /api acl is_static path_beg /static use_backend api_servers if is_api use_backend static_servers if is_static default_backend web_servers backend web_servers balance roundrobin option httpchk GET /health http-check expect status 200 server web1 192.168.10.31:8080 check inter 2s fall 3 rise 2 server web2 192.168.10.32:8080 check inter 2s fall 3 rise 2 backend api_servers balance leastconn option httpchk GET /api/health server api1 192.168.10.41:9090 check inter 2s fall 3 rise 2 server api2 192.168.10.42:9090 check inter 2s fall 3 rise 2 backend static_servers balance source server static1 192.168.10.51:80 check server static2 192.168.10.52:80 check listen stats bind *:8404 stats enable stats uri /haproxy-status stats auth admin:StrongPass456 stats refresh 10snbproc 4开启4个进程,配合cpu-map绑定到不同CPU核心,能充分利用多核性能。但要注意,多进程模式下一些统计信息不共享,而且HAproxy 2.0以后更推荐用多线程模式(nbthread)替代多进程。
option httpchk GET /health是七层健康检查,比四层的TCP检查更准确。http-check expect status 200要求返回200才算健康,这样后端应用即使端口在监听但逻辑异常也能被摘除。
4.3 HAproxy与Keepalived的整合
HAproxy本身不提供IP漂移能力,所以生产环境通常是Keepalived+HAproxy的组合。配置方式和双机热备一样,只是健康检查脚本改成检查HAproxy进程:
vrrp_script chk_haproxy { script "/usr/bin/killall -0 haproxy" interval 2 weight -20 fall 2 rise 2 }这样两台HAproxy做主备,VIP在谁身上谁对外服务。如果想做双活,可以配合DNS轮询或者上层再加一层LVS,但架构复杂度会上升不少,中小规模业务用主备就够了。
5. 三种方案的对比与选型决策
5.1 性能、功能、复杂度三维对比
| 维度 | Keepalived热备 | Keepalived+LVS | HAproxy |
|---|---|---|---|
| 工作层级 | 无(只做IP漂移) | 四层 | 四层/七层 |
| 吞吐性能 | 取决于后端 | 极高,十万级并发 | 高,万级并发 |
| 功能丰富度 | 低 | 中 | 高 |
| 配置复杂度 | 低 | 中高 | 中 |
| 健康检查 | 脚本自定义 | TCP/HTTP | TCP/HTTP/自定义 |
| SSL终止 | 不支持 | 不支持 | 支持 |
| 会话保持 | 不涉及 | persistence | cookie/source |
| 适用规模 | 双机 | 中大型 | 中小型 |
选型的核心逻辑其实就一句话:先看规模,再看功能需求。如果后端就两台机器,业务简单,Keepalived热备足够了;如果后端几十台,追求极致性能,上LVS;如果需要按URL分流、SSL卸载、精细健康检查,HAproxy更合适。实际生产中,LVS+HAproxy的分层架构也很常见:LVS做四层入口,HAproxy做七层分发,Keepalived保证每一层都不单点。
5.2 分层组合架构实战
一个典型的中大型架构是这样的:最外层两台LVS+Keepalived做四层入口,VIP对外;LVS后面挂多台HAproxy做七层分发;HAproxy后面是真正的应用服务器。这样LVS扛住流量峰值,HAproxy做灵活路由,每一层都无单点。
这种架构的配置要点是:LVS的Real Server变成HAproxy的IP,HAproxy的backend指向应用服务器。健康检查要逐层配置,LVS检查HAproxy端口,HAproxy检查应用健康接口。任何一层出问题都能自动摘除,不会影响整体服务。
实操心得:分层架构虽然强大,但排查问题的时候链路变长了。建议每一层都开启详细日志,并且给请求加上统一的trace id,否则一个请求出问题你要从LVS查到HAproxy再查到应用,没有trace id会非常痛苦。
6. 常见故障与排查技巧实录
6.1 脑裂问题的识别与预防
脑裂是高可用架构里最危险的问题。两台机器都认为自己是MASTER,同时持有VIP,导致IP冲突、连接混乱。识别方法很简单:在两端同时执行ip addr show,如果都看到VIP,那就是脑裂了。
预防脑裂有几个手段:第一,确保VRRP通告链路可靠,不要用业务网卡做心跳;第二,配置nopreempt避免抢占导致频繁切换;第三,可以加一个第三方仲裁,比如用脚本检测到双MASTER时强制降级一台。Keepalived本身没有内置仲裁机制,这点要有清醒认识。
# 检测脑裂的简单脚本 vip="192.168.10.100" if ip addr show | grep -q "$vip"; then # 本机持有VIP,尝试ping另一台的物理IP if ping -c 2 -W 1 192.168.10.12 > /dev/null 2>&1; then # 对端也活着,可能脑裂,记录日志 logger "Possible split-brain detected" fi fi6.2 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| VIP不漂移 | VRRP通告被防火墙拦截 | 检查iptables是否放行VRRP协议 |
| 切换后服务不可用 | 后端服务未随VIP启动 | 检查notify脚本 |
| LVS后端全部不可用 | Real Server ARP未抑制 | 检查arp_ignore/arp_announce |
| HAproxy健康检查误判 | 检查间隔太短或超时太短 | 调整inter/fall/rise |
| 连接不均匀 | 调度算法或persistence配置 | 检查lb_algo和timeout |
| SSL握手失败 | 证书链不完整 | 检查pem文件是否包含中间证书 |
| 高并发下丢包 | 连接数超限 | 检查maxconn和内核参数 |
6.3 内核参数调优清单
高并发场景下,默认内核参数往往不够用。以下参数建议在调度器和HAproxy上都配置:
# /etc/sysctl.conf net.ipv4.ip_forward = 1 net.ipv4.ip_nonlocal_bind = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_max_syn_backlog = 8192 net.core.somaxconn = 65535 net.core.netdev_max_backlog = 65535 net.ipv4.tcp_max_tw_buckets = 6000 net.ipv4.tcp_syncookies = 1ip_nonlocal_bind这个参数特别重要,它允许进程绑定非本机IP,Keepalived和HAproxy在VIP漂移后需要这个才能正常绑定。不配置的话,VIP切过来了但服务起不来,非常隐蔽。
注意事项:
tcp_tw_reuse和tcp_tw_recycle经常被一起提,但tcp_tw_recycle在NAT环境下会导致连接异常,新版本内核已经移除,不要再去开它。
7. 生产环境部署的几点个人体会
踩过几次坑之后,我对这套组合有几个比较深的体会。第一,配置一定要版本化管理,Keepalived和HAproxy的配置文件改动频繁,没有版本控制的话,出问题回滚都找不到之前的版本。第二,健康检查脚本要单独测试,不要直接上生产,我见过脚本里写错路径导致检查永远失败,结果VIP一直在漂移。第三,监控要覆盖每一层,不只是看VIP在谁身上,还要看后端Real Server的健康状态、连接数分布、响应时间,任何一个指标异常都可能是故障前兆。
另外关于切换时间,很多人追求越快越好,但实际上1到3秒的切换对大多数业务是可以接受的。如果强行把advert_int调到0.1秒,网络抖动就可能导致误切换,反而更不稳定。稳定性永远优先于极致的切换速度。
最后说一个容易被忽略的点:测试故障切换。不要等真出故障了才发现切换不工作。定期手动触发切换,比如停掉MASTER的Keepalived,观察VIP是否正常漂移、服务是否自动恢复。这个演练每个月做一次,比任何文档都管用。我个人的习惯是每次变更配置后都做一次切换测试,确认无误才算变更完成。