Skip to content

实战-nginx热部署-20250224(测试成功)

image-20250224065436193

nginx热部署

实战-nginx热部署-20250224(测试成功)

目录

[toc]

环境

bash
centos7.61810nginx-1.24.0.tar.gz

版权

本内容来自:《极客时间:Nginx 核心知识 150 讲》,版权归原作者所有,这里仅记录自己的学习笔记。

热升级流程

image-20250224065313192

image-20250224065436193

提示

nginx 热升级步骤:

  1. 将旧的nginx文件替换为新的nginx文件 命令:cp -f (注意新旧nginx文件指定的配置文件路径是一致,备份旧的nginx文件)
  2. 向旧的master 进程发送 USR2 信号。 (此时,master进程会修改pid文件名,加后缀.oldbin。紧接着,旧master进程用新的nginx文件启动新的master进程。)
  3. 再向旧的master进程发送QUIT信号,关闭旧master进程。(注意:当请求正常访问后才执行此操作)
  4. 回滚:向旧master 发HUP(老进程pid保存在pid.ildbin里) ,向新master发送QUIT。替换回旧的nginx文件(发送意外后操作)

老师步骤

因为热升级中,我们只会更换二进制文件,并不会更换其它文件。

对老版本nginx二进制文件做下备份:

bash
[root@localhost sbin]# pwd/home/geek/nginx/sbin[root@localhost sbin]# lltotal3660-rwxr-xr-x.1rootroot3746424Dec3007:21nginx[root@localhost sbin]# cp nginx nginx.old

准备下载新版本的nginx,编译安装,然后输出新版本的二进制文件:

拷贝新版本的二进制文件到刚才目录:

image-20250102072730672

image-20250102072743685

当前nginx进程:

image-20250102073539347

给nginx master发送一个USR2信号,我们开始要热部署了:它会利用新版本的nginx二进制文件新启一个master进程,然后再拉起worker进程。然后就会丝滑切到新版本里来。

image-20250102072926773

给nginx master老版本发送一个WINCH信号:

请优雅地关闭你的worker进程:

image-20250102073040007

老版本master是不会自动退出的,因为可能需要随时回退。


自己测试步骤:

1、下载新版本

https:[root@localhost ~]# cd nginx-1.24.0[root@localhost nginx-1.24.0]# mkdir -p /home/geek/nginx-new[root@localhost nginx-1.24.0]# ./configure --prefix=/home/geek/nginx-new[root@localhost nginx-1.24.0]# cd /home/geek/nginx-new/[root@localhost nginx-new]# lsconfhtmllogssbin[root@localhost nginx-new]# ll sbin/total3824-rwxr-xr-x.1rootroot3913616Jan207:42nginx[root@localhost nginx-new]#

2、备份

  • 备份老版本nginx二进制文件,拷贝新版本二进制文件到对应目录
bash
[root@localhost sbin]# pwd/home/geek/nginx/sbin[root@localhost sbin]# lltotal3660-rwxr-xr-x.1rootroot3746424Jan207:26nginx.old[root@localhost sbin]# cp ../../nginx-new/sbin/nginx .[root@localhost sbin]# lltotal7484-rwxr-xr-x.1rootroot3913616Jan207:43nginx-rwxr-xr-x.1rootroot3746424Jan207:26nginx.old[root@localhost sbin]# [root@localhost sbin]# ps -ef|grepnginxroot182451007:19?00:00:00nginx:masterprocess./nginxnobody1825018245007:19?00:00:00nginx:workerprocessroot2087518010007:44pts/100:00:00grep--color=autonginx[root@localhost sbin]#

3、热部署

