Nginx + Keepalived 双机 VIP 高可用架构设计、部署与配置指南

一、架构说明
该架构适用于:
TDengine 集群
MySQL 集群
Redis 集群
Web/API 服务集群
中间件集群
其他 HTTP/TCP 服务
假设规划如下:
Nginx-01:192.168.10.11
Nginx-02:192.168.10.12
VIP:192.168.10.10
后端节点:
192.168.10.21
192.168.10.22
192.168.10.23
网卡:ens33
整体架构:
客户端 / 应用系统
│
↓
VIP:192.168.10.10
│
┌─────────────┴─────────────┐
│ │
Nginx + Keepalived Nginx + Keepalived
Node01 Node02
192.168.10.11 192.168.10.12
MASTER BACKUP
│ │
└─────────────┬─────────────┘
│
Nginx
│
upstream
│
┌────────────┼────────────┐
│ │ │
Backend-01 Backend-02 Backend-03
192.168.10.21 192.168.10.22 192.168.10.23
二、高可用原理
Keepalived 主要负责:
VIP
MASTER / BACKUP
VRRP
优先级
VIP 漂移
健康检查脚本调用
Nginx 主要负责:
监听业务端口
反向代理
负载均衡
后端节点转发
健康检查脚本负责:
检测 nginx 是否正常
nginx 异常时尝试自动恢复
持续失败时向 Keepalived 返回失败状态
整体逻辑:
Keepalived
↓
每隔几秒调用 check_nginx.sh
↓
检查 Nginx
│
├── 正常
│ ↓
│ 保持当前状态
│
└── 异常
↓
尝试重启 Nginx
│
├── 成功
│ ↓
│ 继续服务
│
└── 持续失败
↓
健康检查失败
↓
当前节点降权
↓
备用节点接管 VIP
所以可以简单记:
Keepalived
负责 VIP 在哪台 Nginx
Nginx
负责请求转发到哪个后端
后端集群
负责真正处理请求
你本次两套环境也是这个模式:Keepalived 通过 vrrp_script 和 track_script 调用 Nginx 检查脚本,Nginx 主配置再通过 include 加载业务代理配置。 粘贴的文本 (1)(10) 粘贴的文本 (1)(10)
三、目录规划
推荐:
/app/nginx1.3/
├── conf/
│ ├── nginx.conf
│ └── http.d/
│ └── http-app.conf
├── logs/
├── html/
└── sbin/
└── nginx
Keepalived:
/etc/keepalived/
├── keepalived.conf
├── check_nginx.sh
└── nginx-attempts.txt
systemd:
/etc/systemd/system/nginx.service
四、Nginx 主配置文件
文件:
vi /app/nginx1.3/conf/nginx.conf
完整模板:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 8192;
multi_accept on;
}
http {
server_tokens off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
include mime.types;
default_type application/octet-stream;
keepalive_timeout 180s;
keepalive_requests 1000;
client_max_body_size 100M;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
proxy_connect_timeout 15s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent '
'$request_time $upstream_response_time '
'upstream_addr="$upstream_addr"';
include /app/nginx1.3/conf/http.d/*.conf;
}
修改完检查:
/app/nginx1.3/sbin/nginx -t
五、Nginx HTTP 业务代理配置文件
文件:
vi /app/nginx1.3/conf/http.d/http-app.conf
下面假设业务端口为:
6041
6043
6060
后端节点:
192.168.10.21
192.168.10.22
192.168.10.23
完整模板:
upstream service_6041 {
least_conn;
server 192.168.10.21:6041 max_fails=2 fail_timeout=10s;
server 192.168.10.22:6041 max_fails=2 fail_timeout=10s;
server 192.168.10.23:6041 max_fails=2 fail_timeout=10s;
}
upstream service_6043 {
ip_hash;
server 192.168.10.21:6043 max_fails=2 fail_timeout=10s;
server 192.168.10.22:6043 max_fails=2 fail_timeout=10s;
server 192.168.10.23:6043 max_fails=2 fail_timeout=10s;
}
upstream service_6060 {
ip_hash;
server 192.168.10.21:6060 max_fails=2 fail_timeout=10s;
server 192.168.10.22:6060 max_fails=2 fail_timeout=10s;
server 192.168.10.23:6060 max_fails=2 fail_timeout=10s;
}
server {
listen 6041;
server_name 192.168.10.10;
access_log /app/nginx1.3/logs/service-6041.access.log main;
error_log /app/nginx1.3/logs/service-6041.error.log;
location / {
proxy_pass http://service_6041;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_read_timeout 3600;
proxy_send_timeout 300;
}
}
server {
listen 6043;
server_name 192.168.10.10;
access_log /app/nginx1.3/logs/service-6043.access.log main;
error_log /app/nginx1.3/logs/service-6043.error.log;
location / {
proxy_pass http://service_6043;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_read_timeout 3600;
proxy_send_timeout 300;
}
}
server {
listen 6060;
server_name 192.168.10.10;
access_log /app/nginx1.3/logs/service-6060.access.log main;
error_log /app/nginx1.3/logs/service-6060.error.log;
location / {
proxy_pass http://service_6060;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_read_timeout 3600;
proxy_send_timeout 300;
}
}
这里必须重点检查:
listen 6041 → proxy_pass service_6041
listen 6043 → proxy_pass service_6043
listen 6060 → proxy_pass service_6060
你本次环境中曾出现过不同端口错误指向同一个 upstream 的问题,而 nginx -t 仍可能通过,所以语法检查之外必须做业务测试。 粘贴的文本 (1)(10)
修改后:
/app/nginx1.3/sbin/nginx -t
systemctl reload nginx
六、Nginx systemd 服务文件
文件:
vi /etc/systemd/system/nginx.service
完整模板:
[Unit]
Description=Nginx Reverse Proxy Service
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/app/nginx1.3/logs/nginx.pid
ExecStartPre=/app/nginx1.3/sbin/nginx -t -c /app/nginx1.3/conf/nginx.conf
ExecStart=/app/nginx1.3/sbin/nginx -c /app/nginx1.3/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
TimeoutStopSec=30
Restart=no
[Install]
WantedBy=multi-user.target
注意:
不要配置 ExecQuit
systemd 不识别:
ExecQuit=
创建后:
systemctl daemon-reload
systemctl enable nginx
systemctl start nginx
检查:
systemctl is-enabled nginx
systemctl is-active nginx
正常:
enabled
active
七、安装 Keepalived
执行:
dnf install -y keepalived
或者:
yum install -y keepalived
确认:
keepalived --version
八、Keepalived 主节点配置文件
Node01:
192.168.10.11
文件:
vi /etc/keepalived/keepalived.conf
完整配置:
! Configuration File for keepalived
global_defs {
router_id nginx-node01
script_user root
enable_script_security
}
vrrp_script chk_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 5
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 100
nopreempt
unicast_src_ip 192.168.10.11
unicast_peer {
192.168.10.12
}
advert_int 1
authentication {
auth_type PASS
auth_pass HaVip123
}
virtual_ipaddress {
192.168.10.10/24 dev ens33 label ens33:1
}
track_script {
chk_nginx
}
}
九、Keepalived 备节点配置文件
Node02:
192.168.10.12
文件:
vi /etc/keepalived/keepalived.conf
完整配置:
! Configuration File for keepalived
global_defs {
router_id nginx-node02
script_user root
enable_script_security
}
vrrp_script chk_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 5
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51
priority 90
nopreempt
unicast_src_ip 192.168.10.12
unicast_peer {
192.168.10.11
}
advert_int 1
authentication {
auth_type PASS
auth_pass HaVip123
}
virtual_ipaddress {
192.168.10.10/24 dev ens33 label ens33:1
}
track_script {
chk_nginx
}
}
两台主要区别:
router_id
priority
unicast_src_ip
unicast_peer
必须保持一致:
virtual_router_id
VIP
网卡
auth_pass
健康检查脚本
十、Nginx 健康检查脚本
文件:
vi /etc/keepalived/check_nginx.sh
完整模板:
#!/bin/bash
LOG_DIR="/app/keepalived/logs"
LOG_FILE="${LOG_DIR}/nginx-check.log"
ATTEMPT_FILE="/etc/keepalived/nginx-attempts.txt"
MAX_RESTART_ATTEMPTS=3
mkdir -p "${LOG_DIR}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "${LOG_FILE}"
}
get_attempts() {
if [ -f "${ATTEMPT_FILE}" ]; then
cat "${ATTEMPT_FILE}"
else
echo 0
fi
}
set_attempts() {
echo "$1" > "${ATTEMPT_FILE}"
}
if systemctl is-active --quiet nginx; then
set_attempts 0
exit 0
fi
log "Nginx service is not active."
attempts=$(get_attempts)
if [ "${attempts}" -lt "${MAX_RESTART_ATTEMPTS}" ]; then
attempts=$((attempts + 1))
log "Restart Nginx, attempt ${attempts}."
systemctl restart nginx
sleep 3
if systemctl is-active --quiet nginx; then
log "Nginx restart successful."
set_attempts 0
exit 0
fi
set_attempts "${attempts}"
log "Nginx restart failed."
exit 1
fi
log "Nginx restart failed ${MAX_RESTART_ATTEMPTS} times."
exit 1
赋权:
chmod 755 /etc/keepalived/check_nginx.sh
chown root:root /etc/keepalived/check_nginx.sh
创建计数文件:
echo 0 > /etc/keepalived/nginx-attempts.txt
chown root:root /etc/keepalived/nginx-attempts.txt
十一、为什么脚本不重启 Keepalived
不要在:
check_nginx.sh
里面执行:
systemctl restart keepalived
推荐职责:
check_nginx.sh
→ 检查 Nginx
→ 尝试恢复 Nginx
→ 返回 exit 0 / exit 1
Keepalived
→ 根据检查结果决定 VIP 是否漂移
这样逻辑更清晰。
十二、Keepalived 自启动与 systemd 管理
安装后:
systemctl enable keepalived
systemctl start keepalived
检查:
systemctl is-enabled keepalived
systemctl is-active keepalived
正常:
enabled
active
十三、Keepalived 配置检查
修改后:
keepalived -t
然后:
journalctl -u keepalived -n 50 --no-pager
BACKUP 常见:
Entering BACKUP STATE
MASTER 常见:
Entering MASTER STATE
Sending gratuitous ARP
十四、nopreempt 配置说明
配置:
nopreempt
作用:
原 MASTER 故障
↓
VIP 漂移到 BACKUP
↓
原 MASTER 恢复
↓
不主动抢回 VIP
这样可以避免 VIP 反复漂移。
因此:
使用 nopreempt
时,不建议再配置:
preempt_delay 300
否则可能出现:
preempt_delay is incompatible with nopreempt mode
十五、防火墙
业务端口按实际开放:
firewall-cmd --permanent --add-port=6041/tcp
firewall-cmd --permanent --add-port=6043/tcp
firewall-cmd --permanent --add-port=6060/tcp
VRRP:
firewall-cmd --permanent \
--add-rich-rule='rule protocol value="112" accept'
然后:
firewall-cmd --reload
检查:
firewall-cmd --zone=public --list-all
注意:
VRRP 是 IP Protocol 112
不是 TCP 112
不是 UDP 112
十六、标准启动顺序
先检查 Nginx:
/app/nginx1.3/sbin/nginx -t
启动:
systemctl start nginx
确认:
systemctl is-active nginx
两台都正常后,再启动 Keepalived:
systemctl start keepalived
十七、检查 VIP
Node01:
ip addr show ens33
正常情况下:
192.168.10.11
192.168.10.10
Node02:
ip addr show ens33
正常:
192.168.10.12
也就是:
MASTER
→ 有 VIP
BACKUP
→ 无 VIP
十八、如果两台同时出现 VIP
属于异常。
重点检查:
VRRP Protocol 112
virtual_router_id
unicast_src_ip
unicast_peer
interface
防火墙
必要时抓包:
tcpdump -ni ens33 proto 112
十九、后端检查
先测后端:
for ip in 192.168.10.21 192.168.10.22 192.168.10.23; do
echo "===== $ip ====="
curl -sS -o /dev/null \
--connect-timeout 3 \
-w "6041 HTTP=%{http_code}\n" \
http://$ip:6041/
timeout 3 bash -c "</dev/tcp/$ip/6043" \
&& echo "6043 TCP=OK" || echo "6043 TCP=FAIL"
timeout 3 bash -c "</dev/tcp/$ip/6060" \
&& echo "6060 TCP=OK" || echo "6060 TCP=FAIL"
done
二十、VIP 业务验证
6041:
curl -sS -o /dev/null \
--connect-timeout 3 \
-w "6041 HTTP_CODE=%{http_code}\n" \
http://192.168.10.10:6041/
6060:
curl -sS -o /dev/null \
--connect-timeout 3 \
-w "6060 HTTP_CODE=%{http_code}\n" \
http://192.168.10.10:6060/
6043:
timeout 3 bash -c '</dev/tcp/192.168.10.10/6043' \
&& echo "6043 TCP=OK" || echo "6043 TCP=FAIL"
二十一、VIP 漂移测试
假设 VIP 当前在 Node01:
ip addr show ens33 | grep 192.168.10.10
在 Node01:
systemctl stop keepalived
Node02 检查:
ip addr show ens33 | grep 192.168.10.10
应该看到 VIP。
然后再次测试:
VIP:6041
VIP:6043
VIP:6060
都正常说明:
VIP 漂移正常
备用 Nginx 正常
代理服务正常
二十二、恢复原节点
Node01:
systemctl start keepalived
由于:
nopreempt
所以 VIP 应继续留在 Node02。
此时:
Node01 → BACKUP
Node02 → MASTER
属于正常。
二十三、Nginx 故障自动恢复测试
在维护窗口:
systemctl stop nginx
观察:
tail -f /app/keepalived/logs/nginx-check.log
应该看到类似:
Nginx service is not active
Restart Nginx
Nginx restart successful
如果 Nginx 成功恢复:
VIP 不漂移
如果连续失败:
Keepalived 健康检查失败
↓
当前节点降权
↓
备用节点接管 VIP
二十四、如果代理 MySQL 等 TCP 服务
如果后端是:
MySQL
Redis
某些 TCP 中间件
不能使用:
http {
}
需要使用:
stream {
}
例如:
stream {
upstream mysql_backend {
server 192.168.10.21:3306;
server 192.168.10.22:3306;
}
server {
listen 3306;
proxy_connect_timeout 5s;
proxy_timeout 3600s;
proxy_pass mysql_backend;
}
}
但这里要特别注意:
MySQL 主从、双主、MGR、Galera、双向复制等架构,不能简单把所有节点无差别加入 upstream。
必须结合:
读写角色
主从角色
是否允许双写
复制机制
冲突风险
单独设计代理策略。
二十五、日常检查命令
Nginx:
systemctl is-active nginx
systemctl is-enabled nginx
/app/nginx1.3/sbin/nginx -t
Keepalived:
systemctl is-active keepalived
systemctl is-enabled keepalived
keepalived -t
VIP:
ip addr show ens33
Nginx 监听:
ss -lntp | grep nginx
Keepalived 日志:
journalctl -u keepalived -n 50 --no-pager
Nginx 日志:
journalctl -u nginx -n 50 --no-pager
二十六、新环境需要修改哪些地方
keepalived.conf
主要修改:
router_id
interface
virtual_router_id
priority
unicast_src_ip
unicast_peer
auth_pass
VIP
http-app.conf
主要修改:
VIP
业务端口
后端 IP
后端端口
upstream 名称
日志文件名
nginx.conf
如果目录结构一致:
通常基本不用改
nginx.service
如果:
/app/nginx1.3
路径一致,基本可以直接复用。
check_nginx.sh
如果服务名仍然是:
nginx
基本也可以直接复用。
二十七、以后建议保存的模板
nginx-keepalived-ha-template/
│
├── nginx/
│ ├── nginx.conf
│ ├── nginx.service
│ └── http.d/
│ └── http-app.conf
│
└── keepalived/
├── keepalived-master.conf
├── keepalived-backup.conf
└── check_nginx.sh
以后部署新环境就是:
复制模板
↓
修改两台 Nginx IP
↓
修改 VIP
↓
修改网卡
↓
修改后端 IP
↓
修改业务端口
↓
nginx -t
↓
keepalived -t
↓
启动
↓
验证 VIP
↓
验证业务
↓
验证漂移
二十八、最终标准部署流程
以后直接照这个顺序:
1. 规划 Nginx01 IP、Nginx02 IP、VIP、网卡、后端 IP、端口
2. 安装 Nginx
3. 配置 nginx.conf
4. 配置 http-app.conf
5. 创建 nginx.service
6. systemctl daemon-reload
7. systemctl enable nginx
8. systemctl start nginx
9. nginx -t
10. 安装 Keepalived
11. 配置主节点 keepalived.conf
12. 配置备节点 keepalived.conf
13. 创建 check_nginx.sh
14. chmod 755 check_nginx.sh
15. 开放业务端口
16. 开放 VRRP Protocol 112
17. keepalived -t
18. systemctl enable keepalived
19. systemctl start keepalived
20. 确认只有一台持有 VIP
21. 检查后端服务
22. 测试 VIP 业务端口
23. 测试 VIP 漂移
24. 恢复原节点验证 nopreempt
25. 测试 Nginx 自动恢复
26. 最终确认 nginx / keepalived 均 enabled + active
二十九、这次两套环境最值得记住的经验
-
Nginx 配置文件修改以后必须执行
nginx -t systemctl reload nginx -
Nginx 必须加入 systemd
否则健康检查脚本无法可靠执行 systemctl restart nginx -
nginx -t 成功不代表代理逻辑正确
proxy_pass 指错 upstream 仍可能语法正常 -
MASTER 和 BACKUP 不能同时持有 VIP
-
VRRP 放行的是 IP Protocol 112
-
用了 nopreempt 就不要再配 preempt_delay
-
健康检查脚本只负责检查和恢复 Nginx,不负责重启 Keepalived
三十、一句话总结
通过两台 Nginx 提供代理服务,Keepalived 使用 VRRP 维护统一 VIP,并周期性调用健康检查脚本检测 Nginx 状态;当当前代理节点或 Nginx 服务异常且无法恢复时,VIP 自动漂移到备用节点,Nginx 再通过 upstream 将请求转发到后端数据库、服务或中间件集群,从而实现代理入口和后端访问链路的高可用。

更多推荐



所有评论(0)