nginx短篇(10):热升级

  • A+
所属分类:Nginx  运维技术

  

博主会将与Nginx有关的知识点总结到"nginx短篇系列"文章中,如果你对nginx不是特别了解,请按照顺序阅读"nginx短篇系列",以便站在前文的基础上理解新的知识点。

 

前文中,我们总结了nginx中各种信号的用法,并且提到,当我们想要热升级编译安装的nginx时,会用到USR2信号和WINCH信号,那么,这篇文章就来通过实例,详细的演示一下这两个信号的用法。

 

此处将要演示的过程是一个热升级的过程,所谓的热升级也叫做热部署,或者平滑升级,也就是说,在不停止nginx服务的情况下,完成nginx的升级工作。

 

但是这里有一个注意点,就是使用这种方法有一个前提,前提就是你在启动nginx时使用的是nginx二进制文件的绝对路径,而不是直接在命令行中输入"nginx"的方式启动的nginx服务,不通过绝对路径启动的方式通常是为了方便,配置了nginx相关的环境变量,如果没有通过绝对路径启动nginx,那么当你向nginx进程发送更新的信号时,nginx进程可能会无法找到新的二进制程序(由于没有找新版本的二进制程序,所以没有任何反应),有可能会在更新时,在日志中发现如下错误:

此处为了能够正常的演示整个过程,使用nginx二进制文件的绝对路径运行启动命令。

 

我先大概的描述一下整个升级过程,以便你在脑海中有一个大的框架,详细步骤后文再行演示。

假设nginx正在提供服务,一切正常,现在我们想要对nginx进行热升级,大致步骤如下

0、最重要的一步,备份。

1、下载新版本的nginx,根据老版本的编译选项,对新版本完成编译的步骤,只对新版本进行编译操作,不执行安装操作,换句话说就是,只执行make命令,不执行make install命令,完成编译操作后,即可获取到我们需要的新版本的二进制文件,之后,我们需要根据实际情况判断哪些文件需要被替换,此处描述的"根据情况判断"在后文中会有解释,先不用纠结,此处假设,根据情况判断后,只需要替换nginx二进制文件。

2、确定已经备份老版本的nginx二进制文件,以防万一,用编译好的新版本的nginx二进制文件替换老版本的nginx二进制文件,此时老版本的nginx仍然在内存中正常运行,所以不用担心,我们替换的只是硬盘中的二进制文件,做好备份即可。

3、对nginx的master进程(正在运行的老版本的master进程)发送USR2信号,老版本的master进程收到信号后,会通过新版本的二进制文件启动新版本的master进程,新版本的master进程会启动新版本的worker进程,此时新老版本的nginx进程同时存在。

4、向老版本的master进程发送WINCH信号,以便先优雅的停止老版本的worker进程,新的请求会被新版本的worker进程处理,此时老的master进程仍然存在,留下老的master进程是为了以防万一,以便随时回滚,此时老版本的master进程、新版本的master进程和新版本的worker进程同时存在,升级过程暂且完毕。

5、如果升级后万一出现问题,则可以随时进行回滚,由于老版本的master进程并未停止,所以我们可以向老的master进程发送HUP信号,即可通过老版本的master进程重新生成老版本的worker进程,当老版本的worker进程重新被拉起后,即可向新版本的master进程发送QUIT信号,以便优雅的关闭新版本的nginx进程,回滚操作完毕。

 

上述过程就是热升级编译安装的nginx的整个过程,如果你没有进行过实际的操作,可能会有一些疑问,不如我们一起来动手操作一遍。

 

在开始操作之前,请确保你已经完成了备份工作。

 

我的nginx在正常运行,当前nginx的版本是1.14.2

 

现在,我想要热升级nginx,于是,我下载了当前的新版本,新版本的版本号为1.15.9

 

我们现在要做的工作是,根据老版本的编译选项,对新版本的nginx进行编译,这么做是为了与老版本的设置保持一致,以便之前的设置和文件能够继续使用、保持不变,使用nginx -V命令查看老版本的编译选项,我的编译选项如下:

上述返回信息中,"configure arguments:" 对应的一长串文本就是1.14.2版本的编译设置。

 

我们已经获取到了旧版本的nginx的编译设置,现在我们需要使用同样的设置对新版本的nginx源代码进行编译,步骤如下:

如下configure命令使用的编译选项就是旧版本中使用的选项

设置完成后,执行make命令开始进行编译

