Nginx 高可用与 HTTPS 落地:Keepalived 双机主备 + 证书改造实战
Nginx 高可用与 HTTPS 落地:Keepalived 双机主备 + 证书改造实战
大促前做架构巡检时发现一个致命问题:整条链路里 Nginx 入口只有一台,它一挂全站就没了。同时安全合规也要求全站上 HTTPS。两件事凑一起做了一次改造:双机主备入口 + 证书统一下放到入口层。这篇记录配置过程和几个只有实际部署才会遇到的坑。
一、Keepalived 怎么实现高可用
Keepalived 基于 VRRP 协议做主备切换,核心是一台”虚拟路由器”:
- 两台机器(master/backup)组成一个组,对外暴露一个虚拟 IP(VIP);
- 域名解析指向 VIP;
- master 每秒发心跳,backup 收不到心跳就认为自己该接管,把 VIP 抢过来;
- 用户侧无感知——地址没变,只是背后换了台机器。
用户不用改任何配置,因为 VIP 和它的虚拟 MAC 在 ARP 缓存里是稳定的,这一点比”手动把 DNS 切到备机”可靠得多。
二、主备配置
1 | yum install -y keepalived # 两台都装 |
master(lb01):
1 | cat /etc/keepalived/keepalived.conf |
1 | global_defs { |
backup(lb02)只需改三处:router_id lb02、state BACKUP、priority 100。
1 | systemctl start keepalived && systemctl enable keepalived |
切换测试:停掉 master 的 keepalived,看 VIP 是否漂到 backup;再启动 master,看是否抢回。
抢占与非抢占
默认是抢占式:master 恢复后会把 VIP 抢回去。频繁抢来抢去对连接是有损的,追求稳定可以把两边都配成 BACKUP + nopreempt:
1 | vrrp_instance VI_1 { |
非抢占模式下的规则:两台 state 都是 BACKUP、都加 nopreempt、优先级一高一低——谁先拿到就谁持有,直到它真挂。
脑裂:两台都以为自己是主
“脑裂”指主备之间的心跳断了,但两台机器都还活着,各自认为自己是 master,VIP 就冲突了。常见诱因:网线/交换机抖动、主备之间被防火墙拦了心跳。
预防和检测:在备机放一个巡查脚本,能 ping 通主、同时自己又持有 VIP,就说明脑裂了。
1 |
|
生产上更稳妥的做法是把仲裁和告警接进监控(Zabbix/钉钉),脚本只负责发现问题,处理动作走人工确认。
三、Keepalived 要能感知 Nginx 死活
只装 Keepalived 有个问题:Nginx 挂了但 Keepalived 还活着,VIP 不会漂移,用户请求照样打过来报错。必须写个探测脚本,Nginx 没了就主动退出 keepalived 触发切换:
1 |
|
1 | chmod +x /root/check_web.sh |
在 keepalived 配置里挂上:
1 | vrrp_script check_web { |
注意:抢占式只在 master 上配即可;非抢占式两台都要配。脚本执行时间要小于 interval,否则会互相打断。
四、全站 HTTPS 改造
1. 证书从哪来、放哪里
正式环境用 CA 签发的证书(云厂商有免费 DV 证书)。测试环境用 openssl 自签:
1 | mkdir -p /etc/nginx/ssl_key && cd /etc/nginx/ssl_key |
证书文件权限设 600,私钥泄露等于证书作废。
2. 入口层统一终止 HTTPS
证书只配在负载均衡层,后端继续跑 HTTP(内网传输),这是最常见的架构:
1 | upstream website { |
3. 上线后出现”破图”和跳转死循环——两个真实坑
破图:WordPress 早期用 http 安装,站点地址和文章里的图片链接都写进了数据库(http://www.example.com/...)。上 HTTPS 后浏览器判定”HTTPS 页面加载 HTTP 资源”不安全,图片全裂。处理办法:
- 用 WP 后台”设置 → 常规”把站点地址改成 https;
- 数据库里的历史链接做一次批量替换(改前先备份库);
- 长期方案是在加载时统一由
define('WP_HOME', ...)强制协议。
PHP 不认识自己跑在 HTTPS 下:入口是 HTTPS,但后端 Nginx 到 PHP-FPM 是 HTTP,PHP 判断协议出错,重定向时把自己甩回 http,形成跳转循环。要在后端 fastcgi 参数里显式声明:
1 | location ~ \.php$ { |
这个问题排查花了我们不少时间:现象是登录页无限跳转,最后靠翻 PHP 框架源码里 is_ssl() 的判断逻辑才定位到。
4. SSL 参数优化
1 | ssl_session_cache shared:SSL:10m; # 会话复用,减少重复握手 |
另外可以配合入口层的两个强化项:HSTS(add_header Strict-Transport-Security "max-age=31536000" always;)和证书到期监控(openssl s_client | openssl x509 -noout -enddate,到期前 30 天必须告警——证书过期导致全站警告这件事,每年都能在朋友圈看到别人家的翻车)。
五、顺手做的性能优化
高可用改造的同时,把入口层参数也调了一轮:
1 | # 系统层:文件句柄 |
代理到后端的连接池也会配(否则每个用户请求都跟后端新握手一次):
1 | upstream backend { |
我们压测对比过:开 keepalive 之后同一并发下后端的连接数下降一大半;而静态文件交给 Nginx 直接服务时,同样的并发下 QPS 能到 Tomcat 的四倍以上(九千多对两千左右)——这就是动静分离的收益。
六、巡检和证书管理
改完之后给运维组加了几条日常巡检动作:
1 | systemctl status nginx # 服务状态 |
每周看一次慢接口 TOP 和状态码分布,把问题发现在用户投诉之前——这套巡检清单沿用到现在。
七、总结
高可用和 HTTPS 这两件事,配置本身都不复杂,真正决定成败的是细节:Keepalived 要能感知 Nginx 死活、非抢占式要两台都改、HTTPS 要处理应用层的协议识别。做完这一轮,入口层从”最脆弱的一环”变成了整个链路里最稳的一层。


