在本篇博客文章中,我们将介绍如何在不中断服务的情况下对Nginx进行平滑升级。这对于需要保持高可用性的Web服务器来说尤为重要。以下是详细的步骤和注意事项。
为什么要平滑升级?
在生产环境中,直接停止Nginx服务以进行版本更新可能会导致短暂的服务中断,影响用户体验。通过平滑升级,可以在不影响现有连接的前提下完成Nginx的版本更新或模块添加等操作。
nginx 命令和信号
nginx 命令支持向其发送信号,实现不同功能
nginx 格式
nginx [-?hvVtTq] [-s signal] [-p prefix] [-e filename] [-c filename] [-g directives]
说明
帮助: -? -h
使用指定的配置文件: -c
指定配置指令:-g
指定运行目录:-p
测试配置文件是否有语法错误:-t -T
打印nginx的版本信息、编译信息等:-v -V
发送信号: -s 示例: nginx -s reload
信号说明
立刻停止服务:stop,相当于信号SIGTERM,SIGINT
优雅的停止服务:quit,相当于信号SIGQUIT
平滑重启,重新加载配置文件: reload,相当于信号SIGHUP
重新开始记录日志文件:reopen,相当于信号SIGUSR1,在切割日志时用途较大
平滑升级可执行程序:发送信号SIGUSR2,在升级版本时使用
优雅的停止工作进程:发送信号SIGWINCH,在升级版本时使用
查看帮助
[root@rocky9 ~]# nginx -h
nginx version: nginx/1.26.2
Usage: nginx [-?hvVtTq] [-s signal] [-p prefix]
[-e filename] [-c filename] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit
-t : test configuration and exit
-T : test configuration, dump it and exit
-q : suppress non-error messages during configuration testing
-s signal : send signal to a master process: stop, quit, reopen, reload
-p prefix : set prefix path (default: /app/nginx/)
-e filename : set error log file (default: logs/error.log)
-c filename : set configuration file (default: conf/nginx.conf)
-g directives : set global directives out of configuration file
quit 实现worker进程优雅关闭
设置定时器: worker_shutdown_timeout
关闭监听句柄
关闭空闲连接
在循环中等待全部连接关闭
退出nginx所有进程
reload 流程
![图片[1] - nginx命令与信号:平滑升级与优雅关闭 - 宋马](https://pic.songma.com/blogimg/20251005/f93be7e5ea914cfd9fae64ea1710c875.png)
![图片[2] - nginx命令与信号:平滑升级与优雅关闭 - 宋马](https://pic.songma.com/blogimg/20251005/d09b387e381f4d249d7a7c4e52460d0b.png)
利用 reload 可以实现平滑修改配置并生效
向master进程发送HUP信号(reload命令)
master进程校验配置语法是否正确
master进程打开新的监听端口
master进程用新配置启动新的worker子进程
master进程向老worker子进程发送QUIT信号,老的worker对已建立连接继续处理,处理完才会优雅退出。未关闭的worker旧进程不会处理新来的请求
老worker进程关闭监听句柄,处理完当前连接后结束进程
平滑升级和回滚
有时候需要对Nginx版本进行升级以满足对其功能的需求,例如添加新模块,需要新功能,而此时Nginx 又在跑着业务无法停止,这时就可以选择平滑升级
平滑升级流程
平滑升级四个阶段
只有旧版nginx的master和worker进程旧版和新版nginx的master和worker进程并存,由旧版nginx接收处理用户的新请求(注意:做快照方便测试回滚)旧版和新版nginx的master和worker进程并存,由新版nginx接收处理用户的新请求只有新版nginx的master和worker进程
![图片[3] - nginx命令与信号:平滑升级与优雅关闭 - 宋马](https://pic.songma.com/blogimg/20251005/154ce672b51b42e886e8a1e3dc4bba42.png)
![图片[4] - nginx命令与信号:平滑升级与优雅关闭 - 宋马](https://pic.songma.com/blogimg/20251005/1b4e1d3efcd347168fd1923f4304bc67.png)
编译新版本,生成新版本的二进制文件
将旧Nginx二进制文件换成新Nginx程序文件(注意先备份旧版的二进制文件)
向master进程发送USR2信号启动新nginx进程
master进程修改pid文件名加上后缀.oldbin,成为nginx.pid.oldbin
将新生成的master进程的PID存放至新生成的pid文件nginx.pid
master进程用新Nginx二进制文件启动新master进程及worker子进程成为旧master的子进程
系统中将有新旧两个Nginx主进程和对应的worker子进程并存
当前新的请求仍然由旧Nginx的worker进程进行处理
向旧的Nginx服务进程发送WINCH信号,使旧的Nginx worker进程平滑停止,旧的nginxworker进程将不再接收新请求
当前新的请求由新Nginx的worker进程进行处理
旧的nginx master进程仍然存在
测试访问确认新版本是否正常工作
如果发现升级正常,向旧master进程发送QUIT信号,关闭旧master,并删除Nginx.pid.oldbin文件,旧版本的nginx彻底下线,新版本正式上线
如果发现升级有问题,可以回滚,向旧master发送HUP,旧版本的worker开始接收新请求,向新master发送QUIT
实现平滑升级和回滚
#下载最新稳定版
[root@rocky9 nginx-1.27.2]# wget https://nginx.org/download/nginx-1.27.2.tar.gz
[root@rocky9 nginx-1.27.2]# tar -xf nginx-1.27.2.tar.gz
[root@rocky9 nginx-1.27.2]# cd nginx-1.27.2/
#查看当前使用的版本及编译选项。结果如下:
[root@rocky9 nginx-1.27.2]# nginx -V
nginx version: nginx/1.26.2
built by gcc 11.5.0 20240719 (Red Hat 11.5.0-2) (GCC)
built with OpenSSL 3.2.2 4 Jun 2024
TLS SNI support enabled
configure arguments: --prefix=/app/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module
#configure arguments后面是以前编译时的参数。现在编译使用一样的参数
#开始编译新版本
[root@rocky9 nginx-1.27.2]# ./configure --prefix=/app/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module
#只要make无需要make install
[root@rocky9 nginx-1.27.2]# make
[root@rocky9 nginx-1.27.2]# objs/nginx -v
nginx version: nginx/1.27.2
#查看2个版本
[root@rocky9 nginx-1.27.2]# ll objs/nginx /app/nginx/sbin/nginx
-rwxr-xr-x 1 nginx nginx 5593992 Nov 24 20:16 /app/nginx/sbin/nginx
-rwxr-xr-x 1 root root 5638160 Nov 25 23:31 objs/nginx
#把之前的旧版的nginx命令备份
[root@rocky9 nginx-1.27.2]# cd /app/nginx/sbin/
[root@rocky9 sbin]# ls
nginx
[root@rocky9 sbin]# mv nginx nginx.bak
#把新版本的nginx命令复制过去覆盖旧版本程序文件,注意:需要加 -f 选项强制覆盖,否则会提示Text file busy
[root@rocky9 nginx-1.27.2]# cp -f objs/nginx /app/nginx/sbin/nginx
#检测一下有没有问题
[root@rocky9 sbin]# /app/nginx/sbin/nginx -t
nginx: the configuration file /app/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /app/nginx/conf/nginx.conf test is successful
[root@rocky9 sbin]# /app/nginx/sbin/nginx -v
nginx version: nginx/1.27.2
#发送信号USR2 平滑升级可执行程序,将存储有旧版本主进程PID的文件重命名为nginx.pid.oldbin,并启动新的nginx
#此时两个master的进程都在运行,只是旧的master不在监听,由新的master监听80
#此时Nginx开启一个新的master进程,这个master进程会生成新的worker进程,这就是升级后的Nginx进程,此时老的进程不会自动退出,但是当接收到新的请求不作处理而是交给新的进程处理。
[root@rocky9 ~]# kill -USR2 `cat /app/nginx/run/nginx.pid`
#可以看到两个master,新的master是旧版master的子进程,并生成新版的worker进程
[root@rocky9 ~]# ps aux|grep nginx
root 39036 0.0 0.1 11048 2456 ? Ss 20:12 0:00 nginx: master process /app/nginx/sbin/nginx -c /app/nginx/conf/nginx.conf
nginx 39037 0.0 0.2 15380 5016 ? S 20:12 0:00 nginx: worker process
nginx 39038 0.0 0.2 15380 5016 ? S 20:12 0:00 nginx: worker process
nginx 39039 0.0 0.2 15380 5016 ? S 20:12 0:00 nginx: worker process
root 42257 0.1 0.3 11064 6528 ? S 23:44 0:00 nginx: master process /app/nginx/sbin/nginx -c /app/nginx/conf/nginx.conf
nginx 42258 0.0 0.2 15396 5016 ? S 23:44 0:00 nginx: worker process
nginx 42259 0.0 0.2 15396 4888 ? S 23:44 0:00 nginx: worker process
nginx 42260 0.0 0.2 15396 5016 ? S 23:44 0:00 nginx: worker process
root 42262 0.0 0.1 6408 2176 pts/2 S+ 23:44 0:00 grep --color=auto nginx
[root@rocky9 ~]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 39036 root 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 39037 nginx 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 39038 nginx 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 39039 nginx 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 42257 root 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 42258 nginx 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 42259 nginx 6u IPv4 112229 0t0 TCP *:http (LISTEN)
nginx 42260 nginx 6u IPv4 112229 0t0 TCP *:http (LISTEN)
#做一下快照,方便做回滚
#先关闭旧nginx的worker进程,而不关闭nginx主进程方便回滚
#向原Nginx主进程发送WINCH信号,它会逐步关闭旗下的工作进程(主进程不退出),这时所有请求都会由新版Nginx处理
[root@rocky9 ~]# kill -WINCH `cat /app/nginx/run/nginx.pid.oldbin`
[root@rocky9 ~]# pstree -p
systemd(1)─┬─NetworkManager(695)─┬─{NetworkManager}(712)
│ └─{NetworkManager}(716)
├─agetty(758)
├─atd(745)
├─auditd(666)─┬─sedispatch(668)
│ ├─{auditd}(667)
│ └─{auditd}(669)
├─chronyd(38853)
├─crond(1665)
├─dbus-broker-lau(690)───dbus-broker(694)
├─irqbalance(698)───{irqbalance}(702)
├─lsmd(699)
├─mcelog(700)
├─nginx(39036)───nginx(42257)─┬─nginx(42258)
│ ├─nginx(42259)
│ └─nginx(42260)
#经过一段时间测试,新版本服务没问题,最后发送QUIT信号,退出老的master
[root@rocky9 ~]# kill -QUIT `cat /app/nginx/run/nginx.pid.oldbin`
#查看版本是否升级
[root@rocky9 ~]# nginx -v
nginx version: nginx/1.27.2
[root@rocky9 ~]# curl -I 192.168.101.5
HTTP/1.1 200 OK
Server: nginx/1.27.2
Date: Mon, 25 Nov 2024 15:54:14 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Sun, 24 Nov 2024 12:16:29 GMT
Connection: keep-alive
ETag: "6743191d-267"
Accept-Ranges: bytes
#回滚
#如果升级的版本发现问题需要回滚,可以发送HUP信号,重新拉起旧版本的worker
[root@rocky9 ~]# ps auxf|grep nginx
root 42300 0.0 0.1 6408 2176 pts/0 S+ 23:47 0:00 _ grep --color=auto nginx
root 39036 0.0 0.1 11048 2456 ? Ss 20:12 0:00 nginx: master process /app/nginx/sbin/nginx -c /app/nginx/conf/nginx.conf
nginx 39037 0.0 0.2 15380 5016 ? S 20:12 0:00 _ nginx: worker process
nginx 39038 0.0 0.2 15380 5016 ? S 20:12 0:00 _ nginx: worker process
nginx 39039 0.0 0.2 15380 5016 ? S 20:12 0:00 _ nginx: worker process
root 42257 0.0 0.3 11064 6528 ? S 23:44 0:00 _ nginx: master process /app/nginx/sbin/nginx -c /app/nginx/conf/nginx.conf
nginx 42258 0.0 0.2 15396 5016 ? S 23:44 0:00 _ nginx: worker process
nginx 42259 0.0 0.2 15396 4888 ? S 23:44 0:00 _ nginx: worker process
nginx 42260 0.0 0.2 15396 5016 ? S 23:44 0:00 _ nginx: worker process
[root@rocky9 ~]# kill -HUP `cat /app/nginx/run/nginx.pid.oldbin`
[root@rocky9 ~]# pstree -p|grep nginx
|-nginx(39036)-+-nginx(42257)-+-nginx(42258)
| | |-nginx(42259)
| | `-nginx(42260)
| |-nginx(39037)
| |-nginx(39038)
| |-nginx(39039)
| |-nginx(42317)
| |-nginx(42318)
| `-nginx(42319)
#最后关闭新版的master
[root@rocky9 ~]# kill -3 `cat /app/nginx/run/nginx.pid`
[root@rocky9 ~]# pstree -p|grep nginx
|-nginx(39036)-+-nginx(39037)
| |-nginx(39038)
| |-nginx(39039)
| |-nginx(42317)
| |-nginx(42318)
| -nginx(42319)
#恢复旧版的文件
[root@rocky9 sbin]# mv nginx.bak nginx
mv: overwrite 'nginx'? y
[root@rocky9 ~]# nginx -v
nginx version: nginx/1.26.2
结论
通过上述步骤,你可以实现Nginx的平滑升级,从而避免因软件更新导致的服务中断。平滑升级不仅保证了服务的连续性,还为管理员提供了灵活的维护窗口,使得系统管理和维护更加高效可靠。希望这篇指南能够帮助你在维护Web服务器时更加得心应手!

















暂无评论内容