尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Nginx平滑升级实战:编译安装、信号机制与回滚方案全解析

发布时间:2026/9/29 8:52:01

资讯中心
01
ARTICLE

Nginx平滑升级实战:编译安装、信号机制与回滚方案全解析

Nginx平滑升级实战:编译安装、信号机制与回滚方案全解析
聊一个我在生产环境里反复操作过很多次的内容Linux下通过编译安装方式对nginx做平滑升级。先说清楚这个东西解决什么问题。线上nginx承载着域名解析、反向代理、Web服务有的是在跑长连接有的在传输大文件直接停掉服务升级等于把所有正在进行的请求全部掐断。平滑升级的意思是二进制文件换成新版本但正在跑的业务连接完全不受影响旧的worker进程处理完手头请求再退出新请求全部交给新进程。今天这篇就把整个流程讲透包括背后的信号机制、编译参数怎么对齐、回滚方案怎么留后手全是实测过的干货。这个操作适合谁来参考如果你是运维、后端开发或者自己折腾服务器nginx是从源码编译装的而不是yum/apt装的那这篇文章的每一步你都可以直接照着做。用apt或者yum装的同学也别退出后半部分关于信号机制和原理的讲解同样有用因为二进制包方式升级的逻辑是同一套只是编译这一步不用做。1. 为什么平滑升级是刚需它的底层逻辑是什么1.1 先分清“停服升级”和“平滑升级”的区别很多人在第一次接触nginx升级时会有个误区觉得升级嘛把新版本编译好装上去重启服务不就行了。在开发环境随便你怎么折腾但生产环境你试一次就知道有多疼了。直接stop再start所有在线的TCP连接全部断开正在传文件的用户报错websocket直接掉线如果nginx还顶着很重要的接口转发那连锁反应更大。nginx在整个链路里扮演的是“入口”角色入口断一秒钟下游所有服务都会感知到。我自己踩过一次当时图省事直接重启恰好有个用户在下载一个几百MB的安装包下载到一半断掉那会儿还没有断点续传的概念用户直接找客服投诉了。从那以后我对线上nginx的操作只有一个原则能不重启就不重启必须升级就走平滑流程。平滑升级的核心价值一句话概括服务不中断连接不丢失版本已更新。它不要求新老进程在同一时刻完成交接而是通过信号机制让老的worker进程慢慢把手头的活儿干完自然退休新的worker进程提前上岗接客。1.2 nginx的master-worker进程模型是这一切的根基要理解平滑升级为什么能成立得先搞懂nginx的进程模型。nginx启动后有两个角色的进程一个是master主进程负责读取配置、管理worker进程的生命周期、接收外部信号另一个是worker工作进程真正处理用户请求。master和worker之间是父子关系每个worker都是master fork出来的。这个模型就像餐厅里的老板和员工。老板不亲自端菜只负责安排排班、处理突发事件服务员才是真正一对一服务顾客的人。换人的时候怎么办老板先招一批新服务员站在一边服务员把手头这桌客人服务完下班新服务员马上顶上去顾客全程无感知。nginx的平滑升级也正是这么干的。通过信号告诉master进程“有新版本来了”master唰地fork出一批新worker新旧worker同时存在但监听的是同一个端口。旧worker处理完自己队列里的请求之后主动退出整个过渡就完成了。1.3 为什么选择编译安装方式来做平滑升级nginx的安装方式主流有三种系统包管理器、官方预编译二进制包、源码编译安装。平滑升级在三种方式下都能做但用编译安装的方式有它独特的优势。你自己编译编译参数完全可控想要哪些模块、不想要哪些模块都是自己说了算。生产环境里经常需要加第三方模块比如lua、http_v2_module、stream模块这些都需要在编译阶段通过--with参数指定进去。包管理器安装的nginx版本和编译参数被发行版维护者固定了你想加一个模块进去还得去看它到底用的是什么编译选项反而更麻烦。而且很多企业为了保证软件供应链可控本身就规定生产环境软件必须源码编译安装。所以在Linux下用编译安装方式做平滑升级是运维人员必须具备的一项操作技能。2. 动手之前先摸清旧版本的底细2.1 三个命令锁定当前版本和编译参数在编译新版本之前你要先搞清楚一个东西现在线上跑的nginx是什么版本、当初编译时带了哪些参数。如果新编译的nginx不带旧版本的关键参数升级之后轻则缺少某些功能重则配置文件不兼容直接启动失败。用下面的命令查看当前版本和编译配置nginx -V注意是大写的V出来的结果包含版本号、编译器和configure配置参数。如果nginx不在PATH里可以用绝对路径比如/usr/local/nginx/sbin/nginx -V我建议你把输出完整存下来后续configure的时候直接对照着写。输出内容类似这样nginx version: nginx/1.18.0 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) configure arguments: --prefix/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-stream这里的--prefix/usr/local/nginx是安装路径--with-http_ssl_module是SSL模块--with-stream是四层代理模块。每一种都是旧线上环境在跑的能力新版本一个都不能少。2.2 确认新旧版本兼容性再动手确认完旧版本之后去nginx官网下载页找到你想升级的目标版本。基本原则是大版本内的升级比如1.18.0升到1.20.2风险相对较小跨大版本1.x升到1.25.x要慎重一些。新版本可能会调整默认行为比如HTTP/2的实现有变化某些指令的默认值变了这些在小版本升级里不会出现。我的习惯是看一眼官方的CHANGES文件也就是更新日志重点关注有没有不兼容变更说明。如果是从1.18升到1.24这种跨度建议先在测试环境完整走一遍升级流程确认业务正常后再动生产。生产环境永远不要做“没验证过的操作”这句话值得写在工位上。2.3 备份旧二进制文件这是你最后一条退路无论你有多自信升级前必须备份旧版nginx的二进制文件。这一步非常简单但很多人会漏掉。平滑升级做完之后旧的master进程还挂在内存里旧二进制文件如果被覆盖你后续想回滚就会非常被动。我就是一直保持这个习惯升级前先把当前的nginx可执行文件复制一份带版本号的文件名cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak_1.18.0这样做的目的是万一新版本上线后发现严重问题可以直接把旧文件拷回去重新走一遍平滑流程快速回滚到旧版本。备份配置这些也是一样的道理nginx.conf如果有改动提前复制一份nginx.conf.bak就够了。3. 核心实操一步步带你看完整升级流程3.1 下载新版本源码并解压以升级到nginx 1.24.0为例先去官网下载源码包。服务器上不一定要有图形界面直接在命令行用wget或者curl下载就行。cd /usr/local/src wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.0建议下载之后验证一下文件完整性官网会提供校验值用md5sum nginx-1.24.0.tar.gz对比一下防止下载到损坏的包或者被人篡改过的包。虽然nginx官网被篡改的概率很小但安全这个事养成习惯比什么都强。3.2 configure配置编译参数必须对齐旧版本这一步是整个编译安装方式的核心。如果你只是./configure不加任何参数默认会安装到/usr/local/nginx但很多常用模块不会带上。所以配置的时候把旧版本nginx -V输出的configure arguments原封不动粘过来有需要再加新参数。例如旧版本输出的是--prefix/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-stream那新版本配置时敲./configure --prefix/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-stream如果新增业务需要WebSocket支持或者更完善的代理功能可以在这个基础上追加./configure --prefix/usr/local/nginx \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-stream \ --with-http_v2_module \ --with-http_gzip_static_moduleconfigure过程中如果提示缺少依赖比如PCRE、zlib、OpenSSL这些库先用包管理器装上对应开发包。嫌麻烦的话也可以直接在configure参数里指定nginx源码目录下的第三方库源码路径但生产环境我更推荐用系统库方便统一维护和升级。配置完成后会生成Makefile文件同时输出一段摘要显示启用了哪些模块。检查一遍有没有你特别依赖的模块尤其是--with-stream这些别漏掉。3.3 make编译但绝不make install很多人习惯性的make make install一把梭但平滑升级这里千万不能这么做。make install会把新二进制安装到指定prefix路径下覆盖掉旧版本的nginx文件但此时你还是想保留旧文件的否则就没有平滑可言了。这个场景下只需编译不安装make编译过程一般一两分钟就能完成结束后在源码目录下的objs文件夹里会生成一个nginx文件这就是新版本的可执行文件。可以确认一下版本./objs/nginx -V看到输出版本号是1.24.0并且configure arguments包含了你需要的参数这个二进制文件就是待会儿要用来替换的成品。这里解释一下为什么安装这一步要分开做。平滑升级的本质是“二进制文件替换信号触发”make install本身做的事情太多了比如安装html目录、配置文件等这些不是你想要的而且它覆盖文件的方式不受控。手动cp二进制文件给你最大的掌控感路径对、文件对、时机对心里有底。3.4 替换二进制文件之前再做一次配置测试新的二进制文件编译好之后不要急着替换先用它测试一下现有配置有没有问题。因为nginx配置版本之间多少会有细微差异提前验证能帮你规避升级后启动失败的问题。用新编译好的二进制文件去测试当前nginx.conf./objs/nginx -t -c /usr/local/nginx/conf/nginx.conf-t是测试配置-c指定配置文件路径。如果输出syntax is ok和test is successful说明新版本解析你的配置没有问题可以进入下一步。如果报错仔细看一下是哪条指令或者哪个参数不兼容处理后重新编译或者调整配置。这一步给了你第二次反悔的机会。很多升级事故都发生在“配置不兼容”上先测试一遍就能避掉大半风险。3.5 正式替换旧二进制备份工作之前已经做了新文件也验证过了这时可以正式替换cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old cp /usr/local/src/nginx-1.24.0/objs/nginx /usr/local/nginx/sbin/nginx执行完ls -l /usr/local/nginx/sbin/nginx看一眼确认文件时间戳和大小已经变成新版。注意替换的是磁盘上的文件当前运行中的旧master进程还在内存里指着老文件这就是为什么我们可以放心覆盖。等到用信号触发之后新master进程启动时才会读取磁盘上的新文件。3.6 信号触发平滑升级的真正核心这里要用到nginx的信号机制了。先找到当前master进程的PIDps -ef | grep nginx输出里第一列是用户第二列是PID找到master process那一行的PID比如是12345。记下这个PID。平滑升级分三步一个信号一个信号来不要图省事合并执行这样才能把主动权牢牢握在自己手里。第一步向旧的master进程发送USR2信号kill -USR2 12345USR2信号对nginx来说就是“启动新版本的master进程”。旧master收到这个信号后会使用磁盘上新的二进制文件启动一个新的master进程同时新master会fork出一批新的worker进程。这时系统里存在两个master进程和两套worker进程它们共享监听同一个端口。旧的worker继续处理旧连接新的worker开始接新连接。验证一下当前进程状态ps -ef | grep nginx输出中你会看到两个master进程一个是旧的PID12345另一个是新的master PID通常会是12346或者更大的值。第二步向旧的master进程发送WINCH信号让它优雅关闭旧workerkill -WINCH 12345WINCH信号告诉旧master慢慢把旧的worker进程都关掉。注意“慢慢”这个词每一个worker会等到当前连接处理完再退休不会切断任何活跃请求。过几秒再看进程列表旧的worker进程就已经退光了只剩旧master壳子在那等着。第三步确认一切正常后让旧master彻底退出kill -QUIT 12345QUIT信号让旧master优雅退出。到这里整个升级过程完成系统里只剩新master和新worker。再查看进程列表旧的master已经不在了全部是新的PID。3.7 升级完成后的验证工作升级完不能只看进程列表就收工要确认几件事。第一直接看版本号/usr/local/nginx/sbin/nginx -V此时输出应该显示新版本比如nginx version: nginx/1.24.0。第二检查监听端口和worker进程是否健康netstat -tnlp | grep nginx确认80/443这些预期端口都在正常监听进程名显示的是nginx没有异常状态。第三从外部访问一下服务确认业务正常。比如在本地执行curl -I http://127.0.0.1/看看HTTP状态码是否正常或者直接访问你已经配置好的域名确认页面能打开接口能通。如果是负载均衡场景多打几次请求看看后端转发是否正常。第四查看错误日志tail -f /usr/local/nginx/logs/error.log如果有warning或者error信息需要仔细排查。正常情况下平滑升级后error.log不会出现新增的错误条目。3.8 回滚流程你最好知道但不希望用到没人能保证每次升级都完美所以回滚方案必须在动手之前设计好。如果升级后发现问题比如新版本兼容性问题、模块异常需要立刻回滚操作也很简单。第一步找出系统里还在运行的旧master进程的PID。如果步骤3.6里旧master还没有收到QUIT信号那它还在系统里挂着直接向它发送HUP信号kill -HUP 旧masterPIDHUP信号会让旧master重新读取配置文件并启动一批worker进程。此时系统的控制权就又回到了旧版本手里。如果旧master已经QUIT退出了那就手动把备份的旧二进制文件恢复回来cp /usr/local/nginx/sbin/nginx.old /usr/local/nginx/sbin/nginx然后向当前的新master进程发送USR2信号让它加载这个恢复出来的旧版本二进制再走一遍平滑流程切回旧版本。经历过一次之后你就会发现备份旧二进制文件这个习惯到底有多重要。回滚流程速查表场景操作旧master还活着kill -HUP 旧masterPID旧master已退出恢复旧二进制kill -USR2 新masterPID需要彻底切换版本重复平滑升级流程4. 实操中高频踩坑点这些坑我替你踩过了4.1 编译参数不一致导致功能丢失这是我见过最多的问题。有人升级时图省事不查看旧版本的configure参数新版本直接默认编译结果升级完发现stream模块没了四层转发功能全部失效线上业务直接受影响。这种问题排查起来非常痛苦因为报错不一定发生在nginx层而是在业务层面表现为“部分服务不通”。所以我的建议是升级之前务必执行nginx -V把输出内容原原本本保留下来。configure参数一个都不能少只能在原有基础上增加不能减少。如果你不确定某个模块当前是否用得上宁可编译进去也别砍掉内存占用多了几MB但线上兜底能力完全不同。4.2 忘记备份回滚无门升级前不备份二进制文件这个问题我没少在同事身上看到。有人觉得反正新版本是兼容的不会出问题不需要备份。但生产环境最怕的就是“觉得”。一旦升级后出现诡异问题比如CPU飙高、内存泄漏、连接数异常你手里没有旧版二进制回滚就只能现找资源重新编译一个旧版本出来那时候的心情用“绝望”来形容都不夸张。养成习惯升级前一条命令的事cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old顺带把nginx.conf也备份一份虽然配置文件一般不会变但多一道保险总没坏处。4.3 信号发给了错误进程信号必须发给master进程发给worker进程不会触发平滑升级逻辑。很多人用ps -ef | grep nginx会把master、worker、还有grep进程本身都列出来一时分不清哪个是master哪个是worker。分辨方式很简单看命令行参数master进程那一行带的是master process字样下面的一堆worker进程带的是worker process字样。还有一个更稳妥的方式nginx启动时会在/usr/local/nginx/logs/nginx.pid文件里写入master进程的PID直接读这个文件就行cat /usr/local/nginx/logs/nginx.pid用这个PID发信号基本不会发错。4.4 磁盘空间不足导致编译失败编译nginx本身不需要多少磁盘空间但解压源码、编译产生的中间文件加到一块几百MB是有的。如果你服务器上磁盘常年飘红到configure或者make这一步卡住提示No space left on device那就尴尬了。提前检查一下磁盘使用情况df -h /usr/local/src确保源码目录所在分区有足够的剩余空间再动手。编译完成后如果不想留着源码也可以直接rm -rf /usr/local/src/nginx-1.24.0把中间文件清掉省出空间。4.5 升级之后worker进程数量不对有时候升级完发现worker进程数量跟配置文件里的worker_processes设置不一致不用慌张大概率是新master启动时读取配置还没完全加载好或者你改了编译参数导致某些模块没生效。用HUP信号让新master重新加载一下配置kill -HUP 新masterPID等一两秒再看进程列表worker数量应该就恢复正常了。如果还不正常检查一下nginx.conf里的worker_processes指令配置值。4.6 依赖库版本太老导致编译报错如果你线上系统比较老比如CentOS 6、Ubuntu 14.04这种编译新版本nginx时很可能会遇到依赖库版本过旧的问题。常见报错是找不到OpenSSL头文件、PCRE库太老等。这时候基本只能先升级依赖库或者使用nginx源码包自带的第三方库源码。比如nginx的configure支持这样指定./configure --with-pcre/usr/local/src/pcre-8.44 --with-zlib/usr/local/src/zlib-1.2.12 --with-openssl/usr/local/src/openssl-1.1.1w这种方式把依赖库源码一并编译进nginx不用动系统库对老系统比较友好。缺点是升级系统库时nginx需要重新编译一遍所以生产环境还是建议用系统库为主特殊情况再走源码内嵌。4.7 编译成功了但版本号还是旧的这个问题很隐蔽。你执行完kill -USR2之后进程列表里出现了一个新master进程但你执行nginx -V看到的还是旧版本号。原因大概率是PATH环境变量里先找到了旧的nginx可执行文件而你升级后的二进制文件在另一个路径。排查方式很简单用绝对路径执行/usr/local/nginx/sbin/nginx -V如果绝对路径下的版本号已经是新版本那说明升级本身没问题只是命令行自动补全或者PATH解析的问题。这种情况下顺便检查一下which nginx指向的是哪个文件确保后续运维操作不会误用旧文件。5. 两条生产环境建议都是踩过坑之后的体会5.1 先在测试环境完整演练一遍再动生产平滑升级流程看起来不复杂但真正在生产环境操作时环境复杂度会放大每一个细节问题。我个人的习惯是在测试环境搭一套和生产配置完全一致的nginx先把升级流程完整走一遍包括版本切换、回滚流程、业务验证确认所有步骤都熟记于心之后再操作生产。测试环境不只用来验证新版本兼容性更重要的是帮你建立“手感”。平滑升级最重要的就是信号发送的时机和验证节奏这些光靠文档理解不如实际演练一遍。演练结束后把过程记录成文档下次操作直接照着做心里特别踏实。5.2 升级尽量选在业务低峰期虽然平滑升级不会中断连接但新老进程交替期间如果遇到突发的大流量新worker刚启动还没进入最佳状态处理能力可能略有波动。所以尽量选在业务低峰期操作比如半夜两点到五点之间。这个时间段即使出现一些意外情况也有足够的时间排查处理不至于影响线上用户。最后分享一个实用技巧升级时在终端开两个窗口一个窗口执行信号发送另一个窗口用watch -n 1 ps -ef | grep nginx实时盯着进程状态变化。当你敲下kill -USR2的瞬间能清晰看到进程树的变化整个平滑升级过程尽在掌握。这种掌控感是运维工作里最让人安心的时刻。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。