bash
#给nginx master发送一个USR2信号,我们开始要热部署了:它会利用新版本的nginx二进制文件新启一个master进程,然后再拉起worker进程。然后就会丝滑切到新版本里来。[root@localhost sbin]# kill -USR2 18245[root@localhost sbin]# ps -ef|grepnginxroot182451007:19?00:00:00nginx:masterprocess./nginxnobody1825018245007:19?00:00:00nginx:workerprocessroot2087718245007:45?00:00:00nginx:masterprocess./nginxnobody2087820877007:45?00:00:00nginx:workerprocessroot2088018010007:45pts/100:00:00grep--color=autonginx[root@localhost sbin]# #给nginx master老版本发送一个WINCH信号:请优雅地关闭你的worker进程:(老版本master是不会自动退出的,因为可能需要随时回退。)[root@localhost sbin]# kill -WINCH 18245[root@localhost sbin]# ps -ef|grepnginxroot182451007:19?00:00:00nginx:masterprocess./nginxroot2087718245007:45?00:00:00nginx:masterprocess./nginxnobody2087820877007:45?00:00:00nginx:workerprocessroot2088218010007:46pts/100:00:00grep--color=autonginx[root@localhost sbin]#

警告

bash
kill-USR218245#用户自定义信号2kill-WINCH18245#窗口大小改变,当终端或窗口大小发生变化时发出奇奇怪怪的说明。。。

当前自己进程如下:

bash
[root@localhost nginx]# ps -ef|grepnginxroot182451005:11?00:00:00nginx:masterprocess./nginxroot2087718245005:37?00:00:00nginx:masterprocess./nginxnobody2087820877005:37?00:00:00nginx:workerprocessroot2197321497007:21pts/000:00:00grep--color=autonginx[root@localhost nginx]# lsof -i:80 -NPCOMMANDPIDUSERFDTYPEDEVICESIZE/OFFNODENAMEnginx18245root6uIPv4896040t0TCP*:80(LISTEN)nginx20877root6uIPv4896040t0TCP*:80(LISTEN)nginx20878nobody6uIPv4896040t0TCP*:80(LISTEN)[root@localhost nginx]# netstat -antlp|grep80tcp000.0.0.0:800.0.0.0:*LISTEN18245/nginx:mastertcp00192.168.1.102:22192.168.1.1:55266ESTABLISHED18006/sshd:root@pttcp00192.168.1.102:22192.168.1.1:55284ESTABLISHED18050/sshd:root@pttcp00192.168.1.102:22192.168.1.1:55281ESTABLISHED18028/sshd:root@pt[root@localhost nginx]#

image-20250106072342509

4、杀掉老进程或者回退

处理方法:

如果新master运行没问题: 可以执行kill -QUIT old-nginx-master-pid把老master进程杀掉。

如果新master运行有问题,需要版本回退: 执行kill -HUP old-nginx-master-pid把老master reload了(不能用nginx -s reload,因为当前的nginx二进制文件已经是新的,不会对老的nginx起作用),这时候老master会起来自己的worker进程。

image-20250106210812349

FAQ

案例:加-f参数强制替换

(记得对老nginx的二进制文件做好备份)

cp objs/nginx /etc/nginx/sbin 报错:cp:cannot create regular file ‘/etc/nginx/sbin/nginx’:Text file busy 应该怎么办呀

作者回复:加-f参数

案例:老进程还在监听但不工作了哦

🍊案例:

老师,您好,我在做nginx热部署的时候遇到一个问题,您说,在发送USR2信号后,旧的nginx进程不再监听端口,但是我测试的时候,出现如下情况(新旧进程都在监听端口),麻烦您看一下,是哪里出了问题?

bash
[root@VM_0_5_centos sbin]# kill -USR2 11848[root@VM_0_5_centos sbin]# ps -ef |grepnginxroot118481016:34?00:00:00nginx:masterprocess./nginxnobody1184911848016:34?00:00:00nginx:workerprocessroot1831311848016:53?00:00:00nginx:masterprocess./nginxnobody1831418313016:53?00:00:00nginx:workerprocessroot183188534016:53pts/700:00:00grep--color=autonginx[root@VM_0_5_centos sbin]# lsof -i:8000 -NPCOMMANDPIDUSERFDTYPEDEVICESIZE/OFFNODENAMEnginx11848root6uIPv4586594720t0TCP*:8000(LISTEN)nginx11849nobody6uIPv4586594720t0TCP*:8000(LISTEN)nginx18313root6uIPv4586594720t0TCP*:8000(LISTEN)nginx18314nobody6uIPv4586594720t0TCP*:8000(LISTEN)

作者回复:旧nginx进程仍然在LISTEN,只是不会去处理这个socket,因为没有把它加到epoll中。master进程打开监听端口,但不处理,由worker进程处理。另外,旧master是新master的父进程,所以新master才能共享打开的监听端口。

