1. 为什么要做离线安装,和在线安装差在哪
先说结论:离线安装Nginx这件事,往往不是“想不想”的问题,而是“只能这么干”的问题。我在一线运维这几年,真正动手做离线安装的场景基本都是同一类:服务器在内网隔离区、生产环境不允许直接连外网、或者客户现场的网络策略卡得很死。这种情况下,yum、apt、wget这些平时用得行云流水的操作全部失效,你只能靠优盘、内网FTP或者堡垒机上传递文件,把安装包“人肉”带进服务器。
在线安装和离线安装的本质差异,不在Nginx本身,而在“依赖”两个字。在线安装时,包管理器会自动帮你把PCRE、zlib、openssl这些依赖一并拉下来;离线安装时,这些依赖一个都不会自己出现,必须你亲手准备齐全。这就像出门吃饭,在线安装是去食堂,刷卡就行;离线安装是去野外露营,锅碗瓢盆、米面油盐、打火机全得自己背包里带上。少带一样,饭就做不成。
还有一个经常被忽略的点是“版本一致性”。在线安装你看到的依赖版本,是软件仓库里当前最新的那一批;离线安装你手里拿到的,是在有网机器上下载时固定的那一批。这个差异会直接影响编译是否顺利。所以我现在做离线安装,第一步永远是先把环境信息摸清楚,再决定用哪些版本的包,而不是随手拿个安装包就往生产服务器上怼。
另外要提醒一句:如果你只是在自己笔记本的虚拟机上实验,网络又是通的,那完全不需要离线安装,直接yum install nginx或者apt install nginx就完事。这篇文章是给那些“必须离线、而且还得编译安装”的人准备的。我下面分享的过程,是我在CentOS 7.x和银河麒麟这类系统上反复验证过的,你把命令照抄过去,踩坑概率会低很多。
2. 动手前的准备:版本选型与依赖盘点
2.1 先确认你的服务器基础信息
不管装在什么系统上,第一件事不是下载安装包,而是先看目标机器的“底细”。先执行下面这三条命令:
cat /etc/os-release uname -m cat /proc/version/etc/os-release告诉你操作系统发行版和版本号;uname -m告诉你架构是x86_64还是aarch64;/proc/version里能看到内核编译信息,有的老机器内核版本比较低,这会影响你选Nginx版本。比如你在aarch64的机器上装了x86_64编译出来的二进制,那肯定跑不起来,这种低级错误我见过不止一次。
还有一个很容易被忽略的检查项,是看目标机器上是否已经装了gcc和make。Nginx官方提供的源码包是C语言写的,编译必须靠gcc,构建过程要靠make。如果目标机器连编译工具链都没有,你就得多带两个RPM包进去。可以用下面的命令快速判断:
gcc --version make --version如果提示command not found,那你还需要额外准备gcc、make相关的RPM安装包,或者干脆找一台架构相同的机器,把编译好的整个Nginx目录打包拷过去。后者是很多老运维的土办法,但说实话,后续升级和管理都不如编译安装来得清爽,所以我更推荐老老实实把依赖带齐。
2.2 Nginx离线安装的四大依赖
Nginx编译安装时最重要的依赖有四个:PCRE、zlib、OpenSSL,以及一个隐藏依赖——C编译器工具链。前三个分别承担不同的职责:
- PCRE库:Nginx的
rewrite模块重度依赖正则表达式,PCRE就是这个正则引擎。在Nginx 1.17之前,基本都是用PCRE 8.x系列;新版官方建议用PCRE2,但要注意,编译参数的写法会有变化,下面实操里我会详细说。 - zlib库:提供gzip压缩能力。如果你不在编译参数里指定
--with-http_gzip_static_module或者--with-http_gzip_module,zlib不是强制的,但生产环境你几乎一定会开gzip,所以这个依赖基本属于必带。 - OpenSSL库:提供HTTPS支持。只要你想配置
ssl监听、颁发自签名证书或者做TLS反向代理,就必须有OpenSSL。很多生产环境要求HTTPS加密传输,这个依赖不能省。
依赖的获取方式有两种。一种是在有网机器上直接下载源码包:PCRE去官网找tar.gz,zlib去官网找tar.gz,OpenSSL去官网找tar.gz。另一种是直接下载RPM包。我在实际项目里更推荐源码包,因为RPM包比较依赖系统版本,比如CentOS 7的RPM用到CentOS 6上经常装不上,而源码包只要编译器能做交叉编译,基本都能适配。
2.3 有网机器上准备离线安装包
假设你现在有台能上外网的机器,或者自己在本地电脑上操作,先把下面这些包下载好备用。具体版本号以我当时实操为准,不一定最新,但稳定组合验证过:
- nginx-1.24.0.tar.gz
- pcre2-10.42.tar.gz
- zlib-1.3.tar.gz
- openssl-3.0.13.tar.gz
如果你不确定去哪里找下载地址,就直接去官网对应的下载目录页面找,Nginx官网提供的是nginx.org/download/,pcre.org是PCRE的官方网站,zlib.net是zlib官网,openssl.org是OpenSSL的官网。把这些tar.gz打包放到一个目录下,比如我习惯建一个/opt/nginx-offline-packages/。
除此之外,强烈建议再准备两个东西:一个是有网机器上执行过的安装命令记录,方便你后面核对编译参数;另一个是Nginx官方文档的离线版或者PDF版,万一内网环境打不开官网,你还能查参数说明。别笑,实战中“查不了文档”是离线环境最大的痛点,我因为记错指令去试错浪费过不少时间。
3. 完整实操:离线环境编译安装Nginx
3.1 将离线包拷贝到目标机器的三种方式
包里准备好之后,怎么把文件安全地弄进内网机器,也有讲究。我先说三种我实测过的方式,大家按自己环境选择。
第一种,如果有内网FTP或者HTTP文件服务器,直接让运维开个临时目录,用curl或wget拉取:
wget http://192.168.1.100/pub/nginx-offline.tar.gz第二种,只允许SSH访问的话,用scp或者sftp:
scp -P 22 /opt/nginx-offline-packages.tar.gz root@目标IP:/opt/第三种,实在不行就用堡垒机的文件上传功能,我遇到很多保密要求高的项目只能走这条路。拷完之后先校验一下文件完整性,免得传输过程中文件损坏:
md5sum /opt/nginx-offline-packages.tar.gz把输出的MD5值和有网机器上算出来的对比一下,一致再继续。这一步看起来很鸡肋,但我真的遇到过一次传完了最后编译报错,排查半天发现是文件在传输时被截断了。
3.2 解压并配置编译参数
进入目标机器的/opt目录,先把压缩包解开:
cd /opt tar -xzf nginx-offline-packages.tar.gz cd nginx-offline-packages解开后你会看到四个源码目录。然后进入Nginx源码目录,配置编译参数。这里直接给出我常用的一套参数,你可以根据自己的实际需求增删:
cd nginx-1.24.0 ./configure \ --prefix=/usr/local/nginx \ --sbin-path=/usr/sbin/nginx \ --conf-path=/etc/nginx/nginx.conf \ --pid-path=/var/run/nginx.pid \ --error-log-path=/var/log/nginx/error.log \ --http-log-path=/var/log/nginx/access.log \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_gzip_static_module \ --with-pcre=../pcre2-10.42 \ --with-zlib=../zlib-1.3 \ --with-openssl=../openssl-3.0.13每条参数后面都是有讲究的。--prefix指定安装根目录;--sbin-path把可执行文件直接放到/usr/sbin下,这样你在任何目录直接输入nginx就能执行,不用加路径;--conf-path把配置文件放在/etc/nginx下,这更符合Linux的FHS习惯;三个--with参数是离线安装时最关键的,它们告诉编译系统:“PCRE、zlib和OpenSSL我放在这里,你编译时直接引用。”
这里有个大坑:如果你用了PCRE2,而且你的Nginx版本比较老,比如1.20.x,可能会报错说找不到PCRE,因为老版本源码里的一些头文件引用还是PCRE 8.x的写法。我用1.24.0配合PCRE2 10.42实测没问题,但如果你的Nginx是1.18或者更早,建议还是下载PCRE 8.45这个版本,省得折腾。
3.3 编译安装与动态链接库处理
配置成功后,屏幕会打印出一大堆摘要信息,最后一句一般是“Configuration summary”加“Support the following modules”,看到这些说明configure这一步过了。接着执行编译和安装:
make -j4 make install-j4是让make并行编译,用几个核看你服务器的CPU配置,nproc命令可以查。并行编译能明显缩短时间,但如果你内存只有1G,建议别开太多并行任务,不然内存会被撑爆。
编译过程中可能出现一个非常典型的报错:找不到libpcre.so或者libssl.so。这往往不是真的没装,而是动态链接库路径没有更新。处理办法是修改/etc/ld.so.conf,把动态库所在的目录加进去,然后执行ldconfig:
echo "/usr/local/lib" >> /etc/ld.so.conf ldconfig如果编译完之后,你直接运行/usr/local/nginx/sbin/nginx提示找不到某些动态库,也可以用这个办法解决。
安装完成后,验证一下二进制文件是否能正常执行:
/usr/local/nginx/sbin/nginx -V这条命令会把编译参数和版本信息打印出来,看到你之前配置的那一串--with参数都在,说明编译安装成功了。
3.4 注册systemd服务并设置开机自启
编译安装完的Nginx,默认不会开机自启。生产环境里服务器重启之后Nginx没起来,那可是事故。所以我建议直接把systemd服务文件写好。
在/usr/lib/systemd/system/nginx.service里新建一个文件:
[Unit] Description=nginx - high performance web server After=network.target remote-fs.target nss-lookup.target [Service] Type=forking PIDFile=/var/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf ExecReload=/usr/sbin/nginx -s reload ExecStop=/usr/sbin/nginx -s quit PrivateTmp=true [Install] WantedBy=multi-user.target写完之后依次执行:
systemctl daemon-reload systemctl enable nginx.service systemctl start nginx.service systemctl status nginx.service看到Active: active (running)就说明服务起来且开机自启已经设置好了。这里的ExecStartPre会在启动前先做一次配置语法检查,如果配置文件写错了,服务根本不会启动,这个设计能避免把线上服务搞挂。
如果是老一点的内网系统,systemd还没有普及,那你可以走传统方式,在/etc/rc.d/rc.local里加一行/usr/sbin/nginx,然后给rc.local加执行权限:
chmod +x /etc/rc.d/rc.local这条方法在CentOS 6、7早期版本以及某些国产系统上都还适用。
4. Nginx核心配置与上线前验证
4.1 先理解nginx.conf的骨架结构
安装完成了,服务也起来了,接下来就是配置了。我见过很多新手上来就改nginx.conf,结果改完一执行nginx -t就报错。想少踩坑,先把配置文件的结构搞清楚。
Nginx的配置文件核心是“块”的概念。整个文件从外到内依次是:main块、events块、http块、server块、location块。层级关系像一个俄罗斯套娃,外层配置默认作用在全局,内层配置只作用于它那一个范围。事件模型、worker进程数、日志文件路径通常在main级或http级配置;具体某个域名或端口的监听参数放在server块;针对不同URL路径的处理规则放在location块。
一份最精简的nginx.conf长这样:
worker_processes auto; worker_rlimit_nofile 65535; events { worker_connections 2048; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; gzip on; server_tokens off; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } } }worker_processes auto是让Nginx根据CPU核心数自动决定worker进程数,省得手动算。worker_rlimit_nofile是提升文件描述符上限,在线上的高并发场景非常关键,不然连接数一多就会报too many open files。sendfile on是提升静态文件传输效率的,利用内核的sendfile系统调用直接拷贝数据,减少用户态和内核态的切换。
4.2 一份最常用的server配置示例
实际工作中,大多数场景是把Nginx当作网站服务器或者反向代理。我贴一份适合测试环境的完整server块配置,你直接替换上面配置里的server部分即可:
server { listen 8080; server_name your.domain.com; charset utf-8; access_log /var/log/nginx/your.domain.access.log main; error_log /var/log/nginx/your.domain.error.log; location / { root /var/www/yourproject; index index.html index.htm; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里有些细节值得注意:try_files $uri $uri/ /index.html是前端单页应用部署常见的写法,路由是history模式时刷新页面不会404,这行配置能把所有未命中的请求都返回给index.html。proxy_set_header三行是必要的,否则后端拿不到真实客户端IP,日志排查时会很痛苦。我的习惯是把不同项目的access_log和error_log分开,整个内网的应用都是各记各的,排查问题比全堆在一个主日志里方便多了。
4.3 配置语法检查与平滑重载
改完配置,千万别直接重启Nginx,先做语法检查:
/usr/sbin/nginx -t如果输出syntax is ok和test is successful,说明配置没问题。然后执行平滑重载:
/usr/sbin/nginx -s reload平滑重载的原理是:master进程收到信号后,重新读取配置文件并创建新的worker进程,处理完当前请求后,旧worker会自动退出。这个过程中已有的连接不会中断,这是Nginx在运维体验上最大的优势之一。所以在生产环境,改完配置永远是reload而不是restart,除非你改了listen端口或server_name这类无法热加载的属性。
4.4 反向代理与负载均衡配置速览
做反向代理和负载均衡,是Nginx在内网环境里最常见的使用场景。上游有多台后端服务时,用upstream块做负载均衡:
upstream backend_servers { server 192.168.1.11:8080 weight=3; server 192.168.1.12:8080 weight=1; } server { listen 80; server_name proxy.internal; location / { proxy_pass http://backend_servers; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }weight=3表示相对权重,意思是第一台服务器承受的请求量大约是三倍于第二台。如果不写weight,默认每台权重相同,Nginx会按轮询策略分发请求。离线环境内部署这种代理结构的人不少,很多业务系统做服务拆分后,各个服务的入口统一走Nginx,维护起来很省心。
另外,如果后端是HTTPS接口,Nginx需要在上游请求时做SSL转发,你可以在proxy_pass后面加上https,或者在upstream里增加SSL相关参数。这个场景配置复杂一点,但思路是清晰的:客户端用HTTP访问Nginx,Nginx回源时再走HTTPS。只要你编译时有--with-http_ssl_module,这些都能支持。
5. 高频踩坑实录与排查方法
5.1 编译阶段的三大报错
我在离线安装过程中,碰到最多的编译报错就三类,这里把排查思路写出来。
第一类是checking for pcre library... not found或者C compiler cannot create executables。前者说明configure没找到PCRE,要么是路径没写对,要么是用错了PCRE版本。后者说明gcc编译器本身有问题,可能没装或者环境变量不对。解决方式是先重跑configure,确认--with-pcre=../pcre2-10.42的路径确实存在,再检查gcc --version是否有输出。离线环境最容易出现“源码包没拷全”的情况,我就经历过把Nginx和zlib带进去了,PCRE忘带,configure报错又回去重新传文件,特别折腾。
第二类是make: *** No rule to make target 'build'这类报错。这通常是configure过程没跑完就中断,导致make没有生成正确的Makefile。之所以configure没跑完,大概率是依赖检查时某个库找不到,你直接跳过了错误信息。所以遇到这个报错,我的建议是回到configure那一步,把最后几行日志认真看完,找出它到底卡在哪个库上。
第三类是undefined reference to 'pcre_free_study'。这个报错很典型:版本匹配问题。你在老版本Nginx里传了太新的PCRE2,两个版本的接口对不上。解决方式很直接,换PCRE 8.45重新编译,别硬刚。
5.2 启动阶段的问题
编译安装成功后,执行nginx不报错,但浏览器访问不了,这种问题也很常见。我的排查顺序是固定的:
先看进程有没有起来:
ps -ef | grep nginx再看日志:
tail -100 /var/log/nginx/error.log如果进程起来但页面打不开,大概率是防火墙拦截了端口。内网环境经常有安全组策略,放行规则不归你管,这时候你需要联系运维确认80或者8080端口是否放通。如果日志里报[emerg] bind() to 0.0.0.0:80 failed (13: Permission denied),说明你没有权限监听1024以下端口,需要以root身份启动。
还有一个隐蔽的坑:SELinux。很多CentOS系统默认开着SELinux,它会在权限层面对进程进行额外限制。如果Nginx启动正常、防火墙端口也放行了,但仍然访问不了,就需要检查SELinux:
getenforce输出Enforcing的话,可以把Nginx的端口加入允许列表,或者临时先setenforce 0做测试。注意,setenforce 0只是临时的,重启后SELinux会恢复,生产环境还是建议在SELinux里配置白名单,而不是直接关掉它。
5.3 端口冲突与权限问题
端口冲突也是高频问题。bind() to 0.0.0.0:80 failed (98: Address already in use)这行日志一出,基本上就是80端口被别的东西占了。用下面的命令查看谁占用了端口:
netstat -tlnp | grep :80或者更新的写法:
ss -tlnp | grep :80如果是Apache或者其它Web服务占了80,你需要在重启操作之前决定是停掉旧服务还是让Nginx换端口。千万别在生产环境直接kill进程,先确认那个进程是什么,再决定动作。
权限相关的另一个坑是文件描述符限制。一旦接入的连接数超过系统默认的ulimit值,Nginx会报Too many open files。这时候需要调整两个地方:在nginx.conf里设置worker_rlimit_nofile,同时修改系统的/etc/security/limits.conf:
root soft nofile 65535 root hard nofile 65535 nginx soft nofile 65535 nginx hard nofile 65535改完要退出重新登录终端才生效。
5.4 离线环境依赖缺失的排查思路
离线环境最大的麻烦,就是你没法用yum install或apt-get install来现场补依赖。如果configure阶段发现缺少某个系统库,你只能回有网机器上去下载对应的RPM或deb包再传进去。这就需要一套判断方法。
我的实操习惯是:编译Nginx之前,先在有网机器上把同样版本的系统拉起来,在外网环境完整跑一遍安装流程,记录下它依赖的底层库。然后回到离线机器,逐个检查这些库是否已存在。用ldconfig -p | grep 库名可以快速查看动态库是否在系统缓存里。缺哪个库,就去有网机器下载对应的软件包。比如缺libpcre,你在CentOS上下载pcre和pcre-devel两个RPM,传进离线机器后执行:
rpm -Uvh pcre-*.rpm pcre-devel-*.rpm这里有个很关键的细节:一定要连-devel包一起装。编译Nginx需要的是头文件,光有运行时动态库是不够的。很多新手就是少了这一步,编译时一直提示找不到头文件。
如果你需要安装多个RPM包,可以用yum localinstall的方式,它会尝试解析本地目录里的RPM依赖:
yum localinstall -y /opt/rpms/*.rpm6. 个人实操体会与补充建议
离线安装Nginx这件事,看起来是一串命令,做多了你就会发现它考验的是你对整个系统依赖关系的理解程度。我最开始做的时候也踩过不少坑,比如在configure那一步漏掉了--with-http_ssl_module,导致后面配HTTPS的时候发现不支持SSL,只能重新编译;再比如在一个国产系统上装完了才发现系统内核太老,Nginx 1.24的某些特性跑不起来,最后换了旧版本才稳定。
我的建议是,在准备离线包之前,先花五分钟把目标机器的系统版本、架构、内核信息记下来,再决定用哪个版本的Nginx。这五分钟省下来的时间,可能比后面排查报错花的两小时还多。另外,离线环境下一定要养成记录命令和参数的习惯,因为没法上网查,自己的笔记就是唯一的参考资料。
还有一个小技巧,编译之前先把configure的参数存成一个文件放在/opt目录下,下次重新编译时直接复制执行,不用靠记忆敲。毕竟内网机器一旦多了,每台的系统环境还有些微差异,有个参数备份能省太多事。
最后给几个实用的小建议。第一,安装完成之后,用curl -I http://127.0.0.1测试一下本地访问,确认有HTTP响应再告诉业务方。第二,把Nginx日志做切割,不然时间久了access.log会涨到几个G,排查问题拖都拖不动。第三,定期备份/etc/nginx/nginx.conf,改配置前先cp一份带日期的备份,改挂了还可以快速回滚。这几个习惯看着不起眼,但在项目现场能帮你少挨很多骂。