Keepalived 原理详解:从 VRRP 选主到 VIP 故障切换
两台服务器都运行着相同的业务服务,并不等于入口已经高可用。
假设客户端把请求固定发给 node-1。即使 node-2 完全正常,只要 node-1 宕机,客户端仍然找不到 node-2。问题不在于“有没有备用服务器”,而在于入口地址仍然属于某一台具体机器。
Keepalived 解决的就是这个入口问题:它让多个节点共同维护一个虚拟 IP,并根据节点状态决定当前由谁持有这个 IP。客户端只记住虚拟地址,不需要参与主备判断。
本文不讨论软件安装和系统命令,而是围绕以下问题建立完整的理解:
- Keepalived、VRRP 和 VIP 分别负责什么?
- MASTER 是写在配置里的,还是选举出来的?
- Nginx 故障为什么能够影响 VIP 的归属?
priority和weight如何共同决定有效优先级?- 服务故障、Keepalived 故障和主机故障有什么区别?
- 节点恢复后,VIP 为什么有时会切回来,有时不会?
- 什么是脑裂,Keepalived 又有哪些能力边界?
🧭 Keepalived 解决的核心问题
先看一个典型的双节点入口:
图中有三个不同层次的地址:
| 地址 | 含义 |
|---|---|
| node-1 的真实 IP | 唯一标识 node-1,本身不会漂移 |
| node-2 的真实 IP | 唯一标识 node-2,本身不会漂移 |
| VIP | 面向客户端的服务入口,可以在两个节点之间移动 |
正常情况下,node-1 是 MASTER,VIP 绑定在 node-1 上。node-2 是 BACKUP,它不会绑定 VIP,但会持续观察 MASTER 是否正常。
当 node-1 不再适合提供服务时,node-2 接管 VIP。客户端仍然访问原来的地址,变化只发生在入口地址背后。
这里要特别注意:Keepalived 不是 Nginx 的替代品,也不是请求代理。Nginx 负责处理 HTTP 请求,Keepalived 只负责回答一个问题:现在应该由哪台机器拥有 VIP?
🧩 Keepalived、VRRP 与 VIP
这三个概念经常一起出现,但它们并不是同一层面的东西。
Keepalived
Keepalived 是运行在 Linux 节点上的高可用软件。它负责运行 VRRP 实例、检查本机状态、计算优先级,并在角色变化时添加或移除 VIP。
可以把它理解成每台节点上的“本地决策者”。它既观察本机,也接收其他节点发来的状态信息。
VRRP
VRRP(Virtual Router Redundancy Protocol)是节点之间协调主备关系的协议。多个节点通过相同的 virtual_router_id 组成一个虚拟路由器,其中一个节点成为 MASTER,其余节点处于 BACKUP。
MASTER 周期性发送通告,表达“我仍然在线,而且我的优先级是这么多”。BACKUP 收到通告后保持等待;如果更高优先级节点出现,或者 MASTER 的通告消失,角色就可能改变。
VRRP 使用的是 IP 协议号 112,不是某个 TCP 或 UDP 端口。组播和单播只是报文发送方式不同,传输的仍然是 VRRP 通告。
VIP
VIP(Virtual IP)是虚拟路由器对外提供的地址。它不永久属于某台服务器,而属于“当前 MASTER”这个角色。
因此,VIP 漂移的本质不是修改客户端配置,而是改变这个地址当前绑定在哪台机器的网卡上。新 MASTER 接管后,通常还会通过 Gratuitous ARP 等机制通知周围网络设备更新地址映射。
👑 MASTER 与 BACKUP 如何协作
可以把双节点关系理解成一个持续运行的状态机:
- 两个节点启动并加入同一个 VRRP 实例。
- 节点比较优先级,选出 MASTER。
- MASTER 绑定 VIP,并周期性发送 VRRP 通告。
- BACKUP 不绑定 VIP,只监听 MASTER 的通告。
- 当选举条件变化时,BACKUP 可能升级为 MASTER。
- 原 MASTER 失去资格后移除 VIP,新的 MASTER 绑定 VIP。
配置中的 state MASTER 或 state BACKUP 主要表达初始状态倾向,并不是永久身份。最终角色由 VRRP 通告、优先级、跟踪项以及抢占策略共同决定。
换句话说,MASTER 是运行时角色,不是服务器的固定名称。今天的 node-1 可以是 MASTER,故障后 node-2 也可以成为 MASTER。
为什么 BACKUP 不立即接管
网络偶尔丢失一条报文并不意味着 MASTER 已经故障。如果 BACKUP 因为一次通告没收到就接管 VIP,系统会非常容易抖动。
因此,BACKUP 会等待一个 MASTER Down 时间窗口。只有在这个窗口内持续收不到有效通告,才认为原 MASTER 已经不可用。advert_int 越短,故障发现通常越快,但报文频率也越高;实际业务恢复时间还会受到健康检查、系统调度、地址通告和客户端重试影响。
⚖️ VRRP 如何选出 MASTER
选举的核心规则很直观:有效优先级更高的节点更有资格成为 MASTER。
常见配置可以压缩成下面这个最小模型:
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 120
advert_int 1
virtual_ipaddress {
192.168.10.100/24
}
}
这里最值得理解的不是语法,而是参数之间的关系:
| 参数 | 逻辑含义 |
|---|---|
virtual_router_id | 哪些节点属于同一场选举 |
priority | 本节点的基础竞争力 |
advert_int | MASTER 多久声明一次自己仍然可用 |
virtual_ipaddress | 获胜节点需要持有的入口地址 |
interface | VIP 和 VRRP 实例关联的网络接口 |
同一个 VRRP 实例中的节点必须使用相同的 VRID,但应该使用不同的优先级。假设 node-1 的基础优先级为 120,node-2 为 100,两者状态都正常时,node-1 成为 MASTER。
如果优先级相同,协议还需要使用其他规则打破平局。学习和设计时不应依赖平局结果,而应主动为节点设置清晰、不同的优先级。
🧮 基础优先级与有效优先级
只看 priority 还不够,因为 Keepalived 可以根据本机状态动态调整节点的竞争力。
- 基础优先级:配置中的
priority。 - 有效优先级:基础优先级叠加健康检查、接口状态或其他跟踪项的权重之后,用于实际比较的值。
以本文的双节点为例:
node-1 基础优先级:120
node-2 基础优先级:100
node-1 的 Nginx 检查失败权重:-30
正常时,120 > 100,node-1 持有 VIP。node-1 的 Nginx 故障后:
node-1 有效优先级:120 - 30 = 90
node-2 有效优先级:100
此时 90 < 100,node-2 更有资格成为 MASTER。
这也解释了一个常见误区:健康检查失败,不一定会导致 VIP 漂移。如果 node-1 的权重只是 -10,它的有效优先级仍然是 110,依然高于 node-2 的 100。
所以设计权重时,真正的问题不是“要扣多少分”,而是:
故障发生后,剩余的有效优先级是否足以改变选举结果?
🩺 Keepalived 如何感知业务故障
VRRP 通告只能证明 Keepalived 和网络仍在工作,不能证明 Nginx、数据库或业务接口一定健康。
例如,node-1 的操作系统和 Keepalived 都正常,但 Nginx 已经停止。此时 node-1 仍然能够发送 VRRP 通告。如果没有业务健康检查,它会继续持有 VIP,客户端却只能访问到一个失效入口。
Keepalived 通过 vrrp_script 定义检查,再通过 track_script 把检查结果关联到 VRRP 实例:
vrrp_script chk_nginx {
script "/etc/keepalived/check-nginx.sh"
interval 2
timeout 1
weight -30
fall 2
rise 2
}
vrrp_instance VI_1 {
priority 120
track_script {
chk_nginx
}
}
这段配置表达的是一条决策链:
运行检查脚本
↓
根据退出码判断健康状态
↓
连续失败达到 fall 次
↓
应用 weight,修改有效优先级
↓
重新比较节点资格
↓
必要时触发 MASTER 切换
interval、timeout、fall 与 rise
| 参数 | 含义 | 主要影响 |
|---|---|---|
interval 2 | 每 2 秒检查一次 | 检查频率 |
timeout 1 | 单次检查最多等待 1 秒 | 防止脚本卡住 |
fall 2 | 连续失败 2 次才确认故障 | 抗瞬时抖动能力 |
rise 2 | 连续成功 2 次才确认恢复 | 防止刚恢复就立即回切 |
阈值越敏感,发现故障越快,但误判风险越高;阈值越保守,系统越稳定,但接管会更慢。它们本质上是在“恢复速度”和“抗抖动能力”之间做取舍。
负权重与 FAULT
负权重表示检查失败后降低有效优先级,让节点继续参与比较。这适合把多个因素综合起来决定角色。
某些跟踪方式也可以在失败时让实例直接进入 FAULT。这表示当前节点不再具备持有 VIP 的基本条件,而不是简单地少一些分数。
可以这样区分:
- 负权重:这个节点变差了,但仍可以参与选举。
FAULT:这个节点当前没有资格成为 MASTER。
🔄 一次完整的故障切换
下面把 Nginx 故障后的变化连成一条完整时间线:
- 正常阶段:node-1 的有效优先级为
120,作为 MASTER 持有 VIP;node-2 以100处于 BACKUP。 - 检测阶段:node-1 的 Nginx 开始失败,但 Keepalived 不会因为单次失败立即切换。
- 确认阶段:失败次数达到
fall,检查结果正式变为不健康。 - 降权阶段:node-1 应用
weight -30,有效优先级降到90。 - 重新选主:node-2 的
100高于 node-1 的90,node-2 成为新的 MASTER。 - VIP 转移:node-1 移除 VIP,node-2 绑定 VIP,并向网络声明新的地址归属。
- 客户端恢复:客户端仍然访问同一个 VIP,但请求已经由 node-2 处理。
“故障切换时间”不是一个单独参数。它至少由以下几部分组成:
故障确认时间
+ VRRP 状态收敛时间
+ VIP 添加与网络邻居更新
+ 客户端超时和重试
= 用户感知到的恢复时间
因此,即使 VIP 很快完成漂移,原有 TCP 连接也可能中断。Keepalived 解决的是入口重新可达,并不会把已经建立的连接状态一起迁移到新节点。
🧯 三类故障有什么不同
理解故障类型,比记住停止服务的命令更重要。
业务服务故障
例如 Nginx 停止响应,但操作系统、网络和 Keepalived 仍然正常。
这类故障必须依赖业务健康检查发现。切换的直接原因通常是有效优先级下降,或者 VRRP 实例进入 FAULT。
Nginx 故障 → 检查失败 → 有效优先级变化 → VIP 切换
Keepalived 进程故障
业务服务可能仍然正常,但 Keepalived 已经停止,无法继续维护 VIP 或发送 VRRP 通告。
BACKUP 会因为失去 MASTER 通告而接管。这里不需要依赖 Nginx 检查,核心信号是 VRRP 心跳消失。
Keepalived 故障 → VRRP 通告消失 → BACKUP 超时 → VIP 切换
主机或网络故障
服务器断电、内核崩溃、网卡离线或链路中断时,原 MASTER 无法执行优雅清理,也无法主动告诉其他节点自己已经故障。
BACKUP 仍然只能通过“持续收不到通告”推断故障。这类切换更能反映真实高可用能力,因为它不依赖原节点配合。
主机或链路故障 → 通告不可达 → BACKUP 超时 → VIP 切换
三类故障最终都可能表现为 VIP 漂移,但触发信号不同:
| 故障类型 | Keepalived 是否还在运行 | 主要判断依据 |
|---|---|---|
| 业务服务故障 | 是 | 本地健康检查 |
| Keepalived 进程故障 | 否 | VRRP 通告消失 |
| 主机或网络故障 | 否或不可达 | VRRP 通告持续不可达 |
🔁 抢占、延迟抢占与非抢占
故障节点恢复后,还需要回答一个问题:VIP 要不要切回原来的高优先级节点?
默认抢占
高优先级节点恢复后重新成为 MASTER。优点是拓扑会回到预设状态;缺点是恢复过程又产生一次切换,可能再次影响连接。
延迟抢占
通过 preempt_delay 给恢复节点预留观察时间:
state BACKUP
preempt_delay 60
它不会延迟故障时的接管,而是延迟高优先级节点恢复后的回切。这样可以避免服务刚恢复就立刻承担入口流量。
非抢占
通过 nopreempt 让当前 MASTER 继续工作,即使原来的高优先级节点已经恢复:
state BACKUP
nopreempt
非抢占模式通常要求实例以 BACKUP 作为初始状态。它减少了不必要的来回切换,但意味着当前 MASTER 可能长期不是最初规划的主节点。
三种策略没有绝对优劣:
| 策略 | 恢复后的行为 | 适合关注点 |
|---|---|---|
| 默认抢占 | VIP 切回高优先级节点 | 固定主备关系 |
| 延迟抢占 | 等待一段时间后再切回 | 恢复稳定性 |
| 非抢占 | 保持当前 MASTER | 减少回切次数 |
⚠️ 脑裂、抖动与错误切换
脑裂
脑裂指多个节点都认为自己应该成为 MASTER,并同时持有同一个 VIP。
最常见的原因不是两台机器同时坏了,而是它们之间的 VRRP 通信被切断:
node-1 收不到 node-2
node-2 也收不到 node-1
两边都认为对方已经失效
两边都尝试成为 MASTER
这说明 Keepalived 的判断建立在“我能看到什么”之上,而不是一个拥有全局视角的中心协调器。网络隔离时,每个节点都可能基于局部信息做出合理但互相冲突的决定。
健康检查抖动
如果业务接口时好时坏,而 fall、rise 又设置得过小,节点的有效优先级会频繁变化,VIP 也可能在两边来回漂移。
解决思路不是无限增加切换速度,而是区分:
- 短暂超时是否应该被忽略?
- 连续多少次失败才代表真实故障?
- 恢复后需要稳定多久才重新接管?
- 是否真的需要自动回切?
权重无法改变结果
健康检查已经失败,VIP 却没有移动,往往不是脚本“没生效”,而是降权后的节点仍然拥有更高有效优先级。
排查这类问题时,应先写出每个节点的计算式,而不是先盯着配置语法:
基础优先级 + 所有跟踪项权重 = 有效优先级
🧱 Keepalived 能做什么,不能做什么
Keepalived 的能力边界非常清晰。
它能做什么
- 在多个节点之间选出一个 MASTER。
- 根据 VRRP 通告判断对端是否仍然可达。
- 根据脚本、接口和其他跟踪项调整节点资格。
- 在角色变化时添加或移除 VIP。
- 为客户端提供相对稳定的网络入口。
它不能做什么
- 不会同步 Nginx 配置、证书或静态文件。
- 不会复制数据库数据或解决数据一致性问题。
- 不会迁移已经建立的 TCP 连接和内存会话。
- 不会自动保证两个节点的业务能力完全相同。
- 无法单独消除所有网络分区和脑裂风险。
- 不能保证切换对客户端绝对无感。
所以,一套完整的高可用系统至少还需要考虑:
入口高可用:Keepalived / VIP
业务高可用:两个节点都能独立处理请求
数据高可用:复制、共识或共享存储
客户端韧性:超时、重试和连接重建
可观测性:状态、日志、告警和故障演练
Keepalived 只覆盖其中一部分,但这一部分非常关键:它把客户端与某台具体服务器解耦了。
🧠 建立自己的判断模型
以后看到一份 Keepalived 配置,可以按下面的顺序理解,而不是逐行背参数:
- 谁在参加同一场选举? 看
virtual_router_id、网络接口和对端关系。 - 正常时谁应该获胜? 比较各节点的基础优先级。
- 什么故障会改变资格? 找到
track_script、接口和其他跟踪项。 - 故障后分数是多少? 计算每个节点的有效优先级。
- MASTER 消失后多久接管? 看 VRRP 通告与故障确认逻辑。
- 故障节点恢复后是否回切? 看抢占、延迟抢占和非抢占策略。
- 如果节点互相看不见会怎样? 评估网络隔离和脑裂风险。
- VIP 切换后业务真的可用吗? 检查数据、会话和客户端重试能力。
只要这八个问题能回答清楚,配置中的大多数参数就不再是孤立的关键字,而是一套完整决策逻辑的组成部分。
✍️ 思考题
可以尝试不看前文回答下面的问题:
- node-1 的优先级是
120,node-2 是100。如果 node-1 的失败权重为-10,VIP 会切换吗?为什么? - 两台节点的 Nginx 都正常,但 MASTER 的 Keepalived 进程退出,BACKUP 依靠什么发现故障?
- 为什么
fall 1能更快切换,却不一定是更好的配置? - VIP 已经切换到 node-2,为什么客户端的原有连接仍可能断开?
- node-1 恢复后不希望 VIP 自动切回来,应该考虑哪种策略?
- 两个节点之间的 VRRP 报文被防火墙同时拦截,最危险的结果是什么?
- Keepalived 已经实现 VIP 高可用,为什么数据库仍然可能是单点?
✅ 总结
Keepalived 的核心逻辑可以浓缩为四句话:
- 多个节点通过 VRRP 组成一个虚拟路由器。
- 有效优先级决定当前由谁成为 MASTER 并持有 VIP。
- 健康检查把本机业务状态转换成优先级或资格变化。
- 抢占策略决定故障恢复后是否再次切换。
理解 Keepalived,不应该从复制两份配置开始,而应该先理解“节点看到什么、如何计算、何时改变角色”。当你能根据基础优先级、健康检查结果和抢占策略推导出 VIP 的归属时,就真正掌握了它的工作逻辑。