是的,11848 就是nginx的master进程,虽然在监听,但实际不会处理新连接,因为fd已经从epoll中移出了。 保留master是为了方便回滚,可以发信号QUIT或者直接杀掉进程。


🍊案例:

问题:新老 master 进程同时存在,是不是同时有两个进程同时监听 80 端口吗?

作者回复:是的,但是监听不代表会处理。listen以后,还需要通过epoll_ctl加入到epoll中,进程才会处理。而master进程是不会执行这一步的,如果你熟悉源代码,你可以看ngx_event_process_init函数,它只有在worker进程才执行,它负责把监听的fd加入epoll

🍊案例:

我有一个问题是当新老master同时存在的时候,他们都可以接受请求。这个意思是说,他们可能监听同一个端口。从外部来的请求会到达哪个master,是操作系统随机决定吗?

作者回复:不是的,不可能同时监听同一端口,老master会把listen的句柄通过epoll_ctl从epoll中移除,不再监听80或者443端口。


🍊案例:

老师你好。

接老师的留言回复。

-------- 新连接只会被新worker处理,老连接仍然在老worker处理 --------

请问这里的新连接和老连接各代表的是什么意思?

我上一个留言中使用curl命令访问nginx节点的10次请求,都是在给旧版本的master进程发送-USR2信号之后执行的。

请问这10次请求是算新连接还是老连接?如果是新连接,为什么旧worker进程和新worker进程都会处理这些连接?

作者回复:当新worker进程启动后,新发起的连接都必须被新worker进程处理。注意,是新发起的TCP连接,有很多方法可以验证,例如**$connection变量**


🍊案例:

问题:

老师请教一下,热部署时,对老进程发送USR2信号后,老进程不再监听80或443端口,那么此时正在处理的http请求还能正常返回客户端吗?因为80或443端口已经不属于这个进程了,通过什么端口返回响应?

作者回复:不再监听,不代表已经建立的TCP连接被关闭了,所以正在处理的http请求仍然可以返回客户端,且报文的源端口仍然可以是80或者443,它与监听与否无关。


🍊案例:父子进程是能同时监听同一个端口的

为什么可以新旧一起listen?难道不会“bind:address already in use”

作者回复:因为是父子进程,所以可以.

案例:孤儿进程

问题:

kill -WINCH PID 之后 老的master成为了新的master的父进程,那就是说老的master进程要一直存在,不能杀死了吗 老师

作者回复:

可以杀死的,父进程杀死后,不会导致子进程也被杀死的,此时子进程的父进程为linux系统上的第1个进程。


知识点:

补充一个linux知识点:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。所以老master推出后,新的master并不会退出。

作者回复:谢谢分享!

案例:如何回滚

🍊案例:

热部署回退那里不是很明白。旧的nginx命令改为nginx.old,再把新的nginx命令复制过来,执行kill -USR2 master_old_pid后,会生成master_new_pid,再执行kill -WINCH master_old_pid后,旧master不再处理业务。

假设现在要回退

1,执行kill -USR1master_old_pid 就行了吗?

2,是否要把旧版本的二进制文件(即nginx命令)先回复为nginx.old,再执行kill -USR1 master_old_pid呢?

作者回复:首先,旧的master进程,就是用老的二进制文件启的。 所以,回退时,对旧master进程调用kill -SIGHUP(等同-s reload),这样老worker就又起来了。再把新worker退出。 这一课只是简单的示例,WINCH只是取消accept新连接,它适用于灵活的控制新老nginx是否处理新连接。详细的介绍请参见第2部分第25课。


🍊案例:

问下老的master进程什么时候关闭?关闭以后是不是就不能回滚了?

作者回复:

1、确认此次升级正常后,就可以关闭。

2、关闭以后不能回滚,如果想回滚,就需要再走一次热升级流程,用备份好的老nginx文件作为新的热升级文件。


🍊案例:

必须通过向老master进程发送信号SIGTERM或者SIGQUIT的方式才可以

陶老师请教下:当我跟着您的步骤执行完热部署的时候,我执行./nginx -s stop命令,然后新部署的nginx停止,然后我执行./nginx再次启动时提示我80端口被绑定,这里是原来旧的那个master进程又可以继续接受请求进行处理了嘛?

