Nginx 平滑升级:从 1.26.3 到 1.28.0,用户无感知

nginx平滑升级

Nginx 平滑升级(又称热升级)是运维必会的技能之一。本文通过源码编译安装 Nginx 1.26.3,模拟用户正在下载大文件的场景,逐步升级到 1.28.0,全程下载不中断。文章涵盖:为什么 dnf 安装不行、USR2/WINCH/QUIT 三个信号的作用、新旧进程如何共用端口、以及升级失败如何回滚。手把手带你完成一次真正的零停机升级。

大概思路:

关于平滑升级,其核心在于:

服务从旧到新平滑切换,但用户无感知

所以需要模拟用户无感知:

这里用下载文件来模拟,规定带宽,确保时间够长足以完成nginx平滑升级

关于平滑升级的步骤:

1.有旧版本运行

2.新版本和旧版本共存

3.新版本替换旧版本服务

在这个过程中要保证下载文件不中断

旧版本nginx

练习平滑升级,建议卸载 dnf 版,改用源码编译安装

因为 dnf 安装的 nginx 没有提供平滑升级需要的信号机制控制。

简单说:

  • dnf 安装 :文件分散在系统目录,升级时会直接覆盖二进制、重启服务 ,无法手动发 USR2WINCHQUIT 信号来控制新旧进程共存。
  • 源码编译 :可以自定义安装路径(如 /opt/nginx),手动替换二进制、手动发信号,实现新旧共存、无感知切换
bash 复制代码
[root@bogon ~]# dnf remove -y nginx

#环境依赖
[root@bogon ~]#  dnf install -y gcc-c++ pcre-devel openssl-devel zlib-devel

#下载nginx包
[root@bogon ~]# wget https://nginx.org/download/nginx-1.26.3.tar.gz


#解压
[root@bogon ~]# tar -zxf nginx-1.26.3.tar.gz -C /usr/local/src/


#创建一个专门运行 nginx 的系统用户,并禁止其登录。
[root@bogon ~]# useradd  -M -r -s /sbin/nologin nginx
useradd: user 'nginx' already exists
[root@bogon ~]# id nginx
uid=995(nginx) gid=994(nginx) groups=994(nginx)
#应该是之前dnf安装自动创建了,可以直接用


#配置nginx服务
[root@bogon ~]# cd /usr/local/src/nginx-1.26.3/

[root@bogon nginx-1.26.3]# ./configure \
--prefix=/opt/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_sub_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-stream \
--with-stream_ssl_module \
--with-stream_realip_module

#编译&安装
[root@bogon nginx-1.26.3]# make && make install 


#启动服务
root@bogon nginx-1.26.3]# /opt/nginx/sbin/nginx 


#查看进程
[root@bogon nginx-1.26.3]# ps -ef | grep nginx
root        8112       1  0 15:30 ?        00:00:00 nginx: master process /opt/nginx/sbin/nginx
nginx       8113    8112  0 15:30 ?        00:00:00 nginx: worker process
root        8115    1597  0 15:31 pts/0    00:00:00 grep --color=auto nginx

[root@bogon nginx-1.26.3]# ps auxf | grep nginx
root        8119  0.0  0.1   6416  2280 pts/0    S+   15:31   0:00              \_ grep --color=auto nginx
root        8112  0.0  0.1  11872  2400 ?        Ss   15:30   0:00 nginx: master process /opt/nginx/sbin/nginx
nginx       8113  0.0  0.3  16108  5356 ?        S    15:30   0:00  \_ nginx: worker process

#可以看到master的pid为8112,而其子进程worker的pid为8113


#测试服务
[root@bogon nginx-1.26.3]# curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

/usr/local/src 就是"放源码的公共文件夹",方便统一管理手动编译的软件源码。

为什么要创建nginx用户和组?

源码安装不会自动创建nginx用户和组

为了安全,让 Nginx 的工作进程(Worker Process)不以 root 身份运行

关于启动服务

如果是rpm/dnf安装一般会有.service文件,可直接用systemctl命令来管理;

但源码安装则没有这个文件,源码安装的 nginx 在 /opt/nginx/ 目录下,systemd 不认识它,只能直接运行二进制文件启动。

不清楚是哪个文件可以找一下/opt/nginx/下面的目录,一般都是sbin/下面

关于进程查看

  • ps -ef | grep nginx:横向列出完整信息(标准格式)
  • ps auxf | grep nginx :纵向带树形结构,能看清父子关系(f 选项显示进程树)

准备下载文件

