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

资讯详情

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

信创环境Nginx离线部署:从源码编译到一键安装包实践

信创环境Nginx离线部署:从源码编译到一键安装包实践 简介面向信创国产化环境的Web服务部署需求离线安装包支持银河麒麟、欧拉、统信等主流国产操作系统及更多常用国产发行版并针对ARM六十四位架构进行优化。它解决了内网隔离环境下无法在线获取依赖的难题内置Nginx以及关联的PCRE、Zlib、OpenSSL源码安装时自动完成编译。资源共有九个文件包含源码压缩包、安装与卸载脚本、预置的配置文件、系统服务文件以及说明文档整体体积不到二十兆字节目录结构清晰按源码、依赖、配置、服务模块划分便于维护。安装后默认部署在指定目录并注册为系统服务可通过常用的系统命令启动或停止。目前已有四百六十九人学习适合在政务、金融等国产化替代项目中快速搭建高性能Web服务器降低手动编译复杂度提升交付效率。 上周在客户现场处理完最后一个联调问题已经是凌晨。其实业务需求特别简单一套物理隔离的内网环境银河麒麟V10ARM版要把Nginx作为统一流量入口部署上去承接后面的业务系统。环境没有任何外网访问能力yum源指向内网镜像可镜像里偏偏缺pcre、openssl这些关键开发包。最开始我想走捷径——把开发机上编译好的Nginx二进制直接拷贝过去结果一条Exec format error就把计划全部打乱x86_64 的产物在 aarch64 机器上根本跑不起来。这个场景不是个例。这几年跑信创项目交付我见过太多系统是国产的、架构是多样的、网络是隔离的尴尬现场。后来我把重复做的事固化下来做了个 “Nginx离线一键安装包”。它不是把源码包打个压缩包那么简单而是把编译工具链、依赖源码、Nginx本身、配置模板、systemd托管脚本全部揉成一个包到目标机上跑一条命令就能装好。这篇文章就把这套方案的设计思路、脚本逻辑和踩过的坑完整拆出来。1. 信创机房里的部署现实离线不是“没有网”这么简单1.1 操作系统和包管理器的割裂信创环境里最常见的操作系统我接触到的至少有银河麒麟、中标麒麟、统信UOS、中科方德、openEuler这几类。它们底层有的走RPM系yum/dnf有的走DEB系apt命令体系和软件仓库完全不一样。这就带来一个头疼问题如果按传统思路在开发机上下载好RPM包、拷到现场用rpm -ivh nginx.rpm安装你首先得确认目标系统是哪个发行版、哪个版本。同一个Nginx版本在麒麟V10上是RPM在统信UOS 20上可能是DEB而且依赖关系还各不相同。也就是说靠单一种类安装包去覆盖信创全场景基本是走不通的。1.2 真正的坑在CPU架构如果说操作系统还能靠“多准备几套包”应付过去CPU架构就是必须认真对待的另一个变量。目前国产服务器CPU最常见的是x86_64兆芯、海光等、aarch64鲲鹏、飞腾此外还有loongarch64龙芯、sw_64申威。Nginx源码本身对多种架构支持得不错但编译产物是完全不通用的。x86_64上编出来的二进制拿到aarch64上直接报错反过来也一样。这就意味着做安装包时不能只做一个“通用包”至少要按CPU架构分目录存放编译产物或者干脆采用“目标机本地源码编译”的方式让每一台机器都用自己的编译器生成二进制。后者兼容性最好也是我最后选择的路线。1.3 开发包缺失和编译器版本滞后离线环境下最难受的还不是没有Nginx而是没有编译依赖。Nginx源码编译需要 gcc、make、libpcre、libssl、zlib 等一堆开发库。在内网机器的默认软件源里这些包往往不全特别是 openssl-devel、pcre-devel 这类开发包。更麻烦的是部分老版本国产系统的gcc版本偏低。比如某款麒麟V10系统自带gcc 4.8.5连Nginx 1.24 OpenSSL 1.1.1的常规组合都编得吃力OpenSSL 3.x 更是要求gcc 5以上。所以做安装包时除了Nginx源码还得把依赖源码和编译器工具链一并带进去否则现场照样卡住。2. 安装包设计为什么坚持源码编译而不是复制RPM2.1 二进制拷贝为什么容易翻车很多同事一听到离线部署第一反应是找一台同系统同架构的机器把Nginx目录打包拷过去。听起来省事实际雷点很多。首先动态库依赖。你本地ldd nginx会看到 libpcre.so、libssl.so、libcrypto.so 这些它们分散在系统不同目录。拷贝时只拷了/usr/local/nginx没带动态库目标机启动Nginx时就会报error while loading shared libraries。就算你把动态库一起拷过去还要处理LD_LIBRARY_PATH、ldconfig 缓存麻烦事不少。其次glibc版本差异。在CentOS 7上编译的Nginx拿到openEuler或麒麟新系统上可能因为目标机器glibc版本比编译环境低启动时直接报GLIBC_2.28 not found。这类问题排查起来非常隐蔽现场没网时想补都补不了。所以除非你能保证编译机和目标机“同发行版、同大版本、同架构、同glibc”否则二进制拷贝只适合做应急手段不适合做成标准交付方案。2.2 RPM/DEB只能当辅助手段利用目标机自带的包管理器离线安装依赖也是一个思路在能联网的机器上下载全套RPM/DEB拷到现场rpm -ivh *.rpm安装。这个方案的问题同样明显下载依赖时要核对依赖链Nginx依赖的openssl、pcre可能又依赖其他库一环扣一环。不同发行版的RPM包不能通用DEB包也一样。你至少要维护CentOS系、openEuler系、Debian系三套离线包每套包光收集依赖就要花不少时间。所以我把 RPM/DEB 离线工具链定位为“兜底手段”如果目标机器没有 gcc/make安装包自动尝试通过 tools 目录下的离线包把编译链装好如果装不上再提示用户联系编译环境。主要安装路径还是源码编译。2.3 源码编译静态依赖的可行路径源码编译最大的好处是兼容性最强只要目标机有可用的C编译器和make理论上任何架构都能编。为了让这个方案更可靠我对依赖做了“静态编入”处理。这里说的静态编入不是指直接编译一个纯静态二进制而是利用Nginx configure 机制把 pcre、zlib、openssl 的源码目录直接传给Nginx让Nginx在编译内部模块时自己带上这些依赖。具体来说就是 configure 参数里加--with-pcre../pcre-8.45 --with-zlib../zlib-1.3 --with-openssl../openssl-1.1.1w这样编出来的Nginx在加载SSL、Rewrite、Gzip这些功能时不会再去系统里找 libpcre.so、libssl.so减少了大量动态库兼容问题。这种做法的代价是编出来的二进制会比直接从系统装稍微大一点但换来的是跨系统部署的稳定性值。2.4 安装包的目录结构设计我的安装包最终目录结构大致如下nginx-offline-install/ ├── install.sh ├── README.txt ├── checksums.md5 ├── conf/ │ ├── nginx.conf.tmpl │ └── ssl-site.conf.example ├── src/ │ ├── nginx-1.24.0.tar.gz │ ├── pcre-8.45.tar.gz │ ├── zlib-1.3.tar.gz │ └── openssl-1.1.1w.tar.gz └── tools/ ├── rpms/ └── debs/src目录放所有源码包conf放配置模板tools放离线编译工具链。这个结构有点冗余但好处是现场人员拿到后不需要做任何额外判断直接执行 install.sh 就行出问题时也能根据目录名快速定位。3. install.sh 核心逻辑拆解从环境检测到systemd托管3.1 安装前环境检测脚本第一步不是急着解压编译而是先确认“这台机器能不能装、该按什么方式装”。我写了一个环境检测函数逻辑大概是detect_env() { if [ $(id -u) -ne 0 ]; then echo 请使用root用户执行 exit 1 fi SYS_VERSION$(cat /etc/os-release | grep -E ^(ID|VERSION_ID) | tr \n ) ARCH$(uname -m) echo 当前系统: $SYS_VERSION echo 当前架构: $ARCH if ss -lnp | grep -q :80 ; then echo 端口80已被占用请先处理 exit 1 fi grep -q ^www: /etc/passwd || useradd -r -s /sbin/nologin www }这个检测有两个容易被忽略的地方一是必须用root执行因为后面要写/usr/local/nginx、要注册systemd服务二是提前检查80端口避免启动时报“address already in use”现场排查起来更麻烦。另外我把Nginx的worker进程用户固定为www保证Web根目录和日志目录的权限统一管理。3.2 依赖构建与Nginx编译参数检测通过后先解压源码包cd src tar zxf nginx-1.24.0.tar.gz tar zxf pcre-8.45.tar.gz tar zxf zlib-1.3.tar.gz tar zxf openssl-1.1.1w.tar.gz然后执行configure。我的固定参数组合是这样的cd nginx-1.24.0 ./configure \ --prefix/usr/local/nginx \ --userwww \ --groupwww \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_stub_status_module \ --with-http_realip_module \ --with-http_gzip_static_module \ --with-stream \ --with-stream_ssl_module \ --with-pcre../pcre-8.45 \ --with-zlib../zlib-1.3 \ --with-openssl../openssl-1.1.1w \ --with-cc-opt-O2 make -j$(nproc) make install这里解释几个关键选择--with-http_v2_module对应HTTP/2现在对外提供服务基本都会用到--with-stream是为了支持TCP/UDP四层转发在信创环境里经常要用来做数据库端口的反向代理--with-pcre...、--with-openssl...指向源码目录就是前面说的静态编入思路。需要特别说明的是make -j$(nproc)用所有CPU核心并行编译是为了缩短编译时间。在部分虚拟机环境中 nproc 返回的核数比较多但单核性能一般并行编译可能把内存吃满如果现场机器内存小于2G建议把-j参数固定为2。3.3 动态生成nginx.conf编译安装完成后直接用固定配置不太合理不同机器的CPU核数、内存大小差别很大。我写了一个基于模板的配置生成逻辑WORKERS$(nproc) MEM_KB$(grep MemTotal /proc/meminfo | awk {print $2}) if [ $MEM_KB -lt 2097152 ]; then CONNS1024 elif [ $MEM_KB -lt 8388608 ]; then CONNS2048 else CONNS4096 fi sed -e s/__WORKERS__/$WORKERS/g \ -e s/__CONNS__/$CONNS/g \ conf/nginx.conf.tmpl /usr/local/nginx/conf/nginx.conf模板文件里核心部分长这样worker_processes __WORKERS__; events { worker_connections __CONNS__; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; include /usr/local/nginx/conf/conf.d/*.conf; }worker_connections 根据内存大小动态计算这是一个经验值内存越大的机器可以并发处理更多连接盲目改成65535在低配机器上反而容易把内存耗尽。3.4 systemd服务注册与启动脚本最后一步是让Nginx能开机自启、能用systemctl管理。我直接写入unit文件cat /usr/lib/systemd/system/nginx.service EOF [Unit] DescriptionNginx Offline Install Afternetwork.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 ExecReload/usr/local/nginx/sbin/nginx -s reload ExecStop/usr/local/nginx/sbin/nginx -s stop PrivateTmptrue [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable nginx systemctl start nginxExecStartPre里执行nginx -t是个好习惯如果配置文件有语法错误启动会直接被拦截不会出现“服务起来了但页面打不开”这种诡异问题。3.5 一个开箱即用的SSL站点配置片段安装完Nginx之后现场最常问的问题是“证书怎么配”。我在安装包里附了一个ssl-site.conf.example内容很典型server { listen 443 ssl; server_name example.local; ssl_certificate /etc/nginx/certs/server.pem; ssl_certificate_key /etc/nginx/certs/server.key; ssl_protocols TLSv1.2 TLSv1.3; location / { root /opt/www; index index.html; } }这里要提醒一下信创环境里部分旧版浏览器/TLS库对TLS 1.3支持不佳如果现场兼容性测试失败可以把ssl_protocols临时改成TLSv1.2优先保证业务可用。4. 真机部署踩坑记录这些问题我建议你提前躲开4.1 ARM架构的“Exec format error”与编译陷阱文章开头提到的Exec format error本质上是CPU指令集不匹配。很多人第一次遇到会以为文件损坏其实是把x86_64的二进制放到了aarch64机器上。更隐蔽的一个坑是在aarch64上用部分老版本交叉编译工具链编Nginx时链接Openssl会报unknown type name u_int64_t这是因为部分工具链的头文件不完整。我的规避办法是尽量在目标机本地源码编译不要用交叉编译除非你确信工具链完整。4.2 OpenSSL版本选择和编译失败早期我图省事在安装包里放了OpenSSL 3.0.x源码。结果在gcc 4.8的机器上编译直接报错因为OpenSSL 3.x要求gcc 5以上。后来我把默认版本降到OpenSSL 1.1.1w兼容性立刻好了很多。虽然1.1.1在2023年停止维护但信创环境的系统库普遍还停留在这一代作为离线部署的基础版本是合理选择。另外遇到一个诡异问题OpenSSL源码目录如果之前被配置过比如直接执行过./configNginx的configure阶段会报错。解决办法是重新解压一个干净的源码包。4.3 SELinux/AppArmor拦截导致403或启动失败在麒麟、统信这类系统上安全模块默认开启的情况比CentOS还普遍。我遇到过两种典型问题第一种Nginx启动后访问首页返回403。查日志发现是SELinux拦截了Nginx对/opt/www目录的读取。快速验证方法是用setenforce 0临时关闭SELinux再访问如果能通就基本锁定是SELinux策略问题。生产环境不建议直接关闭SELinux可以用audit2allow -a -M nginx_www生成自定义策略模块加载。第二种某个服务器版本上Nginx启动后监听端口正常但systemd状态显示启动失败。排查后是AppArmor对/usr/local/nginx写路径有限制。这类问题看/var/log/audit/audit.log或dmesg就能发现比盲目改配置高效得多。4.4 防火墙放行问题安装包默认只操作系统和Nginx不会去动防火墙配置。结果很多现场装了之后本地curl 127.0.0.1有响应换成局域网IP访问就超时。第一次碰到这类问题时我直接进入“这是不是网络问题”的排查误区最后才发现是firewalld默认只放行了22端口。后来我在脚本里加了一个可选项如果检测到firewalld在运行就提示是否放行80/443端口由现场人员确认而不是自动开。自动开端口在一些安全要求严格的客户环境里是违规操作这点要特别注意。4.5 最容易忽视的系统时间和SSL证书还有一次比较尴尬的现场证书装好、配置也没问题但浏览器访问时总是提示证书无效。排查了半天才发现服务器系统时间比真实时间整整快了两年。证书有效期校验不过是所有证书类故障里最容易被忽略的一种。所以我在README和验证清单里都加了一条安装SSL证书前先执行date确认系统时间。如果发现时间不对先timedatectl set-ntp false、date -s修正后再生成或导入证书。5. 现场交付验证清单怎么确认安装包真的“没问题”交付的时候我不会只丢给运维一句“装好了”。我会按下面的清单逐项确认检查项命令/方法预期结果编译模块/usr/local/nginx/sbin/nginx -V包含ssl、v2、stream等模块配置语法/usr/local/nginx/sbin/nginx -t显示syntax is ok本机访问curl -I http://127.0.0.1/返回200或403无index时服务托管systemctl status nginxactive (running)开机自启systemctl is-enabled nginxenabled远程访问从另一台机器curl能通若不通查防火墙SSL验证openssl s_client -connect 127.0.0.1:443能正常返回证书信息这套清单看起来简单但每一项都对应真实事故漏看nginx -V导致某个模块没编进去、没检查开机自启导致服务器重启后服务没起来、没做远程访问测试导致上线时才发现防火墙没放行。最后再分享一个我养成的小习惯安装包内除了install.sh我还会放一个rollback.sh和backup/目录用来在安装前备份旧的/usr/local/nginx如果存在和 nginx.service 文件。现场升级或者替换场景里这个备份往往能在出问题时帮你省出两小时排查时间。离线环境下的安装包给现场人员多留一条退路永远比追求“绝对不出错”更实际。本文还有配套的精品资源点击获取
返回列表