vlambda博客
学习文章列表

nginx热升级流程和原理

使用Nginx时,经常遇到添加新模块或升级Nginx版本。

如何做到不停机升级部署?

一、升级操作过程

  1. 下载需要升级的nignx源码包

    nginx: download

  2. 查看原先编译的参数。-V可以查看编译时的参数。(-v时查看版本)

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

    ./niginx -V

    nginx热升级流程和原理

  3. 开始编译,编译过程

    ./configure ./configure --prefix=/usr/local/nginx-1.18.0/ --conf-path=/usr/local/nginx-1.18.0/conf/nginx.conf --lock-path=/usr/local/nginx-1.18.0/lock/nginx.lock --pid-path=/usr/local/nginx-1.18.0/nginx.pid --sbin-path=/usr/local/nginx-1.18.0/sbin/nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre --add-module=/usr/local/nchan-1.2.6/

    nginx热升级流程和原理

    make


  4. 替换和备份nignx二进制文件

    #先备份二进制执行文件

    cp /usr/local/nginx-1.18.0/sbin/nginx{,-bak}

    #将新编译的二进制nginx文件考到现在的目录并覆盖原先的nginx

    cp -rf /tmp/nginx-1.20.1/objs/nginx /usr/local/nginx-1.18.0/sbin/

  5. 热升级。至此我们只是替换了二进制文件,但是现在服务中的Nginx进程还是由原来的nginx二进制文件启动的,所以请求还是走原有的逻辑。

    #先通过ps -ef查看Nginx master进程id

    ps -ef|grep nginx

    #给nginx的master进程发送一个信号:USR2

    kill -USR2 104346

    #向老nginx进程发送一个信号:WINCH 让其优雅关闭所有的worker进程

    kill -WINCH 104346

    此时所有的请求都会平滑过渡到新的worker进程。但是旧的master进程104346还在

    nginx热升级流程和原理

    只是没有worker进程,如果要回退,只需要拉回旧的nginx拉回worker进程。如果运行一段时间没有问题,可以通过kill -QUIT 104346彻底关闭老进程


    回滚:

    kill -HUP 1404346

    #拉回原来的nginx进程 (kill -HUP 和 -SIGHUP 作用是一样的)

    kill -QUIT 104557

    #关闭新起的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


三、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只能处理,不能发送,有些则既可以发送也可以处理。


四、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进程不会关闭端口。


五、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)。