Nginx 热升级详解
一、Nginx 热升级介绍
Nginx 热升级能够实现在不影响客户端请求(服务不中断)的情形下,以新的 Nginx 二进制文件替换旧的二进制文件,并关停旧进程。这一特性得益于 Nginx 的多进程架构设计,其可在旧的 Nginx 主进程不退出时,以子进程形式启动新的 master 进程与新的 worker 进程,新 master 进程重新监听端口并接收请求。待新 master 进程稳定后,通过发送信号使旧 master 进程退出,从而完成热升级流程。
二、Nginx 热升级流程
(一)准备新的 Nginx 二进制文件
- 编译生成:通过编译获取新的 Nginx 二进制文件。需注意,为确保升级顺利,编译时必须保证与旧 Nginx 的配置文件路径、日志路径、pid 文件路径等保持一致,否则将导致 Nginx 配置失效。
- 示例演示:以特定演示环境为例,当前 Nginx 版本及配置路径如下:
$ ./nginx -v nginx version: nginx/1.22.0prefix=/home/geng/nginx /home/geng/nginx ├── client_body_temp ├── conf │ ├── nginx.conf │ └──....... ├── fastcgi_temp ├── html │ ├── 50x.html │ └── index.html ├── logs │ ├── access.log │ ├── error.log │ └── nginx.pid ├── proxy_temp ├── sbin │ ├── nginx ├── scgi_temp └── uwsgi_temp现在要升级到1.23.1版本,
下载地址http://nginx.org/download/nginx-1.23.1.tar.gz,使用wget工具下载wget http://nginx.org/download/nginx-1.23.1.tar.gz解压进入目录执行:
$ ./configure --prefix=/home/geng/nginx $ make注意:这里指定的安装路径前缀和老的nginx保持一致!
如果执行make没有报错的话在目录的objs目录下就会生成nginx二进制文件。
至此准备工作已经完成。
(二)替换老的 Nginx 二进制文件
为了方便对比,当前nginx的版本还是1.22.0

进入nginx的sbin目录,备份nginx二进制文件为nginx.oldbin
$ ls
nginx
$ mv nginx nginx.oldbin
$ ls
nginx.oldbin
拷贝新的nginx二进制文件到sbin目录:
$ cp ~/nginx-1.23.1/objs/nginx ./
$ ls
nginx nginx.oldbin
(三)通知老的 Nginx master 进程启动新的 Nginx master 进程
nginx进程间通过系统信号进行通信。
要想通知老的nginx master进程启动新的nginx master进程,只需发送USR2的系统信号到老的nginx进程。
首先查看一下nginx进程号:
$ ps -ef |grep nginx
root 8267 10 0 20:08 ? 00:00:00 nginx: master process ./nginx
nobody 8268 8267 0 20:08 ? 00:00:00 nginx: worker process
geng 8678 8599 0 20:45 pts/1 00:00:00 grep --color=auto nginx
8267就是nginx主进程号,它只有一个worker进程(pid:8268)。
使用kill命令发送信号(USR2对应的信号为12,如果记不住可以使用kill -l查看):
$ sudo kill -12 8267
此时查看nginx进程:
$ ps -ef |grep nginx
root 8267 10 0 20:08 ? 00:00:00 nginx: master process ./nginx
nobody 8268 8267 0 20:08 ? 00:00:00 nginx: worker process
root 8681 8267 0 20:47 ? 00:00:00 nginx: master process ./nginx
nobody 8682 8681 0 20:47 ? 00:00:00 nginx: worker process
geng 8684 8599 0 20:49 pts/1 00:00:00 grep --color=auto nginx
发现有两个master进程,第二个master进程就是新的主进程,并且是作为老进程的子进程启动的。
并且nginx会将老的pid文件命名为nginx.pid.oldbin。
其实此时再有新的连接进来,就由新的主进程和新的worker进程处理了。
我们访问一下就能验证:

