线上业务正跑着,安全公告突然说要升级nginx版本,不然有漏洞风险。这时候要是直接nginx -s stop再启动,所有在线的连接瞬间全断,正在上传文件的用户、正在走的支付请求,全都得重来一遍。我遇到这种情况不止一次,所以热升级(也叫平滑升级)是我在CentOS 7上处理nginx版本更新时最常用的方式。整个过程不中断服务、不丢连接,几分钟内把二进制从旧版本换到新版本,老连接处理完自然退出,新连接直接由新进程接管。这篇内容就是完整记录我在CentOS 7上做nginx热升级的步骤、背后逻辑和踩过的坑,给要做同样操作的运维、后端开发一个可以直接照着走的参考。
1. 什么情况下该用热升级,为什么“先停再启”行不通
先说清楚一个概念:热升级解决的是“nginx版本要变,但业务不能断”的问题。注意,这里说的是二进制版本要变,不是配置要变。很多人会把nginx -s reload当成升级手段,但实际上reload只是重新加载配置文件,运行中的nginx代码版本根本不会变化。要换版本,常规做法是停掉服务、替换二进制、再启动,但这个操作在线上场景里往往不现实。
1.1 需要热升级的典型场景
- 安全漏洞修复:nginx官方或发行版仓库发布安全公告后,需要尽快升级到修复版本。这种需求通常很急,但业务又不能停。
- 新增功能或模块:比如要启用HTTP/2、gRPC支持、新的负载均衡策略,或者要加入公司自研的第三方模块,这些往往要求nginx主版本升级。
- 性能优化或老版本兼容问题:某些版本在高并发下存在性能瓶颈,或者和上游组件有兼容性问题,需要升级解决。
- 第三方模块适配:自研模块或商业模块要求nginx版本必须大于某个版本。
CentOS 7上这个问题尤其明显。系统自带的nginx版本往往比较老,yum源里的版本也未必是最新的,很多情况下需要手动编译新版。而线上服务挂着大量的长连接、keepalive连接、WebSocket连接,一旦重启nginx,这些连接全部断裂。客户端重连是一回事,更麻烦的是依赖nginx做反向代理的内部系统,上游连接一断,链路抖动会传导到整个业务。
1.2 热升级和“先停再启”的本质差异
普通重启的过程是:停止进程→释放端口→启动新进程→重新监听端口。在这段间隙里,端口没有进程监听,所有请求直接失败。
热升级的过程则是:旧master进程收到信号后,用磁盘上的新版本二进制再启动一个master进程,新master重新绑定监听端口(因为存在SO_REUSEPORT或者监听socket由master继承,实际上新老进程可以同时监听同一组端口),新连接由新worker处理,旧worker继续处理已经建立的连接,等旧worker处理完手里的请求后优雅退出,最后旧master退出。整个过程没有端口空窗期,没有连接被强制断开。
| 升级方式 | 二进制版本是否变化 | 已有连接是否中断 | 是否需要停机窗口 | 操作复杂度 |
|---|---|---|---|---|
| nginx -s reload | 否 | 不中断 | 不需要 | 低 |
| stop + start | 是 | 全部中断 | 需要 | 低 |
| kill -TERM + start | 是 | 全部中断 | 需要 | 低 |
| 热升级(USR2信号流程) | 是 | 不中断 | 不需要 | 中 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么能不断线:master-worker进程模型和信号交接逻辑
热升级能成立,底层靠的是nginx的进程模型和Unix信号机制。不理解这部分,操作起来就是背命令,一旦命令执行结果和预期不符,完全不知道从哪里排查。
2.1 master-worker架构
nginx启动后默认有两个类型的进程:
- master进程:负责读取配置、绑定监听端口、管理worker进程的生命周期。不直接处理请求。
- worker进程:实际处理请求的进程,每个worker是一个单线程事件循环。连接接受、HTTP解析、日志写入都由worker完成。
在热升级场景里,master进程最关键的能力是:它可以在运行期间,根据信号指令,用磁盘上当前存在的nginx二进制文件再启动一个全新的master进程。这就是为什么我们可以先用新二进制覆盖旧二进制,然后通过信号让旧master“生出”一个新master来。
2.2 热升级用到的几个信号
nginx的信号处理机制非常成熟,以下是热升级流程中涉及的关键信号:
| 信号 | 作用 | 使用场景 |
|---|---|---|
| USR2 | 旧master启动新的master进程,新旧master共存 | 热升级第一步 |
| WINCH | 告知master让其worker进程优雅退出(处理完当前请求后退出),master本身不退出 | 热升级第二步:让旧worker退场 |
| QUIT | 优雅关闭master进程(会先等它的worker全部退出) | 热升级最后一步:关掉旧master |
| HUP | 重载配置,重新生成worker进程 | 回滚时让旧master拉起新worker |
| TERM/INT | 快速关闭(不等待) | 一般不建议在升级流程使用 |
2.3 整个交接过程是怎样的
一次标准的nginx热升级流程,信号发送顺序是这样的:
- 向旧master发送
USR2信号,旧master用新二进制启动新master和新worker。 - 新master和新worker开始接管新连接。
- 向旧master发送
WINCH信号,旧master让旧worker处理完当前请求后逐个退出。 - 确认旧
