Nginx 高可用架构实践:Keepalived 实现故障自动切换,Keepalived主备模式(双机热备),Keepalived互备模式(互为主备,互持 VIP)
Nginx 高可用架构实践:Keepalived 实现故障自动切换,Keepalived主备模式(双机热备),Keepalived互备模式(互为主备,互持 VIP)
📖 为什么需要高可用架构?
在实际生产中,如果只有一台 Nginx 服务器,那么当它宕机(比如断电、系统崩溃、Nginx 服务停止)时,所有用户的请求都会失败,整个网站或系统将无法访问。
为了解决这个问题,我们需要部署多个 Nginx 实例,并通过高可用机制实现自动切换,这样即使一台服务器出问题,另一台也能接管工作,用户访问不会受到影响。
🏗️ 高可用部署方案详解
KeepAlived简介:
KeepAlived是一款基于VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议)的开源软件,主要用于解决网络服务的单点故障问题,特别是在集群环境中提供VIP(Virtual IP,虚拟IP地址)共享和故障切换功能
1️⃣ 主备模式(双机热备)
Nginx + keepalived 双机主从模式(也叫双机热备):
即前端使用两台服务器,一台主服务器和一台热备服务器,正常情况下,主服务器绑定一个虚拟IP,提供负载均衡服务,热备服务器处于空闲状态;当主服务器发生故障时,热备服务器接管主服务器的虚拟IP,提供负载均衡服务;但是热备服务器在主机器不出现故障的时候,永远处于浪费状态,对于服务器不多的网站,该方案不经济实惠。
- 虚拟IP数量:仅需要 1 个 VIP(例如:192.168.0.100)
- 流量特点:所有请求始终流向主节点,一旦主节点宕机,备节点立即接管 VIP,继续提供服务
- 使用场景:中小型系统,访问量不大,请求不均衡问题不明显
🧠 工作机制
- Master 节点绑定 VIP
192.168.0.100
,对外提供服务 - Backup 节点处于监控状态,不处理请求
- 一旦 Master 节点检测失败(例如 Nginx 挂掉),Keepalived 自动将 VIP 漂移到 Backup
- 用户无感知切换,请求继续正常处理
- Master 恢复后,根据是否开启抢占模式,VIP 可以自动漂移回来
2️⃣ 互备模式(互为主备,互持 VIP)
Nginx + keepalived 双机主主模式(也叫双机互备):
即前端使用两台负载均衡服务器,互为主备,且都处于活动状态,同时各自绑定一个虚拟IP,提供负载均衡服务;当其中一台发生故障时,另一台接管发生故障服务器的虚拟IP(这时由非故障机器一台负担所有的请求)
- 虚拟IP数量:需要 2 个 VIP(例如:192.168.0.100 和 192.168.0.101)
- 流量特点:两个节点同时对外提供服务,用户请求可通过 DNS 轮询或负载均衡分发至任一 VIP
- 使用场景:大中型系统,请求量大,需要并发负载分摊
🧠 工作机制
- A 节点绑定 VIP1(192.168.0.100),为 VIP2 的备份
- B 节点绑定 VIP2(192.168.0.101),为 VIP1 的备份
- 正常时两台机器同时提供服务
- 若任一节点宕机,另一节点自动绑定两个 VIP,完全接管服务
- 故障节点恢复后,可以重新分担流量
🧰 什么是 Keepalived?
Keepalived 是一种实现高可用的开源工具。它的核心原理是基于 VRRP 协议(虚拟路由冗余协议)。
它的作用主要是:
- 让多台服务器共享一个或多个虚拟 IP(VIP)
- 实时监控服务(如 Nginx)状态
- 一旦发现主节点异常,自动切换 VIP 到备节点
注意: Keepalived 一般用于实现主备(Master-Backup)高可用架构,这是它设计和最常见的使用场景。仅用 Keepalived 做主主,适合简单场景或实验,生产环境建议结合 LVS、DNS 轮询、或反向代理等方案。
🚦 Keepalived 配置属性说明
下面分 父块 → 子属性 两级结构给出:
global_defs
(全局定义)vrrp_script
(健康检查脚本)vrrp_instance·主备模式
(Master‑Backup)vrrp_instance·主主模式
(Active‑Active,模拟双主)
1. global_defs
(全局定义)
子属性 | 取值范围 / 格式 | 示例 | 作用说明 |
---|---|---|---|
notification_email | 多行邮箱地址,任意合法 e‑mail | admin@163.com | 告警接收人列表 |
notification_email_from | 单行邮箱地址 | keepalived@163.com | 告警邮件发件人 |
smtp_server | IP / FQDN | 192.168.10.200 | SMTP 服务器地址 |
smtp_connect_timeout | 整数 1‑300 秒 | 30 | 连接 SMTP 超时 |
router_id | 任意字符串 | LVS_NODE_A | 当前 Keepalived 实例标识符 主备模式: router_id 推荐相同(可不同), router_id 只是标识当前节点自身,用于日志显示或调试,不影响 VRRP 通讯。主备之间通过 virtual_router_id + interface + VIP 来标识一个虚拟路由组,router_id 只是逻辑名称,不会影响主备切换主主模式: router_id 必须唯一,在主主(即多个实例都为 MASTER )场景下,为了方便区分节点身份、排查问题,建议配置成唯一的名字,避免日志混淆。仍然不会影响 VRRP 协议工作逻辑。 |
vrrp_garp_interval | 整数(秒) | 3 | GARP 报文间隔(更新 ARP 映射表) |
vrrp_gna_interval | 整数(秒) | 3 | GNA 通知间隔(VRRP 通告包) |
2. vrrp_script
(健康检查脚本)
子属性 | 取值范围 / 格式 | 示例 | 作用说明 |
---|---|---|---|
script | 可执行文件绝对路径 | /etc/keepalived/nginx_check.sh | 健康检测脚本路径 |
interval | 整数 1‑60 秒 | 2 | 脚本执行间隔 |
weight | ‑100 ~ +100(可正可负) | 20 | 成功时动态调整优先级(正值↑,负值↓) 假设原始 priority 为 100: 脚本运行正常 → 当前优先级:100 + 20; 脚本异常 → 优先级变为 80,将触发切换 |
3. vrrp_instance
(主备模式 Master‑Backup)
子属性 | 取值范围 / 格式 | 示例 | 作用说明 |
---|---|---|---|
state | MASTER | BACKUP | MASTER | 指定节点初始角色 |
interface | 网卡名(通过 ip a 查看) | eth0 | 发送 VRRP 广播或单播的接口 |
virtual_router_id | 整数 1‑255 | 51 | VRRP 路由实例标识,主备需一致 |
priority | 整数 1‑255 | 主:100 ,备:90 | 节点优先级,越高越可能成为主 |
advert_int | 整数 1‑255(秒) | 1 | 广播同步间隔,MASTER与BACKUP之间同步检查的时间间隔 |
authentication →auth_type | PASS | AH | PASS | 认证方式 |
authentication →auth_pass | 最多 8 字符 | 1111 | 主备需一致的密码 |
virtual_ipaddress | 每行一个 IP(IPv4/IPv6) | 192.168.10.50 | 虚拟 IP(VIP) |
track_script | vrrp_script 名称 | chk_http_port | 引用上方定义的健康检查脚本 |
nopreempt | 无值(直接配置) | nopreempt | 非抢占模式:主节点恢复后不会抢回 VIP |
unicast_src_ip | 本机真实 IP 地址 | 10.218.0.13 | 单播场景下指定本节点 IP |
unicast_peer | 对端节点 IP(可多行) | 10.218.0.14 | 单播发送目标 IP |
garp_master_delay | 整数(秒) | 1 | 节点抢占主后等待多少秒发送 GARP 报文 |
garp_master_refresh | 整数(秒) | 5 | 主节点周期性发送 GARP 报文的时间间隔 |
track_interface | 网卡名 | eth0 | 检查网卡是否可用,网卡不可用时降低优先级 |
4. vrrp_instance
(主主模式 Active‑Active 模拟)
子属性 | 取值范围 / 格式 | 示例 | 与主备区别 / 说明 |
---|---|---|---|
state | 所有节点统一设置为 MASTER | MASTER | 所有节点都初始化为主,用于模拟主主高可用 |
priority | 建议所有节点设置相同优先级(如 100) | 100 | 若无健康检测等动态调整,则由 IP 大小决定主 |
nopreempt | 建议开启 | nopreempt | 非抢占:避免主频繁变化 |
virtual_ipaddress | 单 VIP 共持 / 多 VIP 分配 | A 节点:.50 ,B 节点:.51 | 多 VIP 更易实现流量分摊与容灾切换 |
其余属性 | 同主备模式 | — | 包括 advert_int 、authentication 、track_script 等通用参数均可复用 |
⚠️ 主主模式仅为模拟双主,不是 VRRP 标准特性。
多节点共享 VIP 可能导致 ARP 冲突,通常需搭配:
- LVS/HAProxy 反向代理实现负载分担
- 多 VIP 配合 DNS/SLB 实现服务分片
- 网络设备(如交换机)允许多 MAC 绑定同 IP
⚙️ 主从模式配置示例
假设VIP为 192.168.0.100
Master 节点(192.168.0.11)
! Configuration File for keepalived
global_defs {
router_id ka1
vrrp_garp_interval 3
vrrp_gna_interval 3
}
# 检查脚本
vrrp_script check1{
script "/etc/keepalived/check_health.sh" # 脚本路径
interval 2 #(检测脚本执行的间隔)
weight 2
}
vrrp_instance VI_1 {
# 主MASTER,备BACKUP
state MASTER
# 实例绑定的网卡
interface eth0
# 主备节点要保持一致
virtual_router_id 11
# 设置非抢占模式
nopreempt
# 主优先级高,备优先级低
priority 100
# MASTER与BACKUP之间同步检查的时间间隔,单位是秒
advert_int 5
authentication {
auth_type PASS
auth_pass 1111
}
# 出于安全、网络隔离等原因禁止多播时,可以使用单播模式
# 配置成本机实际IP
unicast_src_ip 192.168.0.11
# 对端设备的IP地址
unicast_peer {
192.168.0.12
}
virtual_ipaddress {
# 虚拟ip地址
192.168.0.100
}
# 设置当切为主状态后多久更新 ARP 缓存
garp_master_delay 1
# 设置主节点发送 ARP 报文的时间间隔
garp_master_refresh 5
# 使用绑定VIP的网卡
track_interface {
eth0
}
# 调用上边的脚本
track_script {
check1
}
}
Backup 节点(192.168.0.12)
! Configuration File for keepalived
global_defs {
router_id ka1
vrrp_garp_interval 3
vrrp_gna_interval 3
}
# 检查脚本
vrrp_script check1{
script "/etc/keepalived/check_health.sh" # 脚本路径
interval 2 #(检测脚本执行的间隔)
weight 2
}
vrrp_instance VI_1 {
# 主MASTER,备BACKUP
state BACKUP
# 实例绑定的网卡
interface eth0
# 主备节点要保持一致
virtual_router_id 11
# 设置非抢占模式
nopreempt
# 主优先级高,备优先级低
priority 90
# MASTER与BACKUP之间同步检查的时间间隔,单位是秒
advert_int 5
authentication {
auth_type PASS
auth_pass 1111
}
# 出于安全、网络隔离等原因禁止多播时,可以使用单播模式
# 配置成本机实际IP
unicast_src_ip 192.168.0.12
# 对端设备的IP地址
unicast_peer {
192.168.0.11
}
virtual_ipaddress {
# 虚拟ip地址
192.168.0.100
}
# 设置当切为主状态后多久更新 ARP 缓存
garp_master_delay 1
# 设置主节点发送 ARP 报文的时间间隔
garp_master_refresh 5
# 使用绑定VIP的网卡
track_interface {
eth0
}
# 调用上边的脚本
track_script {
check1
}
}
⚙️ 主主模式配置示例
假设VIP为 192.168.0.100
,192.168.0.101
节点 A 配置
global_defs {
notification_email {
admin@163.loc
dev@163.loc
}
notification_email_from Keepalived failure notification
smtp_server 192.168.10.200
smtp_connect_timeout 30
router_id NODE_A
}
vrrp_script chk_http_port {
script "/etc/keepalived/check_health.sh" # 脚本路径
interval 2 #(检测脚本执行的间隔)
weight 2
}
# 节点A的VI_1为主节点
vrrp_instance VI_1 {
state MASTER
interface eth0 #实例绑定的网卡, 用ip a命令查看网卡编号
virtual_router_id 11 # 虚拟路由标识,主、备服务器ID必须一样
priority 100 # 优先级,备份服务上将100改为小于100,可配置成90
advert_int 1 # 主备之间同步检查的时间间隔单位秒
authentication { # 验证类型和密码
auth_type PASS # 验证类型有两种 PASS和HA
auth_pass 1111 # 验证密码,在一个实例中主备密码保持一样
}
virtual_ipaddress {
192.168.0.100
}
track_script { # 调用上边的脚本
check1
}
}
# 节点A的VI_2为备节点
vrrp_instance VI_2 {
state BACKUP
interface eth0 #实例绑定的网卡, 用ip a命令查看网卡编号
virtual_router_id 12 # 虚拟路由标识,主、备服务器ID必须一样
priority 90 # 优先级,备份服务上将100改为小于100,可配置成90
advert_int 1 # 主备之间同步检查的时间间隔单位秒
authentication { # 验证类型和密码
auth_type PASS # 验证类型有两种 PASS和HA
auth_pass 1111 # 验证密码,在一个实例中主备密码保持一样
}
virtual_ipaddress {
192.168.0.101
}
track_script { # 调用上边的脚本
check1
}
}
节点 B 配置
global_defs {
notification_email {
admin@163.loc
dev@163.loc
}
notification_email_from Keepalived failure notification
smtp_server 192.168.10.200
smtp_connect_timeout 30
router_id NODE_B
}
vrrp_script chk_http_port {
script "/etc/keepalived/check_health.sh" # 脚本路径
interval 2 #(检测脚本执行的间隔)
weight 2
}
# 节点B的VI_1为备节点
vrrp_instance VI_1 {
state BACKUP
interface eth0 #实例绑定的网卡, 用ip a命令查看网卡编号
virtual_router_id 11 # 虚拟路由标识,主、备服务器ID必须一样
priority 90 # 优先级,备份服务上将100改为小于100,可配置成90
advert_int 1 # 主备之间同步检查的时间间隔单位秒
authentication { # 验证类型和密码
auth_type PASS # 验证类型有两种 PASS和HA
auth_pass 1111 # 验证密码,在一个实例中主备密码保持一样
}
virtual_ipaddress {
192.168.0.100
}
track_script { # 调用上边的脚本
check1
}
}
# 节点B的VI_2为主节点
vrrp_instance VI_2 {
state MASTER
interface eth0 #实例绑定的网卡, 用ip a命令查看网卡编号
virtual_router_id 12 # 虚拟路由标识,主、备服务器ID必须一样
priority 100 # 优先级,备份服务上将100改为小于100,可配置成90
advert_int 1 # 主备之间同步检查的时间间隔单位秒
authentication { # 验证类型和密码
auth_type PASS # 验证类型有两种 PASS和HA
auth_pass 1111 # 验证密码,在一个实例中主备密码保持一样
}
virtual_ipaddress {
192.168.0.101
}
track_script { # 调用上边的脚本
check1
}
}
一句话总结
- 节点 A:
VI_1
为 主(priority 高),VI_2
为 备(priority 低)- 节点 B:
VI_1
为 备,VI_2
为 主
🔍 监控脚本示例: check_health.sh
脚本功能说明
- 使用 curl 访问本地端口(如 80、8080)
- 若访问成功(HTTP 状态码 200),则返回 0(健康)
- 否则返回非 0(不健康,会降低权重或触发 VIP 迁移)
#!/bin/bash
# 要检查的服务地址(可以换成 127.0.0.1、localhost、实际 IP 等)
URL="http://127.0.0.1:80"
# 使用 curl 请求,最多超时 3 秒,只取状态码
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "$URL")
# 如果状态码为 200,表示正常
if [ "$HTTP_CODE" -eq 200 ]; then
exit 0
else
echo "Health check failed, HTTP status: $HTTP_CODE"
exit 1
fi
脚本权限
chmod +x /etc/keepalived/check_health.sh
🛠️ 启动与验证
sudo systemctl start keepalived
sudo systemctl enable keepalived
ip addr | grep 192.168.0.100
- 停掉 master 的 nginx 服务 → VIP 漂移到 backup
- 恢复服务 → VIP 可根据配置漂移回来
✅ 总结对比
模式 | VIP 数量 | 可用性 | 负载分担 | 复杂度 | 推荐场景 |
---|---|---|---|---|---|
主从 | 1 个 | ✅ 高 | ❌ 无 | ✅ 简单 | 小型系统 |
主主 | 2 个 | ✅ 更高 | ✅ 支持 | ⚠️ 较复杂 | 大并发系统 |
📚 延伸阅读
感谢阅读,如有帮助欢迎点赞与转发!