用 dd 造一个 10MB 的假视频文件,专门用来测试平滑升级时连接会不会断。

1.生成文件

bash 复制代码
[root@bogon ~]# cd /opt/nginx/html/
[root@bogon html]# ls
50x.html  index.html
[root@bogon html]# dd if=/dev/zero of=./download.avi bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.003529 s, 3.0 GB/s
[root@bogon html]# ll
total 10248
-rw-r--r--. 1 root root      497 Apr  8 15:20 50x.html
-rw-r--r--. 1 root root 10485760 Apr  8 15:41 download.avi
-rw-r--r--. 1 root root      615 Apr  8 15:20 index.html

2.下载文件

bash 复制代码
[root@bogon html]# wget --limit-rate=1K http://localhost/download.avi
--2026-04-08 15:47:24--  http://localhost/download.avi
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:80... failed: Connection refused.
Connecting to localhost (localhost)|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10485760 (10M) [video/x-msvideo]
Saving to: 'download.avi.1'

download.avi.1               0%[                                        ]   2.81K  1024 B/s    

新版本nginx

复制当前会话

bash 复制代码
#下载新版本压缩包
[root@bogon ~]# wget  https://nginx.org/download/nginx-1.28.0.tar.gz

#解压
[root@bogon ~]# tar -zxf nginx-1.28.0.tar.gz -C /usr/local/src

#配置

[root@bogon ~]# cd /usr/local/src/nginx-1.28.0/
[root@bogon nginx-1.28.0]# ls
auto     CHANGES.ru          conf       contrib          html     man        SECURITY.md
CHANGES  CODE_OF_CONDUCT.md  configure  CONTRIBUTING.md  LICENSE  README.md  src


[root@bogon nginx-1.28.0]# ./configure \
--prefix=/opt/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_sub_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-stream \
--with-stream_ssl_module \
--with-stream_realip_module

#编译
[root@bogon nginx-1.28.0]# make


#查看编译后的目录文件
[root@bogon nginx-1.28.0]# ls
auto     CHANGES.ru          conf       contrib          html     Makefile  objs       SECURITY.md
CHANGES  CODE_OF_CONDUCT.md  configure  CONTRIBUTING.md  LICENSE  man       README.md  src


#查看objs
[root@bogon nginx-1.28.0]# ll objs 
total 5800
-rw-r--r--. 1 root root   18360 Apr  8 15:50 autoconf.err
-rw-r--r--. 1 root root   54316 Apr  8 15:50 Makefile
-rwxr-xr-x. 1 root root 5776648 Apr  8 15:51 nginx
-rw-r--r--. 1 root root    5513 Apr  8 15:51 nginx.8
-rw-r--r--. 1 root root    8380 Apr  8 15:50 ngx_auto_config.h
-rw-r--r--. 1 root root     657 Apr  8 15:50 ngx_auto_headers.h
-rw-r--r--. 1 root root    9198 Apr  8 15:50 ngx_modules.c
-rw-r--r--. 1 root root   41040 Apr  8 15:51 ngx_modules.o
drwxr-xr-x. 9 root root      91 Apr  8 15:50 src

仅编译,一定不要安装:

make install 会覆盖配置文件、静态文件等,可能导致配置丢失。平滑升级只需要替换二进制文件。

关于objs目录:

objs 目录是 Nginx 源码编译过程中生成的中间文件和目标文件的存放目录

包含:

  • 编译产生的 .o 对象文件
  • 最终的 nginx 可执行二进制文件
  • 依赖关系文件、模块链接文件等编译产物

平滑升级

1.查看当前运行的nginx版本

bash 复制代码
[root@bogon nginx-1.28.0]# /opt/nginx/sbin/nginx -v
nginx version: nginx/1.26.3