作者回复:热部署完成后,老master还在的话,必须通过向老master进程发送信号SIGTERM或者SIGQUIT的方式才可以,因为pid文件里保存的是新master进程的id,所以./nginx -s stop是在向新master进程发送SIGTERM信号,它关闭的是新nginx。所以此时你再执行./nginx,由于老master进程仍然存在,且使用了80端口,所以./nginx无法启动。


🍊案例:

作者回复:如果希望回滚,也就是老master进程长期存在,需要发送WINCH信号,而不是QUIT信号。

请优雅地关闭你的worker进程,老版本master是不会自动退出的,因为可能需要随时回退。


🍊案例:

热部署失败之后到底用kill -USR1呢,还是用kill -HUP呢?有点儿不明白,他们两个的问题把我搞糊涂了,或是老师给说说热部署失败之后如何回退呢?

作者回复:**USR1是切割日志的,对应reopen命令。**而HUP是对应reload命令,它会在没有worker进程时启动worker进程。所以,回退应该用HUP信号


🍊案例:

老师好,最后一步回滚的时候,直接向新master发送QUIT信号吗?此时新的worker进程还在的,不需要先向新的master进程发送WINCH信号关闭新的worker进程,然后在发送QUIT信号关闭新的master进程吗?

作者回复:不需要,向master进程发QUIT后,master会向其子进程worker也发送QUIT

案例:新版本编译,是否在同一台机器上?

热部署,在编译新版本的nginx的时候,编辑机器的操作系统和运行目录是不是必须和运行机器保持一致?

作者回复:是的。或者你也可以执行时通过-c指定不同的nginx.conf,但在生产环境中这样相对容易出错。

师好,有个问题没想明白。就是我们编译新版本操作系统环境最好是和运行老版本的操作系统环境保持一致,或者就是最好在同一台机器上编译。 但是如果在同一台机器上编译的话,编译后的目标目录应该不能和老版本的运行目录一致(会覆盖掉老版本正在使用的配置文件)。那么我们cp到运行目录下的新版本的二进制文件挂起master进程的时候是怎么知道读取哪个ng的配置文件呢?

作者回复:通常生产环境启动Nginx时,都会-c显示指定nginx.conf的

师好,热部署时,新版本的二进制文件是不是建议在另外一台主机上编译,以确保配置文件路径等一致?

作者回复:生产环境中运行机器与编译机器不能在同一台主机上,但不是因为这个原因。后续课程的例子中你可以看到,同一台主机编译运行一样可以保持配置文件路径一致的,只是不要执行make install


特别注意:

我下载了新的nginx ./configure --prefix=新目录 make,然后把编译好的sbin/nginx覆盖了以前的sbin/nginx, nginx -t显示 显示我的配置文件在新目录下。不是很理解

作者回复:--prefix指示的目录,被编译进nginx文件中了。

案例:之前的参数必须全部携带

如果想新编译某module到nginx, 之前已经编译的模块也要继续加在 --with 参数后面,还是只需要添加新模块的--with参数?

作者回复:之前的参数必须全部携带!! 因为,configure会清理掉Makefile文件,不会保留上次编译时携带的参数、选项信息。

案例:各信号的作用

提示

USER2信号 --老master进程(USR2信号用于升级nginx,它会以子进程方式启动另一个nginx。) WINCH信号 --老master进程(请优雅地关闭你的worker进程,老版本master是不会自动退出的,因为可能需要随时回退。) QUIT信号 --老master进程(如果新master运行没问题: 可以执行kill -QUIT old-nginx-master-pid把老master进程杀掉。 )

备注: USR1信号对应于reopen命令,用于重新打开文件,即切割日志用的;==reopen命令

HUP信号:HUP是对应reload命令,它会在没有worker进程时启动worker进程;

案例:直接替换nginx二进制再reload一次不行吗(不行)

热升级能不能直接使用nginx reload 直接操作呢

作者回复:不可以,reload命令会把worker进程更换,但更换的只是配置文件内容,而不是程序,且master进程也未更换(观察进程号),所以无法实现热升级(更换新版本nginx程序的内容)。

最近更新