尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Keepalived双主热备架构实战:从配置到故障切换全解析

Keepalived双主热备架构实战:从配置到故障切换全解析 做业务运维最怕听到一句话刚才那台机器挂了你怎么没发现等赶到机房业务已经恢复但因为没人接管流量断了几分钟老板已经在工作群里问话了。这种场景我经历过不少次后来把线上那套经典的keepalived主备方案改造成双主热备架构两台机器总算没白花钱平时各自扛业务出故障时还能互相兜底。这套架构不神秘核心就是让keepalived同时维护两个VRRP实例两个实例分别带着不同的虚拟IP两台服务器互相作为对方的后备。这篇文章就围绕keepalived双主热备架构把设计思路、配置文件怎么写、故障切换怎么测、坑在哪里一步步讲清楚。如果你手里有两台云服务器或者物理机跑的是Nginx这类对外服务正愁一台机器挂了业务就中断、两台机器又只能闲一台那这篇内容正好适合你。文章里不会堆晦涩的协议术语所有配置都基于我实际在线上跑过、验证过的方案拿过去改改IP就能用。1. 双主热备到底解决了什么问题1.1 传统主备模式的资源浪费先说说大家最熟悉的一主一备。两台服务器一台是MASTER一台是BACKUPkeepalived通过VRRP协议让一台持有虚拟IP另一台默默待命。正常情况下所有流量都打在MASTER上BACKUP那台机器CPU利用率可能只有5%内存、带宽、磁盘全部闲置。这其实是巨大的浪费尤其现在云服务器按量计费你等于花了两台的钱只用到一台的性能。有人会想那我直接让流量负载均衡到两台服务器不就行了答案是可以但引入负载均衡器往往又多了一层单点而且LB本身挂了后端两台服务器再健康也没用。keepalived双主热备的思路很朴素两台机器都干活但干不同的活谁都不闲同时还互为对方的备份。1.2 双主热备的核心设计思路所谓双主本质上还是两个主备关系的叠加。具体的做法是在keepalived配置里定义两个VRRP实例实例A和实例B。实例A负责虚拟IP1让服务器1做MASTER、服务器2做BACKUP实例B负责虚拟IP2让服务器2做MASTER、服务器1做BACKUP。这样做有什么效果呢当两台机器都正常时VIP1绑定在服务器1上VIP2绑定在服务器2上流量到达VIP1的请求由服务器1处理到达VIP2的请求由服务器2处理两台机器都在为你创造价值。当服务器1宕机实例A检测到对端失联VIP1会自动漂移到服务器2上服务器2同时承担VIP1和VIP2的流量业务不会中断。等服务器1恢复VIP1再回到服务器1上一切恢复原样。这种架构最直观的优势就是资源利用率提升了一倍而不是简单地把一台机器当冷备藏着。从故障角度看双主架构和普通主备一样任何一个节点故障都能自动切换不会因为引入负载均衡器而增加新的单点。2. 方案选型与架构设计的关键点2.1 两个VRRP实例的规划与虚拟IP划分先说IP规划。以我经常用的一个例子来说两台服务器处于同一个二层网络服务器1eth0 192.168.1.11跑Nginx承载业务A服务器2eth0 192.168.1.12跑Nginx承载业务BVIP1 192.168.1.100对外提供业务A入口VIP2 192.168.1.101对外提供业务B入口实例规划上服务器1在VRRP实例A中扮演MASTERpriority设为150在VRRP实例B中扮演BACKUPpriority设为100。服务器2则相反在实例A中是BACKUPpriority为100在实例B中是MASTERpriority为150。优先级数值建议相差30到50太小了切换不稳定太大了也没必要关键是保证两个节点有明确的灾难切换顺序。有个细节值得注意两个实例需要区分虚拟路由器ID比如实例A用virtual_router_id 101实例B用virtual_router_id 102。不要图省事用同一个ID否则两个VRRP实例会互相干扰keepalived的日志里会出现一堆奇怪的告警。2.2 抢占、组播与单播模式如何取舍keepalived默认的MASTER恢复机制是抢占式。也就是说服务器1恢复后因为它优先级高会自动把VIP1抢回来。这个行为在大多数场景下是合理的因为越早让节点回到它该在的位置架构越清晰。但如果你的业务在切换后需要长时间预热缓存、或者数据库连接池要慢慢重建回切时会有一次短暂中断这种情况下建议关闭抢占。实现不抢占的方法是在MASTER侧的实例里加上nopreempt参数。注意nopreempt通常只写在MASTER节点上如果在两台机器上都写默认的优先级比较机制会变得很混乱容易出现两个节点互相僵持、VIP不漂移的情况。我在实施时如果遇到不希望频繁回切的业务一般只在原MASTER节点配置nopreempt另一台保持默认。另一个关键选择是组播还是单播。VRRP协议默认通过组播地址224.0.0.18发送心跳报文同一个二层网络内可以直接工作。但云环境比较特殊很多公有云的VPC网络不转发组播流量此时就必须开启单播模式。配置上也很简单在global_defs里定义vrrp_mcast_group4或者直接不用组播在实例里用unicast_src_ip和unicast_peer明确指定对端的单播地址。我遇到过不少刚上手的人把组播改成单播时漏改global段导致keepalived还是去尝试组播结果日志一直刷VRRP_Invalid实际上是对端根本收不到报文。记住单播模式下本端的组播配置可以忽略但每台机器都要写清楚自己的源地址和对端地址。2.3 健康检查脚本的设计原则双主热备能不能真正发挥作用健康检查脚本占了很大权重。默认情况下keepalived只检查VRRP协议层面的连通性即对端机器是否活着。但如果对端的Nginx进程已经僵死、端口不响应VRRP报文照样能发出来VIP不会漂移业务照样进不来。所以必须加上服务级别的健康检查。check脚本的设计核心是快速、准确、无副作用。比如检查Nginx最直接的方式是检查进程是否存活#!/bin/bash if pgrep -x nginx /dev/null 21; then exit 0 fi exit 1但进程存在不代表服务可用。更严谨的做法是发一次HTTP请求看返回码是不是200。不过要注意超时时间脚本如果卡住几秒keepalived的心跳周期也会被拖住。我一般给curl加--connect-timeout 2 --max-time 3并且在脚本外面keepalived再包一层timeout。脚本的返回值决定VIP的行为。keepalived通过track_script跟踪脚本当脚本返回非0时当前节点的优先级会被扣除weight指定的数值。比如当前priority是150weight是-20脚本失败后实际参与比较的优先级就变成130如果低于对端的优先级VIP就漂移过去。weight建议设置为-20左右避免扣得太多导致一两个瞬时抖动就把VIP切走。太敏感反而是灾难。3. keepalived配置逐步落地3.1 环境准备与安装无论你用CentOS、Ubuntu还是Debian第一步都是安装keepalived。以Ubuntu为例apt update apt install -y keepalived nginx systemctl enable --now keepalived装好后先确认版本版本不同配置语法有些差异比如早期的1.x和现在的2.x在解析配置上已经有一些区别。我建议至少使用2.0以上版本有些老的distribution源里还是1.4之类的功能不完整特别是脚本权重的处理方式不一致。配置前还需要做两件事第一关闭可能影响VRRP报文的防火墙规则或者放行VRRP协议第二确认两台机器时间同步虽然VRRP对时间同步要求不高但日志排查时时间不一致会让人抓狂。防火墙放行参考# 如果开启单播放行对端IP和VRRP协议 iptables -I INPUT -p vrrp -j ACCEPT iptables -I INPUT -s 192.168.1.12/32 -j ACCEPT iptables -I OUTPUT -p vrrp -j ACCEPT注意iptables规则重启后失效生产环境建议用firewalld或云安全组配置别偷懒。3.2 服务器1的完整实例配置服务器1的keepalived配置如下我直接贴出线上验证过的完整版global_defs { router_id NODE1 enable_script_security script_user root } vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 timeout 2 fall 2 rise 2 weight -20 } vrrp_instance VPC_A { state MASTER interface eth0 virtual_router_id 101 priority 150 advert_int 1 nopreempt auth_type PASS auth_pass vrrp_secure_101 unicast_src_ip 192.168.1.11 unicast_peer { 192.168.1.12 } virtual_ipaddress { 192.168.1.100/24 dev eth0 label eth0:vip1 } track_script { check_nginx } notify_master /etc/keepalived/notify.sh MASTER notify_backup /etc/keepalived/notify.sh BACKUP notify_fault /etc/keepalived/notify.sh FAULT } vrrp_instance VPC_B { state BACKUP interface eth0 virtual_router_id 102 priority 100 advert_int 1 auth_type PASS auth_pass vrrp_secure_102 unicast_src_ip 192.168.1.11 unicast_peer { 192.168.1.12 } virtual_ipaddress { 192.168.1.101/24 dev eth0 label eth0:vip2 } track_script { check_nginx } notify_master /etc/keepalived/notify.sh MASTER notify_backup /etc/keepalived/notify.sh BACKUP notify_fault /etc/keepalived/notify.sh FAULT }这段配置里VPC_A实例是服务器1的主身份VPC_B实例是服务器1的备身份。auth_pass一定不要用太简单的密码而且要保证和服务器2对应实例完全一致。unicast_peer数组里写对端IP多个对端就写多行。label eth0:vip1的意思是VIP绑定在eth0上并且给这个绑定起一个别名方便后面排查时用ip addr一眼看到。3.3 服务器2的实例配置服务器2的配置几乎就是镜像只是把两个实例的state和priority互换global_defs { router_id NODE2 enable_script_security script_user root } vrrp_script check_nginx { script /etc/keepalived/check_nginx.sh interval 2 timeout 2 fall 2 rise 2 weight -20 } vrrp_instance VPC_A { state BACKUP interface eth0 virtual_router_id 101 priority 100 advert_int 1 auth_type PASS auth_pass vrrp_secure_101 unicast_src_ip 192.168.1.12 unicast_peer { 192.168.1.11 } virtual_ipaddress { 192.168.1.100/24 dev eth0 label eth0:vip1 } track_script { check_nginx } notify_master /etc/keepalived/notify.sh MASTER notify_backup /etc/keepalived/notify.sh BACKUP notify_fault /etc/keepalived/notify.sh FAULT } vrrp_instance VPC_B { state MASTER interface eth0 virtual_router_id 102 priority 150 advert_int 1 nopreempt auth_type PASS auth_pass vrrp_secure_102 unicast_src_ip 192.168.1.12 unicast_peer { 192.168.1.11 } virtual_ipaddress { 192.168.1.101/24 dev eth0 label eth0:vip2 } track_script { check_nginx } notify_master /etc/keepalived/notify.sh MASTER notify_backup /etc/keepalived/notify.sh BACKUP notify_fault /etc/keepalived/notify.sh FAULT }这里有个很容易踩的坑服务器2在实例A里的state虽然是BACKUP但virtual_ipaddress里还是把VIP1写上了。很多人不理解认为BACKUP节点不该配置VIP地址。实际上keepalived处于BACKUP状态时不会主动绑定VIP但当它因为对端down而升为MASTER时它会根据配置把VIP绑上去。所以每个实例的virtual_ipaddress必须写完整两边保持一致。只在对端配置VIP是错误做法升主时会导致VIP绑定失败。3.4 通知脚本与启动验证notify脚本用于在状态切换时执行一些自动化动作比如发告警、更新路由、写日志。我常用的一个简洁版本#!/bin/bash STATE$1 LOGFILE/var/log/keepalived_notify.log echo $(date %Y-%m-%d %H:%M:%S) state${STATE} host$(hostname) $LOGFILE如果你有Zabbix或者钉钉机器人在脚本里加上curl通知即可。这个脚本一定要给执行权限并且测试时先手动跑一遍确认没有权限问题chmod x /etc/keepalived/notify.sh /etc/keepalived/check_nginx.sh配置写完后先用语法检查确认解析没问题keepalived -t -f /etc/keepalived/keepalived.conf接着启动服务systemctl restart keepalived在两台机器上分别执行ip addr show eth0正常情况下应该看到服务器1eth0:vip1 192.168.1.100没有eth0:vip2服务器2eth0:vip2 192.168.1.101没有eth0:vip1如果出现两边同时持有同一个VIP说明配置有问题马上去查日志。4. 故障切换实测与常见问题4.1 故障模拟的完整流程架构搭好之后不能等到线上真的出事才验证。我每次上线新架构都会在维护窗口里做一轮完整的故障演练而且只做一次还不够还要反复做。以下是我推荐的故障模拟顺序第一轮模拟服务进程崩溃。在两台服务器上分别杀掉Nginx进程pkill -9 nginx观察10到20秒内该节点的keepalived是否触发了健康检查失败并主动降低优先级VIP是否发生了漂移。正常情况下在拥有VIP1的服务器1上杀掉NginxVIP1应该在十几秒内漂移到服务器2而VIP2不会动。注意由于我在服务器1的实例A里配置了nopreempt所以即使服务器1的Nginx恢复VIP1也不会自动回来需要手动干预或等待通知。第二轮模拟整机宕机。直接执行poweroff或者云控制台强制关机观察对端是否快速接管VIP。因为这时候VRRP协议层面就已经失联切机速度通常比健康检查还要快一般在1到3秒内。第三轮模拟网络分区。这个在物理环境里可以直接拔网线云环境里可以临时关闭网卡ip link set eth0 down网络分区是脑裂的高发场景务必观察两台机器是否同时绑定了同一个VIP。我见过不少配置在正常情况下好好的一断网就出事原因就是对端VRRP报文收不到但本地服务还健康于是本端也升级成MASTER两边同时持有VIP流量来回摇摆。双主架构因为有两个VIP四个实例脑裂的影响比单VIP更严重所以建议把单播源地址和对端地址写清楚同时在云安全组里只放行两个节点之间的VRRP通信不要放行到整个网段。4.2 日志与状态排查技巧排查keepalived问题第一入口永远是日志。CentOS上通常在/var/log/messagesUbuntu上用journalctljournalctl -u keepalived -f日志中出现的关键词含义Sending gratuitous ARP on eth0 for 192.168.1.100实例正常进入MASTER状态正在广播ARP刷新交换机表项。Entering BACKUP STATE本端降级为备份。VRRP_Instance(VPC_A) received higher prio advert收到对端更高优先级的通告说明对端想抢主。VRRP_Instance(VPC_A) ignoring received advert...收到无效通告原因可能是auth不匹配、virtual_router_id不一致等。另外一个排查命令是ip addr show ip route确保VIP的label显示正常。如果eth0:vip1出现了但服务没监听那要检查Nginx是否绑定到了该VIP上。Nginx配置里server块如果用listen 80默认监听所有地址不会出问题如果指定了listen 192.168.1.100:80那么VIP刚绑上的时候Nginx可能已经在运行但监听地址没变此时需要reload。4.3 常见配置陷阱与避坑清单auth_pass长度不一致或者太短。VRRP鉴权密码最长8个字符写多了不会报错但实际只截取前8位两边不一致会导致收不到合法的VRRP报文。virtual_router_id在同一个二层网络里和其他VRRP实例冲突。只要两台交换机和防火墙之间还有其他keepalived集群router id必须全局唯一否则会收到对方集群的报文AR P风暴甚至VIP抢占。健康检查脚本用到了外部命令但没有加绝对路径。keepalived调用脚本时环境变量比手动执行时要精简得多pgrep、curl这些命令在PATH里找不到时脚本会静默失败建议所有命令写绝对路径比如/usr/bin/curl。weight设置过大导致优先级变负数。VRRP协议里优先级范围是0到255运维上建议脚本失败后优先级降到比Backup低20左右即可别直接用-150这种值计算后如果变成负数keepalived行为会非常诡异。云服务器开启源/目的检查。AWS、OpenStack默认会检查流量是否以该实例为源或目的VIP漂移后物理网卡收到目标为VIP的请求可能直接被丢弃。云平台需要在弹性网卡上关闭源/目的检查这个不处理keepalived死活切不过去。下面这个表是我平时排查时直接对照的速查表现象可能原因排查方向两台机器都持有VIP网络分区、防火墙拦截VRRP检查对端连通性查看VRRP报文是否正常到达VIP没有按预期漂移健康检查脚本失败条件不满足手动执行脚本看返回值查看weight配置日志出现auth failureauth_pass不一致逐字符核对两处配置日志出现invalid router id与其他VRRP实例冲突排查二层网络内所有keepalived实例VIP绑定成功但业务不通云平台源/目的检查关闭网卡源/目的检查nopreempt不生效nopreempt写在了BACKUP侧确认nopreempt只写在MASTER侧5. 从双主到更复杂的演进双主热备配置稳定跑起来之后下一步可以考虑业务层面的流量分配。比如VIP1过来的是写操作较多的接口VIP2过来的是读多写少的接口两台服务器的配置可以根据业务特点做差异化调整不需要像主备那样必须保持同规格。另一个演进方向是加第三台机器。keepalived支持在一个网段内多个节点同时运行双主模式下加一台观察节点可以让它在两台主节点都异常时接管所有VIP形成21的仲裁机制。但这里要提醒一句仲裁节点引入后需要考虑投票机制否则仲裁节点本身故障时又会出现新的复杂局面。没有硬性需求之前双主两台机器已经覆盖了绝大多数场景。再往后就是把keepalived的VIP抽象成对外统一入口的一部分配合DNS轮询或者云负载均衡器使用。例如在云上VIP1绑定到云负载均衡的后端云负载均衡器再把流量分发给一批业务实例keepalived退化为接入层的冗余保障。这种层次化设计已经超出了双主本身但它说明了一个事实keepalived双主不是终点而是一个让你在两台机器规模下把可用性提升一大截的实用手段。从我个人实施经验来说双主的坑往往不在配置本身而在对切换条件理解不透彻。一定要把健康检查、优先级、抢占策略、通知脚本这四样东西当成一个整体来设计而不是单独看某一个参数。配置完以后花半小时做一次完整的故障演练比你上线后提心吊胆地盯一周监控都管用。这套双主热备架构我目前维护了两年多除了一次机房物理交换机故障导致两台机器同时上不了外其它几次节点异常都靠它悄无声息地完成了切换。
返回列表