那为什么老的nginx进程为什么不主动退出呢?
这是因为当我们新的nginx二进制文件有问题或需要回滚时,方便我们直接回退到老的nginx。
只需通过向老的信号发送信号“SIGHUP (1)”,然后向新的进程发送信号“SIGQUIT (3)”将新的进程退出。$ sudo kill -1 8267 $ sudo kill -3 8681 $ ps -ef |grep nginx root 8267 10 0 20:08 ? 00:00:00 nginx: master process ./nginx nobody 8268 8267 0 20:08 ? 00:00:00 nginx: worker process nobody 8692 8267 0 20:57 ? 00:00:00 nginx: worker process访问一下,发现已经退回到1.22.0版本了!
(四)退出老的 master 进程
新的master进程如果没有问题,就可以退出老的进程了。
只需通过向老的进程发送信号“SIGQUIT (3)”,老的进程就会优雅的退出。
$ sudo kill -3 8267
$ ps -ef |grep nginx
root 8681 10 0 20:47 ? 00:00:00 nginx: master process ./nginx
nobody 8682 10 0 20:47 ? 00:00:00 nginx: worker process
此时只剩新的进程了。
注意:有些时候由于老的进程中还有请求没处理玩可能不能立即退出,此时查看进程可能是下面的结果:
root 8267 10 0 20:08 ? 00:00:00 nginx: master process ./nginx
nobody 8268 8267 0 20:08 ? 00:00:00 nginx: worker process is shutting down
nobody 8692 8267 0 20:57 ? 00:00:00 nginx: worker process is shutting down
正常情况,当worker进程处理完请求就会连带老的master进程一起退出。
至此nginx热升级完成。
三、Nginx 热升级流程总结
一般遵循如下流程:
- 备份旧的 Nginx 二进制文件,同时准备新的 Nginx 二进制文件,并确保新文件各配置路径与旧文件一致。
- 向旧的 Nginx 进程发送
SIGUSR2 (12)信号,启动新的进程,使新老进程并存。 - 向旧的 Nginx 进程发送
SIGQUIT (3)信号,关停旧的 Nginx 进程。
四、Nginx 热升级过程中的风险
(一)配置兼容性风险
- 风险描述
- 尽管在准备新的 Nginx 二进制文件时强调了配置路径等要保持一致,但新版本的 Nginx 可能会对配置文件的语法、模块的使用方式或者某些指令的语义进行了修改。这可能导致新的 Nginx 进程在加载配置文件时出现错误,无法正常启动或者部分功能失效。
- 例如,某些在旧版本中被允许的配置指令组合,在新版本中可能由于安全策略或者性能优化的考虑而被禁止。
- 风险示例
- 假设旧版本的 Nginx 允许在某个特定模块的配置中有较为宽松的访问控制设置,而在新版本中加强了安全性要求,该设置可能会被判定为不符合要求而导致模块无法正常加载。
(二)二进制文件兼容性风险
- 风险描述
- 新编译的 Nginx 二进制文件可能因为编译环境的差异(如不同的编译器版本、编译选项或者依赖库版本)而存在兼容性问题。这可能导致新的二进制文件在运行时出现不可预测的行为,如段错误、内存泄漏或者性能下降。
- 特别是当依赖库进行了不兼容的更新或者系统底层接口发生变化时,新的 Nginx 二进制文件可能无法正确调用相关的函数或者资源。
- 风险示例
- 如果新的 Nginx 依赖的 OpenSSL 库在升级后改变了其 API 接口,而新的 Nginx 二进制文件是基于旧的 API 编译的,那么在处理 SSL/TLS 相关的请求时就可能出现问题,如无法正确建立加密连接或者解密数据。
如何验证新的Nginx二进制文件的兼容性?
功能测试方面
- HTTP/HTTPS 基本功能测试
- 文件服务测试:使用
curl命令从本地或远程客户端访问 Nginx 服务器提供的静态文件(如 HTML、CSS、JavaScript 文件等)。例如,若 Nginx 服务器的 IP 地址为192.168.1.100,文档根目录下有index.html文件,在客户端执行curl http://192.168.1.100/index.html,检查是否能够正确返回文件内容。对于 HTTPS 服务,使用curl -k https://192.168.1.100/index.html(-k选项用于跳过证书验证,仅用于测试目的),确保能够正常提供加密连接下的文件服务。- 虚拟主机测试:如果配置了多个虚拟主机,分别访问每个虚拟主机对应的域名或 IP 地址,检查是否能够正确返回对应的网站内容。例如,配置了两个虚拟主机,域名分别为
example1.com和example2.com,在客户端的/etc/hosts文件中添加对应的 IP 地址解析(假设 Nginx 服务器 IP 为192.168.1.100),使192.168.1.100 example1.com和192.168.1.100 example2.com生效,然后分别用curl http://example1.com和curl http://example2.com检查虚拟主机是否正常工作。- 反向代理功能测试
- 后端服务连通性测试:如果 Nginx 配置为反向代理后端服务(如 Web 应用服务器、API 服务器等),通过 Nginx 访问后端服务的接口。假设后端服务运行在
192.168.1.200:8080,Nginx 反向代理配置监听192.168.1.100:80并转发到后端服务,在客户端使用curl http://192.168.1.100/api/endpoint(假设/api/endpoint是后端服务的一个接口),检查是否能够正确获取后端服务返回的内容。可以使用后端服务的特定测试工具或接口文档来验证返回结果是否符合预期。- 负载均衡测试(如果适用):当 Nginx 配置了负载均衡功能,将请求分发到多个后端服务器时,通过大量并发请求来测试负载均衡是否正常工作。例如,使用
ab(ApacheBench)工具,执行ab -n 1000 -c 100 http://192.168.1.100/api/endpoint,其中-n表示请求总数为 1000,-c表示并发请求数为 100。检查请求是否均匀地分发到各个后端服务器,可以通过查看后端服务器的访问日志或者监控工具来验证。- 动态模块功能测试
- 检查模块加载情况:如果 Nginx 使用了动态模块(如 HTTP Basic Auth 模块、SSL 模块等),查看 Nginx 启动日志,确认模块是否正确加载。例如,在 Nginx 启动后,检查
error.log文件中是否有关于模块加载失败的错误信息,如[emerg] cannot load module "ngx_http_auth_basic_module"。- 模块功能验证:对于每个加载的动态模块,进行相应的功能测试。以 HTTP Basic Auth 模块为例,配置好用户名和密码后,使用
curl命令带上认证信息访问受保护的资源,如curl -u username:password http://192.168.1.100/protected_resource,检查是否能够正确进行认证并返回受保护的内容。性能测试方面
- 吞吐量测试
- 使用专业工具测试:采用性能测试工具,如
wrk或ab。以wrk为例,在客户端执行wrk -t 10 -c 100 -d 30s http://192.168.1.100/,其中-t表示线程数为 10,-c表示并发连接数为 100,-d表示测试持续时间为 30 秒。工具会输出每秒请求数(Requests/sec)、请求延迟(Latency)等性能指标,将新的 Nginx 二进制文件的测试结果与旧版本进行对比,确保吞吐量没有明显下降。- 响应时间测试
- 结合日志分析:在 Nginx 配置中开启详细的访问日志,记录每个请求的处理时间。例如,在
nginx.conf中配置log_format指令,添加$request_time变量来记录请求处理时间。在性能测试过程中,分析访问日志,统计平均响应时间、最长响应时间等指标,检查新的二进制文件是否导致响应时间明显增加。- 使用性能监控工具:利用系统性能监控工具,如
htop或vmstat,在测试过程中监控系统资源使用情况(如 CPU 使用率、内存使用率等)。如果系统资源占用过高,可能会导致响应时间增加。例如,观察htop中的 CPU 使用率,如果在测试过程中某个 CPU 核心一直处于高负载状态(如超过 80%),可能需要进一步分析是 Nginx 配置问题还是二进制文件性能问题导致的。兼容性测试方面
- 操作系统兼容性测试
- 在不同操作系统环境测试:如果 Nginx 需要在多种操作系统上运行(如 Linux 不同发行版、Windows 等),在目标操作系统上安装并测试新的二进制文件。例如,在 Ubuntu、CentOS 等 Linux 发行版以及 Windows Server 上分别进行功能和性能测试,确保在不同操作系统环境下都能正常工作。
- 检查系统调用兼容性:新的二进制文件可能依赖于系统调用,检查其是否在不同操作系统版本中都能正确执行。例如,在 Linux 系统中,某些较新的系统调用可能在旧版本内核中不存在,通过查看 Nginx 启动日志和运行时错误信息,确认是否存在因系统调用不兼容导致的问题。
- 依赖库兼容性测试
- 检查依赖库版本兼容性:明确 Nginx 所依赖的库(如 PCRE、zlib、OpenSSL 等),确保新的二进制文件在与旧版本相同或兼容的依赖库版本下能够正常工作。例如,若旧的 Nginx 依赖 OpenSSL 1.1.0,检查新的二进制文件在 OpenSSL 1.1.0 下是否存在问题。可以通过在测试环境中安装指定版本的依赖库,然后运行 Nginx 来验证。
- 动态链接库加载测试:对于动态链接的依赖库,检查 Nginx 在启动和运行过程中是否能够正确加载和使用。查看 Nginx 启动日志中是否有关于动态链接库加载失败的信息,如
[error] dlopen() failed for /usr/lib/libpcre.so.1。同时,在运行过程中,可以使用工具如ldd来检查 Nginx 二进制文件所依赖的动态链接库是否完整且正确,例如ldd /usr/sbin/nginx会列出 Nginx 所依赖的所有动态链接库及其路径。
(三)进程通信和信号处理风险
- 风险描述
- 在热升级过程中,依赖于进程间信号来协调新旧 Nginx 进程的切换。如果信号发送失败(例如由于权限问题或者系统信号队列已满)或者信号处理程序出现错误,可能会导致新旧进程之间的切换出现混乱。
- 例如,老的 Nginx 主进程可能无法正确接收新进程启动的信号,或者新进程无法接收到旧进程退出的信号,从而导致两个进程同时处理请求或者出现请求丢失的情况。
- 风险示例
- 若在一个权限设置较为复杂的系统环境中,执行
kill命令发送信号的用户没有足够的权限,信号发送会失败。这可能导致新的 Nginx 进程无法正常启动,而系统管理员可能误以为升级已经成功,进而引发服务故障。
- 若在一个权限设置较为复杂的系统环境中,执行
(四)未完成请求处理风险
- 风险描述
- 当新的 Nginx 进程启动后,旧的 Nginx 进程可能仍有部分未完成的请求在处理。如果旧的进程在这些请求完成之前被强制退出,可能会导致客户端请求中断,数据丢失或者出现错误响应。
- 特别是对于一些长连接或者复杂的事务处理(如文件上传、数据库事务等),这种情况可能会对用户体验和数据完整性造成严重影响。
- 风险示例
- 若一个用户正在通过 Nginx 上传一个大文件,在热升级过程中旧的 Nginx 进程提前退出,可能会导致文件上传中断,用户需要重新上传,而且已经上传的部分数据可能丢失。
五、应对风险的策略
(一)配置兼容性风险应对
- 备份和检查配置文件
- 在升级之前,对旧的 Nginx 配置文件进行完整备份。同时,仔细阅读新版本 Nginx 的官方文档,特别是关于配置文件变化的部分,检查配置文件中是否存在可能不兼容的指令或者设置。
- 例如,可以使用版本控制系统(如 Git)对配置文件进行管理,方便对比旧版本和新版本的配置差异。
- 测试配置文件
- 在测试环境中,使用新的 Nginx 二进制文件和旧的配置文件进行测试,观察是否有加载错误或者警告信息。如果可能,编写自动化测试脚本,模拟各种常见的请求场景,检查 Nginx 的功能是否正常。
- 例如,可以使用工具如
nginx -t来测试配置文件的语法正确性,并且结合模拟客户端工具(如curl)发送不同类型的请求,如 HTTP、HTTPS 请求,检查服务器的响应是否符合预期。
(二)二进制文件兼容性风险应对
- 保持编译环境一致性
- 尽量在与旧 Nginx 编译环境相似的环境中编译新的二进制文件。记录旧版本编译时使用的编译器版本、编译选项和依赖库版本,在新的编译过程中保持一致。
- 例如,如果旧的 Nginx 是使用 GCC 7 编译的,那么在编译新的二进制文件时也应该使用 GCC 7,并且确保相关的依赖库(如 PCRE、zlib 等)的版本与旧环境兼容。
- 进行功能和性能测试
- 在测试环境中,对新编译的二进制文件进行全面的功能测试和性能测试。功能测试包括检查各种 HTTP/HTTPS 功能、反向代理功能、负载均衡功能等是否正常。性能测试可以使用工具如
ab(ApacheBench)或者wrk来测试 Nginx 的吞吐量、响应时间等性能指标,确保新的二进制文件没有性能下降。 - 例如,使用
ab -n 1000 -c 100 http://your-nginx-server/命令来测试 Nginx 服务器在处理 1000 个请求,并发数为 100 时的性能,对比新老版本的测试结果。
- 在测试环境中,对新编译的二进制文件进行全面的功能测试和性能测试。功能测试包括检查各种 HTTP/HTTPS 功能、反向代理功能、负载均衡功能等是否正常。性能测试可以使用工具如
(三)进程通信和信号处理风险应对
- 权限检查和信号队列监控
- 在发送信号之前,检查执行
kill命令的用户是否具有足够的权限。同时,可以通过系统监控工具(如top或者htop)查看系统信号队列的状态,确保信号能够正常发送和接收。 - 例如,在 Linux 系统中,可以使用
ps -eo pid,user,comm,signal命令来查看进程的信号相关信息,确保 Nginx 进程能够正常接收和处理信号。
- 在发送信号之前,检查执行
- 信号处理程序验证
- 可以在 Nginx 的代码中(如果有修改代码的权限)或者通过添加自定义的日志输出,对信号处理程序进行验证。确保信号处理程序能够正确识别和处理各种信号,并且在出现错误时能够进行适当的记录。
- 例如,在信号处理程序中添加日志输出语句,记录接收到的信号类型和处理结果,以便在出现问题时能够快速定位。
(四)未完成请求处理风险应对
- 优雅关闭旧进程
- 不要急于强制退出旧的 Nginx 进程,而是等待旧进程中的所有请求自然完成。可以通过监控旧进程的活动状态(如检查其 worker 进程是否还有正在处理的连接)来判断是否可以安全地关闭旧进程。
- 例如,在 Nginx 的日志配置中添加更详细的请求处理日志,通过查看日志来确定旧进程中的请求是否已经全部完成。
- 客户端重试机制
- 在客户端应用程序中(如果可能),设计合理的重试机制。当出现请求中断的情况时,客户端能够自动重新发送请求,减少对用户体验的影响。
- 例如,对于 Web 浏览器客户端,利用浏览器的自动重试功能或者在前端 JavaScript 代码中添加重试逻辑,当遇到网络错误或者服务器响应异常时,自动重新请求。
更多推荐


所有评论(0)