Nginx热升级流程和原理详解

使用Nginx时,经常遇到添加新模块或升级Nginx版本。如何做到不停机升级部署?本文将讨论热升级的详细步骤和相关机制。

1.热升级步骤

热升级大致分为两步,按照新的要求编译好Nginx的二进制文件,通过信号,完成新老进程的平滑过渡,保证升级期间服务可用。

热升级要升级的部分一般是要添加新的模块或者升级Nginx版本。前者如果是编译第三方模块需要先准备好模块源代码,对于后者需要先下载最近Nginx源码。

  • 第一步:先查看原先编译的参数。-V可以查看编译时的参数。(-v时查看版本)

    在新编译时,要将原有的模块参数也加上,否则原有的模块不会编译进去。

./nginx -V
  • 第二步:开始编译,编译过程和上篇一致,只是增加了要添加模块的参数。
./configure  --add-module=/xx/nginx-xx-moudule --with-compat --with-file-aio  
make

这里通过–add-module可以编译第三方的模块到Nginx。 这里只需要make编译,不要make install安装。

编译完成后的nginx二进制文件在objs目录。一会要用到。

  • 第三步:备份和替换
#先备份二进制执行文件
cd /usr/local/nginx/sbin/
cp nginx nginx.bak
#将新编译的二进制nginx文件考到现在的目录并覆盖原先的nginx
cp -r nginx /usr/local/src/nginx/sbin/ -f
  • 第四步:热升级。至此我们只是替换了二进制文件,但是现在服务中的Nginx进程还是由原来的nginx二进制文件启动的,所以请求还是走原有的逻辑。
#先通过ps -ef查看Nginx master进程id
#给nginx的master进程发送一个信号:USR2
kill -USR2 13195
#向老nginx进程发送一个信号:WINCH 让其优雅关闭所有的worker进程
kill -WINCH 13195

此时所有的请求都会平滑过渡到新的worker进程。但是旧的master进程13195还在
只是没有worker进程,如果要回退,只需要拉回旧的nginx拉回worker进程。如果运行一段时间没有问题,可以通过kill -QUIT 13159彻底关闭老进程

回滚:
kill -HUP 13195 #拉回原来的nginx进程 (kill -HUP 和 -SIGHUP 作用是一样的)
kill -QUIT 18034#关闭新起的master进程,该master进程会通知它下面的worker进程关闭

至此热升级完毕,其中最关键的是Nginx通过信号来协调新老进程的过渡。那么上边个指令生效的详细过程是怎样?这就是下面要讨论的。

热升级流程:

1.将旧Nginx文件换成新的Nginx文件(注意备份)

2.像master进程发送USER2信号会执行下面的动作:

  • master进程修改/logs/nginx.pid文件名(该文件是用来记录启动Nginx后的master进程号),给其加后缀.obin。因为新起的进程会生成一个nginx.pid文件,所以会修改原有文件的名称。这样做可以在回滚时找到对应的进程号。

  • master进程用新Nginx文件启动新的master进程

3.发送WINCH信号,关闭原有旧worker进程

4.向老进程发送QUIT信号,关闭老master(3,4步也可以合成4一步,这样在关闭老master进程时,老master进程会通知自己的worker进程关闭)

5.如果需要回滚:向老master发送HUP,向新master发送QUIT

2.Nginx中的信号管理

Nginx是一个多进程应用,一般多进程通信可以采用共享内存、信号等通信方式。nginx的主进程和worker进程之间使用信号通信。开发者也会通过主动发送信号,控制nginx的行为,比如上面的热部署。

能够发送和处理信号的有Master进程、worker进程以及nginx命令行。通常我们不直接给worker进程发送信号,而是给master进程发送信号,希望通过master进程管理worker进程(master进程和worker进程之间也会互发信号进行通信)。

在信号名前加SIG也可以。比如 kill -HUP 和kill -SIGHUP一样。

在这里插入图片描述

其中发送信号的方式有两种

1.通过 kill -HUP 12392这种方式直接向进程发送信号。

2.通过nginx命令行方式:nginx -s reload

第二种实际上就是利用logs目录下的nginx.pid读取进程id然后发送对于的信号,本质一样。

上面红色标识的信号只能通过kill -命令直接发送给对应进程。而没有对应的nginx命令。

信号作用介绍
  • CHLD:当worker进程出现异常关闭时,会给master进程发送该信号,master进程收到信号会重启worker进程

  • TERM, INT: 这两个信号都是立即停止服务,而不会等待已连接的tcp处理完请求

  • QUIT: 优雅的停止服务,不会立刻断开用户的tcp连接

  • HUP: 重载配置文件

  • USR1: 重新打开日志文件,可以做日志文件的切割

  • USR2: 启动新的master主进程

  • WINCH: 让master进程优雅的关闭所有的worker进程。

发和收不是完全对等,有些信号,nginx只能处理,不能发送,有些则既可以发送也可以处理。

3.reload流程(热重启)

子进程可以共享使用父进程已经打开的端口。

reload流程:

1.向master进程发送HUP信号(等同reload命令)

2.master进程校验配置语法是否正确

3.master进程打开新的监听端口,

4.master进程用新配置启动新的worker子进程

5.master进程向老worker子进程发送QUIT信号

6.老worker进程关闭监听句柄,处理完当前连接后结束进程

说明:

在新的配置文件里,如果我们监听了新的端口,master会打开这个监听端口,以便新起的worker进程进行监听和任务处理

老worker进程正常情况下,在处理已连接的请求后,会优雅退出,有时会出现老worker进程长时间存在,但是也只是影响已经建立的连接。

监听句柄的理解:

即调用过listen的socket。另外:子进程会自动共享父进程被listen的socket。句柄可以理解就是 socket,或者叫fd文件句柄。

集群问题:

如果是频繁修改upstream集群信息,那么不建议使用reload方式,你的修改目标非常简单明确,而reload是重新对所有配置生效,建议使用openresty实现API服务,由API来直接修改upstream信息。

端口问题:

新配置中关闭的端口,reload之后也不会关闭,因为master进程不会关闭端口。

4.Nginx的模块是什么?

热部署我们新增部分模块,那模块在nginx中是什么概念?

我们可以通过./configure --with --without --add-module= 等命令将官方模块或者第三方模块编译进Nginx,可以在编译后的objs目录中的ngx_modules.c文件中查看,在ngx_modules数组中列出了哪些模块编译进了Nginx模块。

在objs/src/下会有该模块的c源文件,其中的ngx_command_t中会定义相关的指令,所以即使文档中没有说明,这里也可以查到。

通过./nginx -V 可以查看编译时的配置参数 -v可以查看版本。

模块分类:

Nginx模块总体分为核心模块和子模块。核心模块中都是某种类别的核心模块,比如http, mail,stream等核心模块,而每一类核心模块下面又有很多子模块,其中xx_core模块是该类别的核心模块,定义了该类模块的通用功能。所以Nginx非常灵活,扩展性非常好。

每种类别的模块中的子模块有一定的顺序,比如core模块是第一位,因为定义了通用的行为属性等。

Http模块:

http是最复杂的一类模块,其子模块分为了三类,请求处理模块,响应过滤模块(带filter关键字),upstream相关模块(和上游服务器交互,带upstream)。

【nginx模块分类】

在nginx-1.16.1/src中的源码中(该源码是下载的,里边有官方的相关模块,编译时会选择编译哪些模块进入nginx)在这里有相关核心模块的源代码,比如http模块中,http根目录中放的是nginx的框架代码和http核心模块的源代码,modules中放的是官方提供的http子模块。


版权声明:本文为gexiaoyizhimei原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。