资讯动态

RedHat 9 源码编译 Nginx 实战:依赖、参数与避坑指南

发布时间:2026/10/27 20:56:09 来源:尧图企业网站定制
1. 为什么在 RedHat 9 上还要折腾源码编译在 RedHat 9 上装 Nginx绝大多数人的第一反应是dnf install nginx一条命令搞定省心省力。但我在实际运维和交付环境里遇到需要源码编译的场景远比想象中多。最典型的情况是系统自带的软件源里 Nginx 版本偏旧而业务侧需要用到某个较新版本才引入的模块特性或者需要把某些第三方模块比如一些自定义的鉴权、日志、限流模块静态编译进去包管理器装的二进制根本没法加再或者交付环境对软件来源有严格要求必须从源码构建、可控可审计。源码编译这件事说难不难说简单也真不简单。难的不是敲那几行./configure make make install而是编译过程中各种依赖缺失、路径配置、权限归属、后续升级维护的坑。我见过太多人第一次编译完能跑起来结果过两个月想加个模块发现当初编译参数没记只能推倒重来也见过编译出来的 Nginx 日志目录权限不对跑一段时间磁盘写满或者权限报错。所以这篇内容我想把 RedHat 9 这个特定环境下源码装 Nginx 的完整链路讲透包括每一步为什么这么做、参数怎么选、装完之后怎么验证、以及那些文档里不会写的经验。适合谁看如果你是有一定 Linux 基础、需要在 RedHat 9或同源的兼容发行版上从源码构建 Nginx 的运维、后端或者交付工程师这篇能直接抄作业如果你只是想快速跑个 Nginx 做测试那包管理器确实更合适但读完你也能明白源码编译到底在做什么。下面我按实际操作的顺序展开中间穿插大量踩坑经验。2. 编译前的环境盘点与依赖补齐2.1 先搞清楚 RedHat 9 的软件源现状RedHat 9 默认的软件仓库策略和 CentOS 时代有区别很多在 CentOS 7 上yum install就能拿到的开发包在 RedHat 9 上需要先确认仓库是否启用。编译 Nginx 需要的核心依赖其实不多但每一个都不能少。我一般先做一次系统更新和仓库检查dnf update -y dnf repolistrepolist的输出能让你确认BaseOS和AppStream这两个基础仓库是否都在。RedHat 9 把很多开发工具拆到了AppStream里如果这个仓库没启用后面装gcc、pcre-devel会直接报找不到包。这一步看起来废话但我遇到过交付环境为了“精简”把 AppStream 禁用了结果卡在装依赖上半天。2.2 编译工具链和库依赖的完整清单Nginx 源码编译依赖分两类一类是编译工具本身一类是功能库。工具链方面需要gcc、make如果要用到 C 相关或者某些模块还得有gcc-c。功能库方面最核心的是三样PCRE正则表达式库Nginx 的location正则匹配、rewrite规则全靠它没有它编译会直接失败。zlibgzip 压缩模块依赖做静态资源服务基本必开。OpenSSL如果要配置 HTTPS这个必须有。对应的开发包在 RedHat 9 里叫pcre2-devel、zlib-devel、openssl-devel。注意这里有个容易踩的坑RedHat 9 默认的 PCRE 已经是 PCRE2 了而老版本 Nginx 对 PCRE2 的支持是从 1.21.5 之后才比较完善。如果你编译的是很老的 Nginx 版本可能会遇到找不到pcre.h的问题因为 PCRE2 的头文件路径和 PCRE1 不一样。我的建议是直接用较新的稳定版 Nginx省去这层兼容麻烦。一次性把依赖装齐dnf install -y gcc make gcc-c pcre2-devel zlib-devel openssl-devel装完可以用rpm -q逐个确认别嫌麻烦我习惯装完立刻验证避免编译到一半才发现某个包其实没装上。2.3 一个常被忽略的前置动作创建专用运行用户很多人编译完直接./nginx用 root 跑起来测试没问题上线就出事。Nginx 的 worker 进程应该用非特权用户运行这是基本的安全实践。RedHat 9 上包管理器安装会自动创建nginx用户但源码编译不会。所以编译前先手动建好groupadd -r nginx useradd -r -g nginx -s /sbin/nologin -M nginx-r表示系统用户-s /sbin/nologin禁止登录-M不创建家目录。这几个参数不是随便写的系统用户 UID 范围小、权限受限禁止 shell 登录防止被拿来做跳板不建家目录是因为 Nginx 运行根本不需要。后面./configure时用--usernginx --groupnginx指定编译出来的配置默认就带上了省得装完再改。提示如果你打算复用已有的用户务必确认该用户没有登录 shell 且权限足够低别图省事用nobody某些系统上nobody的权限边界很模糊。3. 源码获取与 configure 参数的门道3.1 下载源码包与校验源码从 Nginx 官方站点获取选稳定版stable分支。下载后我强烈建议做一次完整性校验尤其是生产环境wget https://nginx.org/download/nginx-1.24.0.tar.gz wget https://nginx.org/download/nginx-1.24.0.tar.gz.asc拿到.asc签名文件后可以用gpg --verify校验确认包没被篡改。这一步在内部交付环境里经常被省略但只要出过一次下载损坏或者中间被替换的事你就会养成校验的习惯。校验通过后解压tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.03.2 configure 参数到底该怎么选./configure是整个编译过程里最需要动脑的一步参数选错了后面全是返工。我先给一个我常用的、兼顾功能和精简的配置./configure \ --prefix/usr/local/nginx \ --sbin-path/usr/local/nginx/sbin/nginx \ --conf-path/usr/local/nginx/conf/nginx.conf \ --pid-path/usr/local/nginx/logs/nginx.pid \ --lock-path/usr/local/nginx/logs/nginx.lock \ --error-log-path/usr/local/nginx/logs/error.log \ --http-log-path/usr/local/nginx/logs/access.log \ --usernginx \ --groupnginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-pcre \ --with-stream \ --with-stream_ssl_module逐个解释关键参数背后的逻辑。--prefix决定安装根目录我习惯统一放/usr/local/nginx好处是所有文件集中卸载时直接删目录升级时也好备份。--sbin-path、--conf-path这些显式指定路径是为了让目录结构清晰可控避免默认值散落在系统各处。--pid-path和--lock-path放 logs 目录下方便排查进程状态。模块方面--with-http_ssl_module和--with-http_v2_module是现在做 Web 服务的基本盘HTTP/2 对静态资源加载提升明显。--with-http_realip_module在走反向代理或负载均衡时用来获取真实客户端 IP不做这层配置日志里全是代理的 IP排查问题很痛苦。--with-http_gzip_static_module支持预压缩的.gz文件直接返回比运行时压缩省 CPU。--with-http_stub_status_module提供基础的状态监控页轻量够用。--with-stream是四层代理模块做 TCP/UDP 转发时用得上即使暂时不用编译进去也不占多少体积省得以后再加。这里有个经验不要盲目--with-*全开。有些模块依赖额外的库全开会引入不必要的依赖和攻击面。按实际需求选把编译参数记到一个文件里比如build-args.txt以后升级或者加模块时直接复用这是我最想强调的习惯。3.3 configure 阶段的报错怎么读./configure跑完会输出一个汇总列出哪些模块启用、哪些没启用、依赖检查结果。如果中途报错最常见的是the HTTP rewrite module requires the PCRE library说明 PCRE 开发包没装或者路径没找到。RedHat 9 上如果装的是pcre2-devel但 Nginx 版本较老可能仍然找不到这时候要么升级 Nginx 版本要么手动指定--with-pcre../pcre-源码目录让它用自带的 PCRE 源码编译。还有一种报错是SSL modules require the OpenSSL library检查openssl-devel是否真的装上了有时候仓库里同名包版本不匹配也会导致检测失败。我的做法是看config.log里面记录了每个检测项的详细过程比终端上那几行报错信息有用得多。4. 编译安装与目录权限的收尾处理4.1 make 与 make install 的实操细节./configure成功后执行make -j$(nproc) make install-j$(nproc)用满所有 CPU 核心并行编译能显著缩短时间。但要注意如果编译机器内存很小比如 1G 的测试机并行度过高可能触发 OOM这时候把-j后面的数字调小比如-j2。我遇到过在低配虚拟机上-j8直接卡死的情况排查半天才发现是内存不够。make install会把文件按 configure 时定的路径铺开。装完先别急着启动做两件事检查目录结构和处理权限。4.2 目录归属与权限的坑源码编译出来的目录默认属主是执行编译的那个用户通常是 root。Nginx 的 worker 进程以nginx用户运行它需要对某些目录有写权限主要是存放临时文件、缓存、PID 和日志的目录。如果权限不对启动时可能报Permission denied或者运行一段时间后日志写不进去。我的处理方式是chown -R root:root /usr/local/nginx chown -R nginx:nginx /usr/local/nginx/logs chown -R nginx:nginx /usr/local/nginx/client_body_temp chown -R nginx:nginx /usr/local/nginx/proxy_temp chown -R nginx:nginx /usr/local/nginx/fastcgi_temp chown -R nginx:nginx /usr/local/nginx/uwsgi_temp chown -R nginx:nginx /usr/local/nginx/scgi_temp主目录和二进制归 root防止被普通用户篡改日志和临时目录归 nginx 用户保证 worker 能写。client_body_temp这些临时目录在首次启动时才会创建如果启动前不存在可以先手动建好再赋权避免首次请求上传大文件时报错。注意不要图省事把整个/usr/local/nginx都chown给 nginx 用户配置文件归普通运行用户所有是安全隐患一旦该用户被攻破配置可被随意篡改。4.3 验证安装结果启动前先用-t检查配置语法/usr/local/nginx/sbin/nginx -t输出syntax is ok和test is successful才算过。然后启动/usr/local/nginx/sbin/nginx用ps -ef | grep nginx确认 master 和 worker 进程都在worker 进程的属主应该是nginx。再curl -I http://127.0.0.1看返回头正常应该看到Server: nginx/1.24.0。最后看error.log有没有异常输出这一步很多人跳过结果带着隐患上线。5. 装完之后必须补的几件事5.1 配置 systemd 服务管理源码编译不会自动生成 systemd unit手动敲命令启停不现实。自己写一个/etc/systemd/system/nginx.service[Unit] Descriptionnginx - high performance web server Afternetwork.target remote-fs.target nss-lookup.target [Service] Typeforking PIDFile/usr/local/nginx/logs/nginx.pid ExecStartPre/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf ExecStart/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload/usr/local/nginx/sbin/nginx -s reload ExecStop/usr/local/nginx/sbin/nginx -s quit PrivateTmptrue [Install] WantedBymulti-user.targetTypeforking是因为 Nginx 默认以守护进程方式运行master 进程 fork 后父进程退出systemd 需要靠PIDFile追踪。ExecStartPre里加-t检查配置配置有语法错误时服务直接起不来而不是带着错误配置跑起来这个细节能省很多事。ExecStop用-s quit而不是-s stopquit是优雅退出会等当前请求处理完stop是快速终止生产环境优先优雅。写完systemctl daemon-reload然后systemctl enable --now nginx设置开机自启并立即启动。5.2 日志轮转不能忘源码装的 Nginx 日志默认无限增长跑久了磁盘会被撑爆。用系统的logrotate来管新建/etc/logrotate.d/nginx/usr/local/nginx/logs/*.log { daily rotate 14 missingok notifempty compress delaycompress sharedscripts postrotate /usr/local/nginx/sbin/nginx -s reopen endscript }关键是postrotate里的-s reopen它让 Nginx 重新打开日志文件句柄这样轮转后的新日志才能被正确写入。没有这一步轮转后 Nginx 还在往被重命名的旧文件里写新文件一直是空的这是日志轮转最经典的坑。5.3 升级与加模块的可持续方案源码编译最大的痛点是后续维护。我的做法是把当初的./configure参数完整保存到/usr/local/nginx/build-args.txt升级时下载新版本源码用同样的参数重新 configure、make然后不要直接 make install 覆盖而是先make install到一个临时目录对比新旧文件确认无误后再替换二进制、平滑 reload。加模块也是同理在原有参数基础上追加--add-module模块路径重新编译。平滑升级可以用信号机制把新二进制放到临时位置给旧 master 进程发USR2信号它会启动新的 master 并用新二进制旧 worker 继续服务存量请求处理完再发WINCH和QUIT逐步退出。这套流程稍微复杂但能做到零停机值得单独花时间练一遍。6. 那些文档里不会写的踩坑记录6.1 编译时找不到 OpenSSL 的诡异情况有一次在 RedHat 9 上编译openssl-devel明明装了./configure还是报找不到 OpenSSL。查了半天发现系统里同时存在多个 OpenSSL 版本头文件路径和库路径不一致。解决办法是在 configure 时显式指定--with-openssl/path/to/openssl-source让 Nginx 用自带的 OpenSSL 源码编译绕开系统路径混乱的问题。这个坑的教训是依赖检测失败不一定是没装可能是装了多个版本互相干扰config.log里能看到它到底在哪些路径找过。6.2 防火墙和 SELinux 的隐形拦截RedHat 9 默认启用 firewalld 和 SELinux。Nginx 起来后本机curl正常外部访问不通八成是防火墙没放行 80/443firewall-cmd --permanent --add-servicehttp firewall-cmd --permanent --add-servicehttps firewall-cmd --reloadSELinux 更隐蔽如果 Nginx 要监听非标准端口或者访问某些目录即使权限看着都对也会被拦。排查时先看ausearch -m avc -ts recent有没有拒绝记录确认是 SELinux 问题再针对性调整策略别上来就setenforce 0关掉那是把安全机制整个废了。6.3 编译参数丢失导致的返工这个坑我踩过不止一次。早期编译时随手敲参数没记录后来要加个模块完全想不起来当初开了哪些选项只能凭记忆重来结果新编译的版本行为和线上不一致排查了很久。从那以后我强制自己把参数写进文件并且把nginx -V的输出也存档-V会打印完整的编译参数和版本信息是最可靠的“编译指纹”。每次升级前先nginx -V对比确保参数一致。6.4 临时目录缺失引发的上传失败Nginx 处理客户端上传的大请求体时会先写到client_body_temp目录。源码编译后这个目录可能不存在小请求没事一旦上传大文件就报 500。手动创建并赋权就能解决但更稳妥的做法是在部署脚本里把几个 temp 目录统一建好别等出问题再补。7. 我个人在实际操作中的体会源码编译 Nginx 这件事技术门槛其实不高真正拉开差距的是对细节的把控和后续可维护性的设计。我现在的习惯是编译前把依赖清单、用户创建、参数选择一次性规划好编译后立刻补 systemd、日志轮转、参数存档这三件事把“能跑”变成“能长期稳定跑”。RedHat 9 这个环境相对规范仓库和工具链都比较新只要注意 PCRE2 的兼容性和 SELinux 的拦截整体流程是顺畅的。最后分享一个小技巧如果你不确定某个模块要不要编译进去先查它的依赖和体积依赖轻、体积小的模块比如stub_status、realip建议直接编进去省得以后为了一个小功能重新编译整个 Nginx依赖重或者有安全考量的模块按需再加。编译参数存档加上nginx -V指纹这两样东西能让你在半年后接手自己当初的构建时不至于一脸茫然。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