注意:此处只进行编译步骤,不执行安装步骤,也就是说,只执行"make"命令,不执行"make install"命令,因为"make install"命令的本质就是将编译好的文件复制到对应的目录中,所以此处省略"make install"命令,以防止某些老的配置文件被覆盖,因为我们只是为了升级nginx,原来的配置仍然需要保持不变,所以要确保配置文件不会被覆盖,但是经过我个人的实际测试,即使执行了"make install"命令,常用的配置文件也都不会被覆盖,如果执行"make install"命令,相关脚本会先判断对应的配置文件是否存在,如果存在,则不会覆盖原来的配置文件,但是为了保险起见,此处仍然不执行"make install"命令。

 

完成上述步骤后,新版本的nginx二进制文件已经编译完成了,现在我们要做的就是使用新版本的二进制程序替换掉老版本的二进制程序,新版本的二进制文件生成在编译目录的objs目录中,即objs/nginx文件。

 

但是,此处有一个注意点,需要注意的是,有可能不仅需要替换二进制程序文件,还需要替换一些别的文件,比如动态库文件,网上的教程大多数只会替换二进制程序文件,而不会操作其他文件,这可能是由于历史原因造成的,在早期的nginx版本中,并不支持动态模块,所有模块都是直接编译到二进制程序文件中的,即所有模块都直接编译到nginx文件中,当我们想要升级时,只需要升级nginx二进制文件就可以了,但是在现在的版本中,有几个模块是支持编译成动态模块的,也就是说,这些模块会以动态库的形式存在,细心如你一定发现了,在我的编译设置中,有几个模块使用了动态模块的方式,比如"--with-http_geoip_module=dynamic",这就表示我把http_geoip_module以动态模块的形式编译了,所以,你除了能在objs目录中找到nginx二进制文件,还能在objs目录中看到一些以".so"结尾的动态库文件,比如ngx_http_geoip_module.so,如果你的情况跟我一样,在编译时使用了动态模块,那么当你想要升级nginx时,最好记得将对应的动态模块也升级了,当然,如果你的编译设置中没有任何动态模块,那么你就不用考虑这么多了,只升级替换nginx二进制文件就好了。

 

现在,将新版本的相关文件拷贝到对应的目录中,再次强调,覆盖任何旧文件之前请先备份。

我已经将老版本的二进制文件和老版本的动态模块文件进行了备份(备份以.old结尾),并且将新版本的文件拷贝到了对应的目录中

还是那句话,如果你没有使用动态模块,你压根看不到modules目录,所以也不用备份它们,只需要备份好二进制文件就行了,总之就是要根据实际情况,随机应变。

 

虽然我们使用新的文件替换了旧版本的文件,但是我们并没有运行这些文件,也就是说,现在在内存中运行的nginx进程,仍然是老版本的nginx,查看一下当前运行的nginx进程,如下:

可以看到,当前nginx进程的主进程号是1837 ,也就是说,老版本的nginx的master进程的PID是1837 ,而且从老版本的master进程信息中可以看到,我使用的启动命令是" /srv/nginx/sbin/nginx",这是一个绝对路径命令。

注意:此时不要使用nginx -v命令查看nginx版本, 因为nginx二进制文件已经替换成了1.15.9版本的nginx二进制文件,所以,如果此时使用nginx -v命令查看版本,得到的版本号必定是1.15.9,但是实际上,内存中运行的nginx进程版本仍然是1.14.2,如果你想要确定此刻内存中真正工作的nginx进程的版本号,可以故意访问一个不存在的资源,默认情况下,nginx的404页面中会显示nginx进程的版本号(除非你修改了相关设置),如下图所示,我故意访问了一个不存在的资源,可以看出,当前内存中的nginx版本号仍然是1.14.2

nginx短篇(10):热升级

现在,我们来搞清楚一下状况,我们已经使用新版本的文件替换了老版本的文件,但是我们并没有运行新版本的程序,内存中的nginx进程仍然是老版本的nginx进程,所以,我们需要使用新版本的程序启动新的进程,此时,就需要用到USR2信号了,此刻,我们只需要向老版本的nginx的master进程发送USR2信号,老master进程就会使用新版本的二进制文件来启动新的master进程,示例如下:

向老版本master进程发送USR2信号

老版本master进程收到USR2信号后,会根据老版本master进程中的nginx启动路径(绝对路径),启动一个新的master进程,由于nginx二进制文件已经替换为新版本的二进制文件,所以,新启动的nginx进程就是新版本的,再次查看nginx相关进程,如下:

如上述信息所示,此时新老版本的nginx进程同时存在,老版本的master进程的PID为1837,老版本的worker进程的PID为1838,新版本的master进程的PID为5625,它的父进程的PID为1837,也就是说,这个新版本的master进程是由老版本的master进程启动的,新版本的worker进程的PID是5626,它是由新版本的master进程生成的。

 

此时,新老版本的nginx进程同时存在,但是我们的最终目的是使用新版本的nignx提供服务,于是,我们需要先优雅的停止老版本的worker进程,此时就需要用到"WINCH"信号了,当老版本的master进程接收到"WINCH"信号后,会停止老版本的worker进程,但是老版本的master进程并不会停止,我们留下老版本的master进程是为了以防万一,具体操作如下:

向老版本master进程发送WINCH信号后,再次查看nginx进程,如下:

如你所见,老版本的worker进程已经优雅的停止了(优雅的停止是在处理完当前连接的请求后再行停止),但是老版本的master进程还在,此时,再次故意访问一个不存在的资源,会发现nginx的版本号已经发生了改变(最好刷新一下浏览器缓存),如下:

nginx短篇(10):热升级

如上图所示,nginx的版本号已经变成了1.15.9,升级完成。

 

如果一切顺利,那么万事大吉,你已经完成了你的升级工作,但是如果出现问题,你可以随时通过老的master进程从新启动一个老版本的worker进程,然后优雅的停止新版本的nginx进程,操作如下:

注:如果有动态库文件,最好提前做好还原工作

从上述过程可以看出,当我们向老版本的master进程发送HUP信号后,老版本的master进程启动了一个老版本的worker进程,PID为5969,老版本的worker进程启动后,我们向新版本的master进程发送了QUIT信号,以便优雅的停止新版本的nginx进程,新版本的nginx进程停止后,再次查看nginx进程,只剩下老版本的nginx进程了,此时,回滚完成。

 

通过上述操作,你应该已经掌握了怎样热升级nginx,无非也就是如下几步:

1、备份

2、根据原编译参数编译新版本

3、使用新版本替换老版本文件

4、发送信号,启动新版本进程

5、优雅的停止老版本

 

上述过程是纯手动的一个过程,你可以自己动手做一遍,然后就会彻底理解每一步的作用了。

 

除了上述方法,还有另一种方法也可以升级nginx,当你编译了新版本的nginx,并且用新版本的文件替换了老版本的文件后,可以直接在新版本的nginx的编译目录中执行如下命令(即在make命令完成后,替换老版本的相关文件后执行如下命令):

# make upgrade

执行"make upgrade"命令其实就是相当于执行了一段脚本,这段脚本会自动向老版本的master进程发送USR2信号,以便启动新版本的nginx进程,并且在新版本的nginx进程启动后,优雅的停止老版本的nginx进程。

当编译操作完成后,nginx的编译目录中会自动生成Makefile,打开编译目录中的Makefile文件,查看其中的内容,你会发现,执行"make upgrade"命令其实就是执行了如下命令

执行"make upgrade"命令后,并不会像我们纯手动操作那样,留下老版本的master进程,而是直接优雅的停止掉所有的老版本的nginx进程,如果你有把握,你也完全可以这样干。

 

其实,如果你的nginx在编译时没有使用动态模块,那么在升级时是非常方便的,不过无论怎样,我觉得都应该先在测试机中模拟一遍,以便发现问题,随机应变。

此处贴出官网的升级过程,以便参考:

https://www.nginx.com/resources/wiki/start/topics/tutorials/commandline/#upgrading-to-a-new-binary-on-the-fly

 

这篇文章就先总结到这里,希望能够对你有所帮助。

weinxin
我的微信公众号
关注"实用运维笔记"微信公众号,当博客中有新文章时,可第一时间得知哦~
朱双印

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

目前评论:7   其中:访客  6   博主  1

    • avatar da西瓜 0

      大佬,催更啦

      • avatar MJ 1

        能否出一篇 upsteam的文章 大神

        • avatar Tim 0

          麻烦问下,后面还会更新nginx的文章吗?

            • avatar 朱双印 Admin

              @Tim 会有,但是不确定更新时间,加油,共勉~

            • avatar aixipip 0

              非常详细!!!请问大佬,后面的博客会涉及nginx+lua配置吗

              • avatar 我是粉丝 0

                说实在的,热升级,属于实战内容吗?常用吗?