nginx热升级流程和原理
使用Nginx时,经常遇到添加新模块或升级Nginx版本。
如何做到不停机升级部署?
一、升级操作过程
下载需要升级的nignx源码包
nginx: download
查看原先编译的参数。-V可以查看编译时的参数。(-v时查看版本)
在新编译时,要将原有的模块参数也加上,否则原有的模块不会编译进去。
./niginx -V
开始编译,编译过程
./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/
make
替换和备份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/
热升级。至此我们只是替换了二进制文件,但是现在服务中的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还在
只是没有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)。