nginx命令与信号:平滑升级与优雅关闭

在本篇博客文章中,我们将介绍如何在不中断服务的情况下对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命令与信号:平滑升级与优雅关闭 - 宋马
图片[2] - nginx命令与信号:平滑升级与优雅关闭 - 宋马
利用 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命令与信号:平滑升级与优雅关闭 - 宋马
图片[4] - nginx命令与信号:平滑升级与优雅关闭 - 宋马
编译新版本,生成新版本的二进制文件
将旧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服务器时更加得心应手!

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容