HAProxy 实现配置热更新且不中断现有连接,最稳妥的方式是通过 systemd 的 reload 命令或启动新进程时带上旧进程 PID,让旧进程处理完现有请求后再退出。
先说结论:HAProxy 支持软停止(Soft Stop)机制,可以在不切断现有 TCP 连接的情况下加载新配置,但需要确保新配置语法正确且进程管理脚本配合得当。
- 适合:生产环境需要变更后端列表、超时参数或 ACL 规则时
- 先准备:务必先用 -c 参数检查新配置文件语法,避免启动失败
- 验收:确认旧进程状态变为停止且新进程 PID 已更新,观察日志无报错
确认 systemd 服务配置
大多数现代 Linux 发行版默认配置了平滑重载,但建议先确认 service 文件中是否包含 ExecReload 指令,否则 systemctl reload 可能退化为重启。
systemctl cat haproxy | grep ExecReload
正常输出应包含 haproxy -f ... -sf ... 类似指令。若为空,需手动修改 service 文件或改用手动重载方式。
执行重载操作
推荐使用 systemd,它会自动处理 PID 文件更新和信号发送:
systemctl reload haproxy
如果手动管理进程,需要先获取旧进程 PID,再启动新进程并指定 -sf 参数:
haproxy -f /etc/haproxy/haproxy.cfg -sf $(cat /var/run/haproxy.pid)
HAProxy 的平滑重载依赖于进程间的文件描述符继承。当新进程启动时,它会接管监听 socket,而旧进程收到 SIGUSR1 信号(通过 -sf 参数触发)后,会停止接受新连接,但继续处理已建立的连接,直到所有会话结束或达到强制停止超时时间。切换耗时取决于当前活跃连接的数量和持续时间,无固定阈值。
配置检查失败排查
在重载前,必须确认新配置文件没有语法错误,否则新进程无法启动,旧进程会继续运行但配置未更新。
haproxy -c -f /etc/haproxy/haproxy.cfg
输出 "Configuration file is valid" 才算通过。若检查失败,根据报错行号排查:
- bind 端口权限不足:确认是否以 root 启动或使用了 cap_net_bind_service
- 语法拼写错误:检查 timeout、backend 名称是否匹配
- 文件路径错误:确认 ssl 证书或 sock 文件路径存在且可读
验证与观察
1. 检查进程 PID
对比重载前后的 PID 是否变化,确认有新进程产生:
ps aux | grep haproxy
2. 检查监听端口
确认端口依然处于 LISTEN 状态,且没有大量 TIME_WAIT 异常:
ss -tlnp | grep haproxy
3. 查看日志
检查 /var/log/haproxy.log 或 journalctl 输出,确认没有 "Fatal" 或 "Cannot bind socket" 错误。
journalctl -u haproxy -n 50
常见风险与处理
1. 旧进程无法退出
如果有长连接(如 WebSocket),旧进程会一直等待连接关闭。建议在配置中设置 hard-stop-after 参数,强制旧进程在指定时间后退出,例如:
global
hard-stop-after 1m2. 配置文件路径不一致
手动启动时,-f 指定的路径必须与 systemd 或旧进程使用的路径一致,否则可能导致配置行为不符合预期。
3. 权限问题
确保新进程有权限读取配置文件和绑定端口,尤其是非 root 用户运行 HAProxy 时,socket 文件权限可能阻碍重载。