Nginx高可用不停机升级

Nginx通过信号量对服务进行管理,以完成优雅的更新升级操作,帮助Nginx达成高可用的目标。

Nginx信号量

Nginx主进程支持的信号量

可以通过以下信号对Nginx主进程进行管理。

TERM, INT  快速关机
QUIT       优雅的关机
HUP        更改配置,跟上更改的时区(仅适用于FreeBSD和Linux),
           使用新配置启动新的工作进程,正常关闭旧工作进程
USR1       重新打开日志文件
USR2       升级可执行文件
WINCH      优雅地关闭工作进程

Nginx工作进程支持的信号量

不一定需要,但确定可以通过以下信号量控制单个工作进程。

TERM, INT    快速退出进程
QUIT         优雅退出进程
USR1         重新打开日志文件
WINCH        调试异常终止(需要启用debug_points)

修改nginx配置

Nginx默认会把主进程的进程ID写入到/usr/local/nginx/logs/nginx.pid文件中,可以在配置nginx.conf中使用pid指令更改此名称 。

为了让nginx重新读取配置文件,应将HUP信号发送到主进程。主进程首先检查语法有效性,然后尝试应用新配置,即打开日志文件和新的侦听socket。如果此操作失败,则会回滚修改并继续使用旧的配置。

如果成功,将启动新的工作进程,并向旧工作进程发送消息,请求它们正常关闭。旧工作进程关闭侦听socket,并继续为已经连接的客户端提供服务。在服务完所有已连接的客户端后,旧工作进程将退出。

通过一个例子来说明这一点,假如在Linux上为nginx运行如下命令

ps axw -o pid,ppid,user,%cpu,vsz,wchan,command | egrep'(nginx | PID)'

输出如下所示:

PID PPID USER%CPU VSZ WCHAN COMMAND 
33126 1 root 0.0 1148 pause nginx:master process /usr/local/nginx/sbin/nginx 
33127 33126 nobody 0.0 1380 kqread nginx:worker process(nginx)
33128 33126 nobody 0.0 1364 kqread nginx:worker process(nginx)
33129 33126 nobody 0.0 1364 kqread nginx:worker process(nginx)

如果将HUP发送到主进程,则输出如下所示:

PID PPID USER%CPU VSZ WCHAN COMMAND
33126 1 root 0.0 1164 pause nginx:master process /usr/local/nginx/sbin/nginx 
33129 33126 nobody 0.0 1380 kqread nginx:工作进程正在关闭 (nginx)
33134 33126 nobody 0.0 1368 kqread nginx:worker process(nginx)
33135 33126 nobody 0.0 1368 kqread nginx:worker process(nginx)
33136 33126 nobody 0.0 1368 kqread nginx:worker process(nginx)

PID 33129的旧工作进程之一仍在继续工作。

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 1368 kqread nginx:worker process (nginx)
33136 33126 nobody 0.0 1368 kqread nginx:worker process (nginx)

Nginx实时升级

升级Nginx时,应使用新版本的可执行文件代替旧版本的文件(nginx)。之后,将USR2信号发送到主进程。主进程首先使用进程ID,将进程文件重命名为带有.oldbin后缀的新文件,例如 /usr/local/nginx/logs/nginx.pid.oldbin,然后启动一个新的可执行文件(nginx 服务),该文件再启动新的工作进程:

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)

之后,所有的工作进程(新旧进程)继续接受请求。如果将WINCH信号发送到第一个主进程,它将向工作进程发送消息,请求它们正常关闭,工作进程将开始退出:

PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33135 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (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)

一段时间后,只有新的工作进程在处理请求:

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)

Nginx不会关闭旧的主进程

在Nignx更新升级事项中需要注意的是,旧的主进程不会关闭其侦听的sockets,所以能够在需要时,通过管理信号再次启动它所属的工作进程。比如,由于某种原因新的可执行文件无法达到预期,或升级无法接受(想找回旧版nginx服务),可以利用这一点将Nginx工作进程恢复到旧版本。

Nginx回退到旧版本服务

如果对Nginx的升级结果不满意时,可以通过如下操作步骤,让Nginx服务回退到旧版本。

首先,将HUP信号发送给旧版本的主进程,旧版本的主进程将启动新的工作进程,此时无需重新读取配置。

然后,将QUIT信号发送到新版本的主进程,使其进入正常关闭流程。

最后,将TERM信号发送到新版本的主进程,它会向其工作进程发送一条消息,要求它们立即退出,这些工作进程一般都会立即退出,如果由于某种原因新进程没有退出,则通过发送KILL信号以强制它们退出。

当新版本的主进程退出时,旧版本的主进程将自动创建新进程进行处理。如果新版本的主进程成功退出,则旧版本的主进程会将旧pid文件中.oldbin后缀去掉。

如果升级成功,应将QUIT信号发送到旧版本的主进程,只保留新版本进程:

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)