说明
此前线上正在运行的Nginx版本因为漏洞扫描或者其他原因需要对Nginx版本进行升级。需要满足以下条件:
- 因为线上项目正在运行,所以不能进行服务停止
- 需要编译相同的模块
Nginx命令和信号
nginx命令格式
nginx [-?hvVtTp] [-s signal] [-c filename] [-p prefix] [-g directives]
选项说明:
-? -h :帮助
-c :指定使用的配置文件
-g :指定配置指令
-p :指定运行目录
-t -T :测试配置文件是否具有语法错误
-v :输出nginx版本
-V :输出nginx版本及编译信息
-s :nginx程序发出控制信号
nginx信号选项
nginx -s signal
signal信号种类如下:
- stop:停止服务,等同于SIGTERM,SIGINT
- quit:优雅的停止服务,等同于SIGQUIT
- reload:重新加载配置文件,等同于SIGHUP
- reopen:重新开始记录日志文件,等同于SIGUSR1,配置logrotate日志切割时可通过此信号重新生成日志文件。
- SIGUSR2:平滑升级可执行程序,设定新的子进程开始接受用户的访问请求,旧的子进程将已接收的任务处理完成后停止运行。通过
kill -USR2 PID
发送。 - SIGWINCH:优雅的停止工作进程。通过
kill -WINCH PID
发送。
平滑升级步骤
此次升级测试将从1.20.0升级至1.24.0版本
备份
$ cd /usr/local/nginx/sbin/
$ cp nginx{,.bak}
$ ls
nginx nginx.bak
查看nginx编译情况
$ nginx -V
nginx version: nginx/1.20.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=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module
编译新的nginx程序
# 下载新的nginx二进制包
$ cd /usr/local/src && wget https://nginx.org/download/nginx-1.24.0.tar.gz
$ tar xvf nginx-1.24.0.tar.gz
$ cd nginx-1.24.0
# 根据上面查出来的编译信息重新编译
$ ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module
$ make # 注意,此处执行后不要执行make install !!!
# make操作后会在objs目录下生成新的nginx程序
$ ls objs/nginx
objs/nginx
程序替换
查看程序当前的进程情况,因为环境上还有docker启动的nginx,所以过滤了一下,筛选出有用的信息。
$ ps -ef | egrep "/usr/local/nginx/sbin/nginx|^nginx" | grep -v grep
nginx 57259 79215 0 Jun19 ? 00:05:56 nginx: worker process
nginx 57260 79215 0 Jun19 ? 00:08:27 nginx: worker process
nginx 57261 79215 0 Jun19 ? 00:11:32 nginx: worker process
nginx 57262 79215 0 Jun19 ? 00:15:07 nginx: worker process
nginx 57263 79215 0 Jun19 ? 00:20:26 nginx: worker process
nginx 57264 79215 1 Jun19 ? 00:34:31 nginx: worker process
nginx 57265 79215 1 Jun19 ? 00:55:50 nginx: worker process
nginx 57266 79215 2 Jun19 ? 01:17:04 nginx: worker process
root 79215 1 0 May10 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
需要记住上方master进程的pid:79215
替换nginx程序
$ chown nginx:nginx objs/nginx
$ cp -f objs/nginx /usr/local/nginx/sbin/nginx
cp: overwrite ‘/usr/local/nginx/sbin/nginx’? yes
# 向旧master进程发送USR2信号,让其平滑升级使用新的进行来接收处理用户的请求。
$ kill -USR2 79215
# 再次查看当前进程情况,可以发现启动了两个master进程,和不同的worker进程。新的master进程是旧master进程的子进程。
$ ps -ef | egrep "/usr/local/nginx/sbin/nginx|^nginx" | grep -v grep
nginx 57259 79215 0 Jun19 ? 00:06:00 nginx: worker process
nginx 57260 79215 0 Jun19 ? 00:08:33 nginx: worker process
nginx 57261 79215 0 Jun19 ? 00:11:39 nginx: worker process
nginx 57262 79215 0 Jun19 ? 00:15:17 nginx: worker process
nginx 57263 79215 0 Jun19 ? 00:20:38 nginx: worker process
nginx 57264 79215 1 Jun19 ? 00:34:52 nginx: worker process
nginx 57265 79215 1 Jun19 ? 00:56:19 nginx: worker process
nginx 57266 79215 2 Jun19 ? 01:17:54 nginx: worker process
root 77298 79215 0 17:12 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx 77299 77298 0 17:12 ? 00:00:00 nginx: worker process
nginx 77300 77298 0 17:12 ? 00:00:00 nginx: worker process
nginx 77301 77298 0 17:12 ? 00:00:00 nginx: worker process
nginx 77302 77298 0 17:12 ? 00:00:00 nginx: worker process
nginx 77303 77298 2 17:12 ? 00:00:00 nginx: worker process
nginx 77304 77298 1 17:12 ? 00:00:00 nginx: worker process
nginx 77305 77298 1 17:12 ? 00:00:00 nginx: worker process
nginx 77306 77298 0 17:12 ? 00:00:00 nginx: worker process
root 79215 1 0 May10 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
# 向旧的master进程发送WINCH信号,使其关闭旧的worker进程
$ kill -WINCH 79215
$ ps -ef | egrep "/usr/local/nginx/sbin/nginx|^nginx" | grep -v grep
root 77298 79215 0 17:12 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx 77299 77298 0 17:12 ? 00:00:00 nginx: worker process
nginx 77300 77298 0 17:12 ? 00:00:01 nginx: worker process
nginx 77301 77298 0 17:12 ? 00:00:02 nginx: worker process
nginx 77302 77298 0 17:12 ? 00:00:02 nginx: worker process
nginx 77303 77298 1 17:12 ? 00:00:03 nginx: worker process
nginx 77304 77298 1 17:12 ? 00:00:03 nginx: worker process
nginx 77305 77298 2 17:12 ? 00:00:05 nginx: worker process
nginx 77306 77298 2 17:12 ? 00:00:05 nginx: worker process
root 79215 1 0 May10 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
# 此时可以看到master进程还是保留了两个,但是旧的worker进程已经被关闭了。新的worker进程是新的master进程的子进程。
测试和关闭旧master进程
此时进行测试和版本确认。
$ 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: --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module
# 测试无问题后关闭旧master进程
$ kill -quit 79215
总结
nginx平滑升级主要流程:
- 备份nginx旧的程序文件。
- 查看nginx配置编译安装的模块。
- 下载需要的nginx二进制文件包。
- 根据旧版本编译参数进行编译,只编译不安装(不要执行make install)。
- 复制新编译的nginx程序到旧的nginx程序路径进行覆盖。
- 查看旧的nginx主进程的PID。
- 向旧的nginx主进程发送USR2信号。
- 向旧的nginx主进程发送WINCH信号。
- 查看nginx版本,确认升级成功无问题后,向旧的nginx主进程发送
kill -quit PID
信号。
如果升级失败(未停止旧的nginx主进程情况下),那么参考下方的平滑回滚主要流程:
- 将备份的旧版本nginx程序文件覆盖新版本nginx程序文件。
- 向旧版本nginx主进程发送HUB信号(不能使用reload),此时会将旧的nginx子进程拉起。
- 向新的nginx主进程发送USR2信号,此时将使用旧版本的nginx处理用户请求。
- 向新的nginx主进程发送WINCH信号,关闭新的nginx子进程。
- 向新的nginx主进程发送QUIT信号,停止新的nginx主进程。