做web开发和做运维的都知道nginx是一个非常优秀的web服务器和反向代理服务器,它优秀的多进程架构保证其稳定的运行,还为我们提供了热升级功能(不影响客户端请求的处理)。
1. nginx热升级介绍
nginx热升级也就是在不影响客户端请求(服务不中断)的情况下使用新的nginx二进制文件替换老的nginx二进制文件在停掉老的进程。
这种热升级得益于nginx的多进程架构设计,能够在老的nginx主进程不退出的情况下以子进程的方式启动新的master进程和新的woker进程,让新的master进程重新监听端口和接收请求。新的master进程稳定之后,通过发送信号的方式通知老master进程退出。完成整个热升级。
2. nginx热升级流程
2.1 准备新的nginx二进制文件
通过编译得到新的nginx二进制文件,注意为了能够完成升级编译时必须保持和老的nginx配置文件路径、日志路径、pid文件路径等保持一致,否则会导致nginx配置失效!
以下面的我的演示环境为例,目前我的nginx版本以及配置路径如下:
$ ./nginx -v
nginx version: nginx/1.22.0
prefix=/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二进制文件。
至此准备工作已经完成。
2.2 替换老的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
2.3 通知老的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版本了!
2.4 退出老的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 ./nginxnobody 8268 8267 0 20:08 ? 00:00:00 nginx: worker process is shutting downnobody 8692 8267 0 20:57 ? 00:00:00 nginx: worker process is shutting down
正常情况,当worker进程处理完请求就会连带老的master进程一起退出。
至此nginx热升级完成。
3. nginx热升级流程总结
一般使用下面的流程:
- 备份老的准备nginx二进制文件
- 准备新的nginx二进制文件,新的nginx二进制各配置路径保持一致
- 向老的nginx进程发送“SIGUSR2 (12)”信号,启动新的进程(新老进程并存)
- 向老的nginx进程发送“SIGQUIT (3)”信号停掉老的nginx进程