在生产环境中升级 Nginx 一直是运维人们最谨慎的操作之一,因为它通常直接暴露在公网,承载着大量的流量,任何一点宕机或配置错误都可能造成业务影响。本文记录了一次真实的线上 Nginx 升级过程:从 1.24.0 升级到 1.28.0 ,全程实现零宕机,并且采用最安全的"二进制文件替换"方式,避免在生产机器上编译。
一、背景与环境

-
机器信息:CentOS 7(内核 3.10),源码编译安装的 Nginx
-
binary 路径 :
/usr/local/nginx/sbin/nginx -
配置路径 :
/etc/nginx/(配置文件为/etc/nginx/nginx.conf) -
当前版本:nginx/1.24.0
-
目标版本:nginx/1.28.0
-
运行方式 :手动启动(非 systemd 管理),但进程手动启动也长期正常运行

-
特点:无第三方动态模块,编译参数相对简单
二、为什么选择"只替换 binary"的方式?
常见的 Nginx 升级方式有三种:
-
直接在生产机源码编译 + make install
风险:需要安装开发工具、下载源码、长时间编译,万一依赖缺失或编译失败,可能影响运行中的服务。
-
使用 yum/apt 等包管理升级
不适用:本机为源码安装,无 RPM 包。
-
在测试机编译新 binary,只拷贝可执行文件替换 (本次采用)
优点:
- 生产机几乎不执行高风险操作
- 只传输一个几 MB 的二进制文件
- 支持 Nginx 官方推荐的热二进制替换,实现真正零宕机
- 保持原有配置、日志、权限完全不变nginx文档
三、升级全过程详解
步骤 1:备份(永远的第一步)
在生产机上备份旧 binary:
bash
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx-v1.24.0
步骤 2:获取当前编译参数(最关键!)
javascript
# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.24.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --user=nginx --group=nginx --prefix=/usr/local/nginx --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --with-pcre-jit --with-http_ssl_module --with-http_v2_module --with-http_sub_module --with-stream --with-stream_ssl_module
输出中重点复制 configure arguments 那一长串(包括 --prefix=/usr/local/nginx),确保新版本用完全相同的参数编译,否则可能缺少模块或路径不一致。
步骤 3:在测试机编译新版本(环境需一致)
选择一台系统、gcc 版本、库版本相近的测试机。
bash
cd /tmp
wget https://nginx.org/download/nginx-1.28.0.tar.gz
tar zxvf nginx-1.28.0.tar.gz
cd nginx-1.28.0
./configure [粘贴上面完整的 configure arguments]
make -j$(nproc) # 快速并行编译(后面的nproc是指最多用多少个核心去编译,如果不写似乎默认是1)
关键提醒 :不要执行 make install!
编译完成后,新 binary 位于:
bash
./objs/nginx
验证版本:
bash
./objs/nginx -v
# 输出:nginx version: nginx/1.28.0
步骤 4:传输并替换生产机 binary
bash
# 从测试机拷贝到生产机的 /tmp
scp objs/nginx root@生产机IP:/tmp/nginx
# 生产机上替换
cd /usr/local/nginx/sbin
mv /tmp/nginx ./
# 直接覆盖或先备份旧的后再 mv
chmod 755 nginx
步骤 5:验证配置与版本
bash
/usr/local/nginx/sbin/nginx -t
# 输出 syntax is ok 和 test is successful
/usr/local/nginx/sbin/nginx -V
# 确认显示 nginx/1.28.0,且 configure arguments 与之前一致
步骤 6:平滑重载(零宕机升级)
bash
/usr/local/nginx/sbin/nginx -s reload
原理:Nginx master 进程收到 reload 信号后,会启动新的 worker 进程(使用新 binary),旧 worker 处理完当前连接后自动退出,整个过程无连接中断。
在这一步其实只是用了新的worker,而nginx的架构是master+worker的,但是master的话只是负责读取和校验配置文件、绑定 80/443 等特权端口、创建、管理、监控 worker 进程等,实际上几乎不占资源,如果要重载master的话,可以进行重启操作:
shell
$/usr/local/nginx/sbin/nginx -s quit
# 等待一段时间,等到nginx相关进程全部停下来,除了 grep 本身,什么都不应该有
$ps -ef | grep nginx
# 重启nginx
$/usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
四、背后的核心原理
-
Nginx 的 master-worker 架构
master 进程负责读取配置、管理 worker;worker 负责实际处理请求。reload 时 master 启动新 worker,旧 worker 优雅退出。
-
二进制热替换支持
Nginx 在启动时会将自身可执行文件路径记录下来。即使你替换了磁盘上的 binary 文件,正在运行的 master 进程仍使用旧的内存镜像。但 reload 后,新 worker 会加载新的 binary,从而实现版本升级。
-
为什么只换 binary 就行?
Nginx 的模块大部分是静态编译进 binary 的(本次无动态模块)。只要编译参数一致,新 binary 就能完美适配原有配置和路径。
五、收尾与建议
- 观察
/var/log/nginx/error.log一段时间,确保无异常 - 可选:切换为 systemd 管理,便于开机自启和统一操作
- 保留旧 binary 至少一周,以备回滚
六、总结
这次升级全程不到 20 分钟,服务无任何感知,真正实现了生产环境的平滑升级。核心经验就是:
- 备份永远第一位
- 编译参数必须一模一样(尤其是 --prefix)
- 优先选择"只替换 binary"的最小侵入方式
- 利用 Nginx 自身平滑重载机制实现零宕机
这种方法特别适合老旧生产环境、源码安装、无包管理的场景咯