利用Nginx信号实现平滑升级
Nginx 命令和信号
Nginx 命令
可以通过nginx命令向nginx进程发送信号,实现各种功能
nginx 命令格式
nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-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,在升级版本时使用
范例:查看nginx 使用帮助
[root@centos7 ~]# nginx -h
nginx version: nginx/1.18.0
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-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: /apps/nginx/)
-c filename : set configuration file (default: conf/nginx.conf)
-g directives : set global directives out of configuration file
quit 信号
quit信号运行流程
- 设置定时器: worker_shutdown_timeout #一定时间后关闭work进程,防止超时。
- 关闭监听句柄。 #不再接收新的连接请求
- 关闭空闲连接
- 在循环中等待全部连接关闭 #待连接任务执行完毕后关闭连接
- 退出nginx所有进程
范例:quit 信号停止nginx进程
[root@centos7 html]# pwd #进入nginx安装目录下
/apps/nginx/html
[root@centos7 html]# dd if=/dev/zero of=/apps/nginx/html/bigfile bs=1M count=1024 #生成1G的大文件bigfile
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 6.78492 s, 158 MB/s
[root@centos7 html]# ll -h bigfile
-rw-r--r-- 1 root root 1.0G Jul 21 17:37 bigfile
[root@centos7 html]# hostname -I #Nginx服务器地址
172.16.16.88
reload 信号
reload信号运行流程
利用 reload 可以实现平滑修改配置并生效
- 向master进程发送HUP信号(reload命令)
- master进程校验配置语法是否正确
- master进程打开新的监听端口
- master进程用新的配置启动新的worker子进程
- master进程向老worker子进程发送QUIT信号通知旧的worker处理完当前连接后退出,空闲worker旧进程不再处理新来的请求
- 旧worker进程关闭监听句柄,处理完当前连接后结束进程
范例:想nginx发送reload信号实现不停机更新配置
[root@centos7 ~]# nginx #启动nginx
[root@centos7 ~]# ps -ef|grep nginx #当前有一个master进程和一个worker进程
root 20091 1 0 17:51 ? 00:00:00 nginx: master process nginx
nginx 20092 20091 0 17:51 ? 00:00:00 nginx: worker process
root 20094 19981 0 17:51 pts/1 00:00:00 grep --color=auto nginx
[root@centos7 ~]# cat /apps/nginx/conf/nginx.conf | grep processes #当前配置nginx的worker进程数为1
worker_processes 1;
[root@centos7 ~]# sed -i '/worker_processes/c\worker_processes 3;' /apps/nginx/conf/nginx.conf #将worker_processes 的值改为3
[root@centos7 ~]# grep worker_processes /apps/nginx/conf/nginx.conf
worker_processes 3;
[root@centos7 ~]# nginx -t && nginx -s reload #检查配置并发送reload信号
nginx: the configuration file /apps/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /apps/nginx/conf/nginx.conf test is successful
[root@centos7 ~]# ps -ef | grep nginx #nginx的worker进程数量为3
root 20091 1 0 17:51 ? 00:00:00 nginx: master process nginx
nginx 20158 20091 0 18:05 ? 00:00:00 nginx: worker process
nginx 20159 20091 0 18:05 ? 00:00:00 nginx: worker process
nginx 20160 20091 0 18:05 ? 00:00:00 nginx: worker process
root 20162 19981 0 18:05 pts/1 00:00:00 grep --color=auto nginx
reopen信号
reopen 信号重写日志文件
1、nginx 当前将日志记录在access.log文件
2、将access.log 重命名为access.log.old,日志仍记录到access.log.old文件中(因为Nginx已持有此文件的描述符)
3、新建access.log文件,向nginx发送reopen信号,使得新的日志记录在新生成的文件中(用于实现日志轮转)
Nginx 平滑升级和回滚
工作中有时候需要对Nginx版本进行升级以满足对其功能的需求,例如添加新模块,需要新功能,而此时
Nginx又在运行着重要业务不能直接停掉。在此背景下可选择平滑升级nginx
平滑升级流程
平滑升级流程
- 将旧Nginx二进制文件换成新Nginx程序文件(注意先备份)
- 向master进程发送USR2信号,master进程自动修改pid文件名加上后缀.oldbin,成为nginx.pid.oldbin
- master进程用新Nginx文件启动新master进程成为旧master的子进程,系统中将有新旧两个Nginx主进程,新生成的master进程的PID存放至新生成的pid文件nginx.pid
- 主进程共同提供Web服务,当前新的请求仍然由旧Nginx的worker进程进行处理
- 向旧的Nginx服务进程发送WINCH信号,使旧的Nginx worker进程平滑停止
- 向旧master进程发送QUIT信号,关闭老master,进程自动删除Nginx.pid.oldbin文件
- 如果发现升级有问题,可以回滚∶向老master发送HUP,向新master发送QUIT
平滑升级和回滚案例
#当前nginx版本及安装路径
[root@centos7 nginx]# nginx -v
nginx version: nginx/1.18.0
[root@centos7 nginx]# which nginx
/apps/nginx/sbin/nginx
#获取老版本编译参数 --prefix
[root@centos7 ~]# nginx -V
nginx version: nginx/1.18.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: --prefix=/apps/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
#下载一个较新版本,使用上一步获得的编译参数进行编译
[root@centos7 ~]# wget http://nginx.org/download/nginx-1.20.1.tar.gz
[root@centos7 ~]# tar xf nginx-1.20.1.tar.gz
[root@centos7 ~]# cd nginx-1.20.1/
[root@centos7 nginx-1.20.1]# ./configure --prefix=/apps/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
[root@centos7 nginx-1.20.1]# make #不需要执行make install
[root@centos7 nginx-1.20.1]# objs/nginx -v #查看编译结果
nginx version: nginx/1.20.1
#备份旧版nginx程序(用于回滚)
[root@centos7 ~]# mv /apps/nginx/sbin/{nginx,nginx.old}
#将编译后的新版nginx拷贝到旧版安装路径下
[root@centos7 ~]# cp ./nginx-1.20.1/objs/nginx /apps/nginx/sbin/
[root@centos7 ~]# ll /apps/nginx/sbin/
total 15308
-rwxr-xr-x 1 root root 7893488 Jul 21 18:49 nginx
-rwxr-xr-x 1 root root 7774224 Jul 21 00:17 nginx.old
#验证替换结果
[root@centos7 ~]# /apps/nginx/sbin/nginx -t
nginx: the configuration file /apps/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /apps/nginx/conf/nginx.conf test is successful
[root@centos7 ~]# /apps/nginx/sbin/nginx -v
nginx version: nginx/1.20.1
#nginx 默认将pid文件存放于/app/nginx/logs/路径下
[root@centos7 ~]# grep pid /apps/nginx/conf/nginx.conf
#pid logs/nginx.pid;
[root@centos7 ~]# ll /apps/nginx/logs/nginx.pid
-rw-r--r-- 1 root root 6 Jul 21 19:01 /apps/nginx/logs/nginx.pid
#查看当前nginx进程
[root@centos7 ~]# ps -ef | grep nginx
root 23503 1 0 19:06 ? 00:00:00 nginx: master process nginx
nginx 23504 23503 0 19:06 ? 00:00:00 nginx: worker process
root 23545 23521 0 19:07 pts/1 00:00:00 grep --color=auto nginx
#向旧版nginx发送USR2信号
[root@centos7 ~]# kill -USR2 `cat /apps/nginx/logs/nginx.pid`
#此时有两个master进程共存,其中一个master是新版nginx生成的。由新版nginx负责监听处理新的请求,旧版nginx不再处理。
[root@centos7 ~]# ps -ef | grep nginx
root 23503 1 0 19:06 ? 00:00:00 nginx: master process nginx
nginx 23504 23503 0 19:06 ? 00:00:00 nginx: worker process
root 23547 23503 0 19:07 ? 00:00:00 nginx: master process nginx
nginx 23548 23547 0 19:07 ? 00:00:00 nginx: worker process
root 23550 23521 0 19:07 pts/1 00:00:00 grep --color=auto nginx
#新版本master进程是旧版nginx进程的子进程
[root@centos7 ~]# pstree -p| grep nginx
|-nginx(23503)-+-nginx(23547)---nginx(23548)
| `-nginx(23504)
#查看端口监听情况,两个版本都在监听80端口,但实际的请求是由旧版nginx的worker进程处理
[root@centos7 ~]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 23503 root 6u IPv4 191644 0t0 TCP *:http (LISTEN)
nginx 23504 nginx 6u IPv4 191644 0t0 TCP *:http (LISTEN)
nginx 23547 root 6u IPv4 191644 0t0 TCP *:http (LISTEN)
nginx 23548 nginx 6u IPv4 191644 0t0 TCP *:http (LISTEN)
#pid文件也生成了新旧两个版本
[root@centos7 ~]# ll /apps/nginx/logs/
total 20
-rw-r--r-- 1 nginx root 172 Jul 21 18:48 access.log
-rw-r--r-- 1 root root 258 Jul 21 18:19 access.log.old
-rw-r--r-- 1 nginx root 1879 Jul 21 19:07 error.log
-rw-r--r-- 1 root root 6 Jul 21 19:07 nginx.pid
-rw-r--r-- 1 root root 6 Jul 21 19:06 nginx.pid.oldbin
。
#向旧版nginx主进程发送WINCH信号,它会逐步关闭所有worker进程(主进程不退出,方便回滚),而后所有请求将由新版nginx处理
[root@centos7 ~]# kill -WINCH `cat /apps/nginx/logs/nginx.pid.oldbin`
[root@centos7 ~]# pstree -p| grep nginx
|-nginx(23503)---nginx(23547)---nginx(23548)
[root@centos7 ~]# ps -ef | grep nginx
root 23503 1 0 19:06 ? 00:00:00 nginx: master process nginx
root 23547 23503 0 19:07 ? 00:00:00 nginx: master process nginx
nginx 23548 23547 0 19:07 ? 00:00:00 nginx: worker process
root 23598 23521 0 19:22 pts/1 00:00:00 grep --color=auto nginx
#如果此时新版本出现问题,可执行回滚操作
####################回滚######################
#向旧版nginx发送HUP信号,重新生成work进程处理新的请求
[root@centos7 ~]# kill -HUP `cat /apps/nginx/logs/nginx.pid.oldbin`
#关闭新版nginx进程
[root@centos7 ~]# kill -QUIT `cat /apps/nginx/logs/nginx.pid`
#####################回滚####################
#经过一段时间后,如果新版nginx正常运行,就可以关闭旧版nginx主进程master
[root@centos7 ~]# kill -QUIT `cat /apps/nginx/logs/nginx.pid.oldbin`
[root@centos7 ~]# ps -ef | grep nginx
root 23547 1 0 19:07 ? 00:00:00 nginx: master process nginx
nginx 23548 23547 0 19:07 ? 00:00:00 nginx: worker process
root 23603 23521 0 19:27 pts/1 00:00:00 grep --color=auto nginx
#版本升级成功
[root@centos7 ~]# nginx -v
nginx version: nginx/1.20.1
[root@centos7 ~]# curl localhost &> /dev/null && echo $?
0