不中断现有连接,平滑升级Nginx

不中断现有连接,平滑升级Nginx

首先让我们了解一下各种信号的意义,我们后面会用到其中的几个:
. 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/

  1. 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进程。

发表评论

电子邮件地址不会被公开。 必填项已用*标注