首先让我们了解一下各种信号的意义,我们后面会用到其中的几个:
TERM,INT 快速退出
QUIT 优雅退出(等待所有连接关闭后再退出程序,不接受新的连接)
HUP 在修改配置后,以新的配置启动worker进程,优雅退出旧的worker进程
USR1 重新打开日志文件
USR2 更新二进制文件
WINCH 优雅地关闭worker进程(但不关闭master)
接下来是步骤:
1. 编译、获得新的二进制文件
这一步很简单,新版本Nginx或者旧版本Nginx源码加上新的编译参数,在经过configure
和make
后,我们在源码目录下的objs目录中找到新的nginx二进制文件。
注意,接下来请不要make install
。
2. 备份与替换
将旧的nginx二进制文件备份一下:
cd /usr/local/nginx/sbin
mv nginx nginx.old
然后将上一步得到的新版本二进制文件复制过来:
cp /path/to/source/nginx /usr/local/nginx/sbin/
3. USR2、WINCH与QUIT
发送USR2信号给master进程
kill -USR2 `cat /usr/local/nginx/logs/nginx.pid`
这一步发生了什么?
master进程首先重命名PID文件,在文件名后添加.oldbin
后缀,比如nginx.pid
会被重命名为nginx.pid.oldbin
。接着依次启动新的执行文件和新的worker进程。这时候我们观察系统的进程,可以看到有两个master进程:
PID PPID USER %CPU VSZ WCHAN COMMAND
33126 1 root 0.0 1164 pause nginx: master process /usr/local/nginx/sbin/nginx
33134 33126 nobody 0.0 1368 kqread nginx: worker process (nginx)
33135 33126 nobody 0.0 1380 kqread nginx: worker process (nginx)
33136 33126 nobody 0.0 1368 kqread nginx: worker process (nginx)
36264 33126 root 0.0 1148 pause nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36266 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36267 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
注意这两个master进程的PID:第一个进程的PPID是1,这是旧master进程,而新master进程的PPID就是旧master进程的PID,这说明新的master来自于旧的master。
这时候,新旧两种worker同时接受连接请求。
发送WINCH给旧master
接下来,发送WINCH信号给旧的master进程来“优雅”地关闭旧的Nginx worker:
kill -WINCH `cat /usr/local/nginx/logs/nginx.pid.oldbin`
过一段时间,我们观察系统进程:
PID PPID USER %CPU VSZ WCHAN COMMAND
33126 1 root 0.0 1164 pause nginx: master process /usr/local/nginx/sbin/nginx
36264 33126 root 0.0 1148 pause nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36266 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36267 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
新旧两个master进程都在,但是worker都是来自于新master。
那么为什么我们不直接用-QUIT退出旧master呢?因为假如这时我们发现新worker进程因为一些原因无法接受请求,那我们就能快速启动旧Nginx。
快速回退的方法见最后。
QUIT退出旧master
升级很成功,新worker正常接受请求,那么我们就可以关掉旧master了:
kill -QUIT `cat /usr/local/nginx/logs/nginx.pid.oldbin`
观察系统进程:
PID PPID USER %CPU VSZ WCHAN COMMAND
36264 1 root 0.0 1148 pause nginx: master process /usr/local/nginx/sbin/nginx
36265 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36266 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
36267 36264 nobody 0.0 1364 kqread nginx: worker process (nginx)
我们看到,新master进程其PPID已经变成了1。
4. 快速回退
如果我们在发送WINCH给旧master进程后发现新master无法正常工作,我们需要能够快速回退。
我们一般用两种方案来快速回退。
- 发送HUP信号给旧master进程。旧master进程会在不重新读取配置文件的情况下直接启动新的worker进程。然后就可以发送QUIT信号给新master进程,使所有新进程可以优雅退出。
- 发送TERM信号给新master进程。新master进程发送信号关闭所有的worker进程(如果因为某些原因新的进程没有退出,就得发送KILL信号强制退出了)。当新master进程退出后,旧master进程会自动启动它的worker进程。