[root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11317  0.0  0.1   6416  2280 pts/1    S+   16:01   0:00              \_ grep --color=auto nginx
root        8112  0.0  0.1  11872  2400 ?        Ss   15:30   0:00 nginx: master process /opt/nginx/sbin/nginx
nginx       8113  0.0  0.3  16108  5736 ?        S    15:30   0:00  \_ nginx: worker process
[root@bogon nginx-1.28.0]# 

还是旧版本

2.备份旧版本的nginx可执行文件

直接移动就可以。

nginx 服务已经在内存中运行了,可执行文件只是磁盘上的一个文件。不会影响当前nginx服务

bash 复制代码
[root@bogon nginx-1.28.0]# mkdir -p /bak/nginx
[root@bogon nginx-1.28.0]# mv /opt/nginx/sbin/nginx /bak/nginx

3.用新版本替代旧版本

就是将新版本的可执行文件复制到 /opt/nginx/sbin/ 目录下

bash 复制代码
[root@bogon nginx-1.28.0]# cp -f objs/nginx /opt/nginx/sbin/

#查看当前nginx进程pid
[root@bogon nginx-1.28.0]# cat /opt/nginx/logs/
access.log  error.log   nginx.pid   
[root@bogon nginx-1.28.0]# cat /opt/nginx/logs/nginx.pid 
8112
#查看当前进程
root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11351  0.0  0.1   6416  2284 pts/1    S+   16:06   0:00              \_ grep --color=auto nginx
root        8112  0.0  0.1  11872  2400 ?        Ss   15:30   0:00 nginx: master process /opt/nginx/sbin/nginx
nginx       8113  0.0  0.3  16108  5736 ?        S    15:30   0:00  \_ nginx: worker process
[root@bogon nginx-1.28.0]# 

nginx进程pid仍然是旧版本

4.新旧共存

发送USR2信号给当前运行nginx进程,

让旧版 nginx 主进程启动新版 nginx 进程,实现新旧共存。

bash 复制代码
[root@bogon nginx-1.28.0]# kill -USR2 `cat /opt/nginx/logs/nginx.pid`
[root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11392  0.0  0.1   6416  2276 pts/1    S+   16:11   0:00              \_ grep --color=auto nginx
root        8112  0.0  0.1  11872  2812 ?        Ss   15:30   0:00 nginx: master process /opt/nginx/sbin/nginx
nginx       8113  0.0  0.3  16108  5736 ?        S    15:30   0:00  \_ nginx: worker process
root       11389  0.0  0.4  11908  7332 ?        S    16:11   0:00  \_ nginx: master process /opt/nginx/sbin/nginx
nginx      11390  0.0  0.3  16160  5476 ?        S    16:11   0:00      \_ nginx: worker process
[root@bogon nginx-1.28.0]# ls /opt/nginx/logs/
access.log  error.log  nginx.pid  nginx.pid.oldbin
[root@bogon nginx-1.28.0]# 

5.关闭旧nginx的worker进程

关闭旧版本的 worker 进程,保留旧 master。

旧 master 还在,但它的 worker 全没了,不再处理新请求。新请求全由新版本 worker 处理。

为什么要这样:

  • 旧 worker 退出 → 新版本完全接管服务
  • 旧 master 保留 → 万一新版本有问题,可以随时唤醒旧 worker 回滚
bash 复制代码
[root@bogon nginx-1.28.0]# kill -WINCH `cat  /opt/nginx/logs/nginx.pid.oldbin`
[root@bogon nginx-1.28.0]# 
[root@bogon nginx-1.28.0]# ls /opt/nginx/logs/
access.log  error.log  nginx.pid  nginx.pid.oldbin
[root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11398  0.0  0.1   6416  2284 pts/1    S+   16:13   0:00              \_ grep --color=auto nginx
root        8112  0.0  0.1  11872  2816 ?        Ss   15:30   0:00 nginx: master process /opt/nginx/sbin/nginx
root       11389  0.0  0.4  11908  7332 ?        S    16:11   0:00  \_ nginx: master process /opt/nginx/sbin/nginx
nginx      11390  0.0  0.3  16160  5476 ?        S    16:11   0:00      \_ nginx: worker process

6.退出旧nginx的master

彻底退出旧版本的 master 进程,完成升级。

只剩下新版本 master 和它的 worker; nginx.pid.oldbin文件被删除(或不再存在)

bash 复制代码
[root@bogon nginx-1.28.0]# kill -QUIT `cat /opt/nginx/logs/nginx.pid.oldbin`
[root@bogon nginx-1.28.0]# 
[root@bogon nginx-1.28.0]# ls /opt/nginx/logs/
access.log  error.log  nginx.pid
[root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11402  0.0  0.1   6416  2288 pts/1    S+   16:13   0:00              \_ grep --color=auto nginx
root       11389  0.0  0.4  11908  7332 ?        S    16:11   0:00 nginx: master process /opt/nginx/sbin/nginx
nginx      11390  0.0  0.3  16160  5476 ?        S    16:11   0:00  \_ nginx: worker process
[root@bogon nginx-1.28.0]# 

7.查看当前nginx服务版本

此时新nginx已经完全接手,查看当前nginx服务的版本号肯定是新版本。

bash 复制代码
[root@bogon nginx-1.28.0]# /opt/nginx/sbin/nginx -v
nginx version: nginx/1.28.0

检查下载窗口,没有中断

bash 复制代码
download.avi.1                         16%[+++++++++++                                                             ]   1.65M  1024 B/s    eta 2h 22m 

至此, Nginx 服务器的平滑升级完成。

8.还原之前的版本

利用旧版本可执行文件的备份即可

复制代码
TERM 信号会优雅退出(处理完当前请求再退)
如果想强制退出可以用 kill -9,但一般不推荐
bash 复制代码
#停止新版本nginx服务
[root@bogon nginx-1.28.0]# kill -TERM `cat /opt/nginx/logs/nginx.pid`

#查看进程-->无
[root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11407  0.0  0.1   6416  2284 pts/1    S+   16:19   0:00              \_ grep --color=auto nginx


#将备份放在/opt/nginx/sbin/nginx(nginx 可执行文件的位置)
[root@bogon nginx-1.28.0]# cp -f /bak/nginx/nginx /opt/nginx/sbin/nginx 
cp: overwrite '/opt/nginx/sbin/nginx'? y


#启动
[root@bogon nginx-1.28.0]# /opt/nginx/sbin/nginx 

#查看版本和进程
[root@bogon nginx-1.28.0]# /opt/nginx/sbin/nginx -v
nginx version: nginx/1.26.3

[root@bogon nginx-1.28.0]# ps auxf | grep nginx
root       11453  0.0  0.1   6416  2280 pts/1    S+   16:25   0:00              \_ grep --color=auto nginx
root       11436  0.0  0.1  11872  2408 ?        Ss   16:21   0:00 nginx: master process /opt/nginx/sbin/nginx
nginx      11437  0.0  0.3  16108  5336 ?        S    16:21   0:00  \_ nginx: worker process
[root@bogon nginx-1.28.0]# 
#发现pid和之前新版本不一样

这里下载文件也没有中断,还原完成

一些相关问题:

Q1:平滑升级和热部署的区别?

平滑升级 :换版本(二进制文件),新旧共存切换
热部署 :不换版本,只重载配置(kill -HUP

Q2:为什么 USR2 后两个 master 能共用同一个端口?

新版 nginx 启动时会从旧 master 继承监听 socket 的文件描述符 ,不需要重新 bind 端口。这是 nginx 源码中通过环境变量 NGINX_VAR 传递的。

旧 master 把监听端口的文件描述符传给新 master,新进程继承使用,避免重复绑定。

Q3:如果升级后发现有问题,怎么回滚?

1.如果还没发 QUIT(旧 master 还在):

bash 复制代码
kill -HUP \`cat nginx.pid.oldbin\`   # 唤醒旧 worker
kill -TERM \`cat nginx.pid\`         # 停掉新版本

2.如果已经发了 QUIT,就只能用备份二进制重启旧版本。

Q4:为什么用 kill -WINCH 而不是直接 -TERM 旧 master?

WINCH 只杀 worker,保留 master。这样回滚时只需要 -HUP 就能唤醒 worker,不需要重新启动整个进程。如果直接 -TERM master,旧 master 就彻底没了。

Q5:源码编译和包管理器安装,生产环境选哪个?

  • 包管理器:方便、易管理、自动处理依赖,适合大多数场景
  • 源码编译:可定制模块、可指定路径、支持平滑升级,适合对版本和路径有特殊要求的场景
相关推荐
一叶知秋yyds3 小时前
Ubuntu 虚拟机安装 OpenClaw 完整流程
linux·运维·ubuntu·openclaw
斯普信云原生组4 小时前
Prometheus 环境监控虚机 Redis 方案(生产实操版)
运维·docker·容器
safestar20125 小时前
ES批量写入性能调优:BulkProcessor 参数详解与实战案例
java·大数据·运维·jenkins
来一颗砂糖橘5 小时前
负载均衡的多维深度解析
运维·负载均衡
楠奕5 小时前
CentOS7安装GoldenDB单机搭建及常见报错解决方案
linux·运维·服务器
GCTTTTTT6 小时前
远程服务器走本地代理
运维·服务器
剑锋所指,所向披靡!6 小时前
Linux常用指令(2)
linux·运维·服务器
不愿透露姓名的大鹏6 小时前
Oracle归档日志爆满急救指南
linux·数据库·oracle·dba
飞Link6 小时前
逆向兼容的桥梁:3to2 自动化降级工具实现全解析
运维·开发语言·python·自动化