尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ARM架构下Nginx离线部署实战:源码编译、依赖打包与系统集成

ARM架构下Nginx离线部署实战:源码编译、依赖打包与系统集成 1. 为什么ARM架构下的离线部署值得单独写一篇如果你平时在x86服务器上装nginx大概率就是一条apt install nginx或者yum install nginx的事网络一通几分钟搞定。但一旦落到ARM架构、而且目标机器完全没有外网这件事的复杂度会陡然上升一个量级。我自己第一次在飞腾ARM64的机器上做离线部署时光是搞清楚编译机架构和目标机架构必须一致这一条就折腾了小半天更别提后面动态库依赖、编译参数、systemd托管这些细节了。这篇内容面向的是这样一类场景你手上有一台或者一批ARM架构的服务器可能是鲲鹏、飞腾、Ampere也可能是树莓派这类ARM开发板它们处在内网或者完全隔离的环境中不能访问任何软件源而你需要把nginx稳定地跑起来并且后续还要能配置反向代理、负载均衡、自签名证书这些常见功能。适合的读者包括运维工程师、嵌入式开发转服务端部署的同学以及需要在国产化ARM平台上做项目交付的开发者。核心要解决的问题就三个第一在没有网络的ARM机器上怎么把nginx装上去第二装上去之后依赖怎么补齐第三装完之后怎么配置才能满足实际业务需求。这三个问题环环相扣任何一个环节出问题nginx都跑不起来。下面我按照实际操作的顺序把整个流程拆开讲包括我踩过的坑和验证过的方案。2. 部署前的整体思路与方案选型2.1 三种离线部署路线的取舍在ARM架构下做nginx离线部署主流有三条路源码编译、RPM/DEB包离线安装、容器镜像导入。这三条路没有绝对的好坏关键看你的目标环境和后续维护需求。源码编译是我最推荐的方式尤其当你的目标机器是国产ARM平台比如银河麒麟、统信UOS这类时。原因很简单这些系统的软件源里nginx版本往往偏旧而且不同发行版之间的包格式不通用。源码编译的好处是可控性最强你可以精确指定nginx版本、编译哪些模块、用哪个版本的OpenSSL和PCRE编译出来的二进制文件只依赖目标机上已有的基础库。缺点是编译过程需要在一台同架构的机器上完成不能拿x86编译出来的东西直接扔到ARM上跑。RPM/DEB包离线安装适合目标机器和编译机器是完全相同的操作系统版本的情况。比如你有一批银河麒麟V10的飞腾服务器那在一台上打好RPM包然后rpm -ivh分发到其他机器上效率很高。但一旦系统版本有差异依赖关系就会出问题libssl、libpcre这些库的版本对不上装上去也跑不起来。容器镜像导入是另一种思路把nginx打包成ARM架构的Docker镜像通过docker save和docker load在离线环境里导入。这条路适合已经有容器运行时的环境但很多国产ARM服务器默认不装Docker而且有些场景下不允许用容器所以适用范围相对窄一些。我个人的建议是如果目标机器数量少、系统版本杂走源码编译如果目标机器数量多、系统版本统一走RPM/DEB包如果已经有容器基础设施走镜像导入。下面重点讲源码编译这条最通用、最可控的路线。2.2 编译机与目标机的架构匹配问题这里有一个很多人容易忽略的点编译机的CPU架构必须和目标机一致至少要是同一指令集。ARM架构本身分ARMv7、ARMv8也就是AArch64/ARM64等多个版本你在ARMv7上编译出来的二进制放到ARMv8上不一定能跑反过来也一样。怎么确认目标机的架构在目标机上执行uname -m如果输出aarch64说明是ARM64如果输出armv7l说明是ARMv7。编译机的架构必须和这个输出一致。我遇到过有人拿x86的交叉编译工具链去编译nginx结果编译出来的二进制在ARM上直接报cannot execute binary file: Exec format error这就是架构不匹配的典型表现。如果你手头没有同架构的编译机可以考虑用QEMU模拟但模拟环境下的编译速度会慢很多而且某些依赖库的检测可能会出问题。更稳妥的做法是找一台同架构的机器哪怕是一块ARM开发板也行只要内存够建议至少2GB编译nginx本身并不需要太强的CPU。2.3 依赖库的提前准备nginx的源码编译依赖几个基础库PCRE用于正则表达式rewrite模块需要、zlib用于gzip压缩、OpenSSL用于HTTPS。这三个库在离线环境下必须提前准备好源码包不能等到编译时再去下载。我的习惯是在编译机上建一个专门的目录把所有需要的源码包放进去mkdir -p /opt/nginx-offline/packages cd /opt/nginx-offline/packages然后依次下载并解压# 下载地址以官方为准这里只写文件名 nginx-1.24.0.tar.gz pcre-8.45.tar.gz zlib-1.2.13.tar.gz openssl-1.1.1w.tar.gz版本选择上nginx我一般用稳定版偶数版本号比如1.24.xOpenSSL用1.1.1系列因为1.1.1是长期支持版本兼容性最好。PCRE用8.45zlib用1.2.13这两个版本足够稳定而且编译兼容性好。注意不要用PCRE2去编译老版本的nginx虽然新版本nginx已经支持PCRE2但如果你用的是1.20之前的版本PCRE2会导致编译报错。保险起见PCRE 8.45是万能选择。3. 源码编译与离线打包的完整实操3.1 编译环境的初始化在编译机上先装好基础的编译工具。如果编译机本身能联网这一步很简单# 以CentOS/麒麟为例 yum install -y gcc gcc-c make如果是完全离线的编译机那就需要提前准备好gcc和make的RPM包这个不在本文展开假设编译机具备基本编译能力。接下来创建一个独立的编译目录把所有源码包解压进去cd /opt/nginx-offline tar -zxvf packages/nginx-1.24.0.tar.gz tar -zxvf packages/pcre-8.45.tar.gz tar -zxvf packages/zlib-1.2.13.tar.gz tar -zxvf packages/openssl-1.1.1w.tar.gz解压完成后目录结构应该是这样的/opt/nginx-offline/ ├── nginx-1.24.0/ ├── pcre-8.45/ ├── zlib-1.2.13/ ├── openssl-1.1.1w/ └── packages/3.2 configure参数的选择与计算进入nginx源码目录执行configure。这一步是整个编译过程中最关键的参数选错了后面要么编译失败要么功能缺失。cd /opt/nginx-offline/nginx-1.24.0 ./configure \ --prefix/usr/local/nginx \ --with-pcre/opt/nginx-offline/pcre-8.45 \ --with-zlib/opt/nginx-offline/zlib-1.2.13 \ --with-openssl/opt/nginx-offline/openssl-1.1.1w \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-stream \ --with-stream_ssl_module \ --with-threads逐个解释这些参数为什么这么选--prefix/usr/local/nginx指定安装路径。我习惯用/usr/local/nginx因为这是nginx的默认路径后续配置文件和日志路径都符合大多数人的直觉。你也可以改成/opt/nginx但要注意后续systemd服务文件里的路径也要跟着改。--with-pcre、--with-zlib、--with-openssl这三个参数指向的是源码目录不是安装目录。nginx的configure脚本会自己去编译这些依赖把它们静态链接进nginx二进制里。这样做的好处是目标机上不需要额外安装这些库的开发包减少了依赖问题。--with-http_ssl_module是HTTPS支持必须加。--with-http_v2_module是HTTP/2支持现在大部分场景都需要。--with-http_realip_module用于获取真实客户端IP如果你前面有负载均衡或者CDN这个模块很有用。--with-http_gzip_static_module支持预压缩的静态文件能减少CPU开销。--with-http_stub_status_module提供状态监控接口方便排查问题。--with-stream和--with-stream_ssl_module是四层代理支持如果你需要代理MySQL、Redis或者做TCP转发这两个模块必须加。--with-threads启用线程池在高并发场景下能提升性能。实操心得configure执行完后仔细看输出里的Configuration summary。如果某个模块显示not found说明对应的依赖没找到需要检查路径是否正确。我曾经因为PCRE路径写成了安装目录而不是源码目录导致rewrite模块没编译进去后来重新configure才发现。3.3 编译与安装configure通过后执行编译make -j$(nproc)-j$(nproc)表示用所有CPU核心并行编译能显著加快速度。在ARM机器上如果核心数少编译时间可能会比较长耐心等就行。编译完成后不要急着make install先做一件事把编译好的二进制文件和配置文件打包。因为我们的目标是在离线机器上部署直接在编译机上install没有意义。make install DESTDIR/opt/nginx-offline/nginx-installDESTDIR参数会把安装文件放到指定目录而不是真正的/usr/local/nginx。这样我们就可以把这个目录整体打包传到目标机上再解压。安装完成后检查一下生成的文件ls -la /opt/nginx-offline/nginx-install/usr/local/nginx/应该能看到sbin/nginx、conf/、html/、logs/这几个目录。其中sbin/nginx就是最终的可执行文件。3.4 打包与传输把安装目录打包成一个压缩文件cd /opt/nginx-offline/nginx-install tar -czvf nginx-arm64-offline.tar.gz usr/local/nginx/这个压缩包就是最终要传到目标机上的东西。传输方式根据你的环境来U盘、内网scp、或者通过跳板机都行。假设你已经把包传到了目标机的/tmp目录下。4. 目标机上的部署与系统集成4.1 解压与目录规划在目标机上先解压cd /tmp tar -zxvf nginx-arm64-offline.tar.gz解压后会得到usr/local/nginx/目录。把它移动到正式位置mv usr/local/nginx /usr/local/nginx如果你希望nginx的配置文件和日志放在其他位置比如/etc/nginx和/var/log/nginx可以在解压后手动调整但要注意修改nginx.conf里的路径。我一般保持默认减少出错概率。4.2 动态库依赖检查这是离线部署最容易出问题的地方。nginx虽然静态链接了PCRE、zlib、OpenSSL但它仍然依赖系统的glibc和libpthread等基础库。在目标机上执行ldd /usr/local/nginx/sbin/nginx如果输出里有not found说明缺少某个动态库。常见的情况是目标机的glibc版本比编译机低导致nginx依赖的glibc符号找不到。这种情况下要么在更低版本的系统上重新编译要么升级目标机的glibc风险较高不建议。我遇到过一次编译机是麒麟V10 SP2目标机是SP1glibc版本差了0.1结果nginx启动时报GLIBC_2.28 not found。后来在SP1的机器上重新编译了一遍才解决。所以编译机和目标机的操作系统版本尽量保持一致这是最省事的做法。4.3 创建nginx运行用户出于安全考虑不要让nginx以root身份运行。创建一个专用用户groupadd -r nginx useradd -r -g nginx -s /sbin/nologin -d /usr/local/nginx -M nginx然后在nginx.conf里指定user nginx nginx;这样master进程仍然以root启动为了绑定80端口但worker进程会切换到nginx用户降低了安全风险。4.4 systemd服务托管为了让nginx能开机自启、方便管理创建一个systemd服务文件vim /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/bin/kill -s HUP $MAINPID ExecStop/bin/kill -s QUIT $MAINPID PrivateTmptrue [Install] WantedBymulti-user.target然后重载systemd并启动systemctl daemon-reload systemctl enable nginx systemctl start nginx systemctl status nginx如果状态显示active (running)说明部署成功。注意有些ARM平台的systemd版本较老可能不支持PrivateTmp选项如果启动报错把这行删掉即可。5. 部署后的核心配置与功能验证5.1 反向代理配置nginx装好之后最常见的用途就是反向代理。假设你后端有一个跑在8080端口的应用配置如下server { listen 80; server_name example.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }这几个proxy_set_header很重要尤其是X-Real-IP和X-Forwarded-For后端应用需要通过它们获取真实客户端IP。如果不加后端看到的全是nginx的IP。5.2 负载均衡配置如果有多个后端实例可以用upstream做负载均衡upstream backend { least_conn; server 192.168.1.101:8080 weight3; server 192.168.1.102:8080 weight1; server 192.168.1.103:8080 backup; } server { listen 80; location / { proxy_pass http://backend; } }least_conn表示按最少连接数分发适合长连接场景。weight是权重backup是备用节点只有主节点全部不可用时才会启用。5.3 自签名证书配置HTTPS在内网环境里没有公网CA签发的证书自签名证书是最实际的选择。生成证书mkdir -p /usr/local/nginx/conf/ssl cd /usr/local/nginx/conf/ssl openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout nginx.key -out nginx.crt \ -subj /CCN/STBeijing/LBeijing/OInternal/CNexample.com然后在nginx.conf里配置server { listen 443 ssl; server_name example.com; ssl_certificate /usr/local/nginx/conf/ssl/nginx.crt; ssl_certificate_key /usr/local/nginx/conf/ssl/nginx.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { root html; index index.html; } }配置完成后用nginx -t检查语法然后systemctl reload nginx重载配置。5.4 验证部署结果几个关键的验证命令# 检查nginx版本和编译参数 /usr/local/nginx/sbin/nginx -V # 检查配置文件语法 /usr/local/nginx/sbin/nginx -t # 检查端口监听 ss -tlnp | grep nginx # 本地访问测试 curl -I http://127.0.0.1如果curl返回200 OK说明nginx已经正常工作。6. 常见问题排查与避坑经验6.1 启动报错error while loading shared libraries这是离线部署最高频的问题。原因通常是目标机缺少某个动态库或者库的版本不匹配。排查步骤ldd /usr/local/nginx/sbin/nginx | grep not found根据输出找到缺失的库然后从编译机上把对应的.so文件拷贝到目标机的/usr/lib64或/usr/local/lib下。如果是因为glibc版本不匹配那就只能在目标机上重新编译没有其他好办法。6.2 端口被占用如果80端口已经被其他服务占用nginx启动会报bind() to 0.0.0.0:80 failed (98: Address already in use)。用ss -tlnp | grep :80找到占用进程要么停掉它要么把nginx换到其他端口。6.3 权限问题导致403 Forbiddennginx的worker进程以nginx用户运行如果网站根目录的权限不允许nginx用户读取就会返回403。检查ls -la /usr/local/nginx/html/ chown -R nginx:nginx /usr/local/nginx/html/ chmod -R 755 /usr/local/nginx/html/6.4 常见问题速查表问题现象可能原因解决方法启动报GLIBC版本错误编译机glibc版本高于目标机在目标机或同版本机器上重新编译403 Forbidden目录权限不足调整目录所有者和权限502 Bad Gateway后端服务未启动或端口不通检查后端服务状态和防火墙nginx -t报语法错误配置文件缺少分号或括号不匹配根据报错行号逐行检查systemctl启动失败service文件路径错误检查ExecStart路径和PIDFile路径HTTPS访问报证书错误自签名证书未被信任客户端导入证书或使用忽略证书校验6.5 几个我踩过的坑第一个坑是编译时用了--with-cc-opt优化参数导致二进制不兼容。有一次我加了-marchnative结果编译出来的nginx在另一台ARM机器上跑不了因为那台机器的CPU不支持某些指令。后来改成-O2就没事了。所以不要用-marchnative除非你确定所有目标机的CPU型号完全一致。第二个坑是日志目录权限。nginx启动时需要写logs/error.log和logs/nginx.pid如果这些文件的所有者是root而worker进程是nginx用户就会写不进去。解决办法是提前把logs目录的权限设好chown -R nginx:nginx /usr/local/nginx/logs第三个坑是SELinux。有些国产ARM系统默认开启SELinux会阻止nginx绑定非标准端口或者访问某些目录。如果排查了半天权限没问题但还是报错可以临时用setenforce 0测试一下如果问题消失说明是SELinux的策略问题需要针对性配置或者关闭。7. 后续扩展与维护建议nginx跑起来之后后续的维护其实比部署本身更重要。我一般会做几件事配置日志轮转避免日志文件无限增长设置监控通过stub_status模块暴露状态数据定期检查证书有效期自签名证书虽然有效期长但也不能忘了续期。日志轮转可以用系统的logrotate创建一个/etc/logrotate.d/nginx文件/usr/local/nginx/logs/*.log { daily missingok rotate 30 compress delaycompress notifempty create 640 nginx nginx sharedscripts postrotate [ -f /usr/local/nginx/logs/nginx.pid ] kill -USR1 cat /usr/local/nginx/logs/nginx.pid endscript }监控方面在nginx.conf里加一个locationlocation /nginx_status { stub_status on; allow 127.0.0.1; deny all; }然后curl http://127.0.0.1/nginx_status就能看到当前连接数、请求数等指标。如果后续需要升级nginx版本流程和初次部署一样在编译机上编译新版本打包传到目标机替换sbin/nginx然后systemctl reload nginx。配置文件一般不需要改但建议升级前先备份。最后分享一个我在多台ARM机器上批量部署时用的小技巧把整个部署过程写成一个shell脚本包括解压、移动目录、创建用户、配置systemd、启动服务这些步骤然后通过内网批量分发执行。这样即使有几十台机器也能在半小时内全部搞定。脚本里记得加错误检查和日志输出方便排查哪台机器出了问题。
返回列表