
1. 为什么离线部署LNMP值得单独拿出来讲搞过内网交付的人都有一个共识能联网的环境里装LNMP一条包管理命令的事一旦进了没有外网出口的机房同样一套东西难度直接翻好几倍。centos7.9离线部署LNMP这个标题说的就是在完全断网或者只开放极少数端口的CentOS 7.9服务器上把Linux、Nginx、MySQL、PHP这套经典Web运行环境从零搭起来。它解决的核心问题不是怎么装软件而是在没有网络的情况下怎么把依赖关系、软件包、配置、服务启动这一整条链路走通。这套东西适合谁看一类是做内网项目交付的运维和实施人员客户机房物理隔离机器搬进去就出不来一类是负责等保、专网、生产隔离区环境搭建的工程师还有一类是想在自己实验室或测试环境里复现一套可控Web栈的开发者。哪怕你只是想在虚拟机上练手把离线流程走一遍对理解RPM依赖、systemd服务管理、PHP与Nginx的通信机制都有实打实的帮助。我先说清楚一个前提CentOS 7.9已经在2024年6月30日停止维护官方源和镜像站陆续下架这恰恰让离线部署从可选项变成了必选项——你没法再指望在线yum install了所有包必须提前准备好。这也是为什么这个话题最近被反复提起。下面我按实际交付的顺序把整套流程拆开讲包括我踩过的坑和几个能省半天时间的技巧。2. 整体方案设计与离线物料准备2.1 为什么选本地源离线RPM而不是源码编译离线装LNMP有两条路源码编译和RPM离线源。很多人第一反应是源码编译更可控但我实际交付下来除非客户明确要求特定版本或需要打补丁否则优先选RPM离线源。原因很直接源码编译要处理gcc、make、cmake、各种-devel包这些依赖本身在离线环境里就是一堆麻烦而且编译参数一旦记错排查成本极高。RPM方案的优势是依赖关系由yum的本地仓库自动解析装完即用systemd服务单元也是现成的。具体做法是在一台能联网、且系统版本与目标机完全一致的CentOS 7.9机器上这点极其重要版本不一致会导致glibc依赖对不上用yumdownloader或reposync把Nginx、MySQL、PHP及其全部依赖下载下来打包成一个本地仓库目录拷到目标机后配置local.repo指向它。这样目标机上执行yum install时yum会从这个本地目录解析依赖效果和在线一模一样。注意准备物料的机器和目标机必须是同一小版本都是7.9架构一致都是x86_64。我见过用7.6的机器准备包结果目标机7.9上glibc版本不匹配装到一半报依赖冲突只能重来。2.2 离线物料清单与目录规划物料准备阶段我习惯按下面的结构组织拷过去之后一目了然目录内容说明/opt/offline/rpm-repo所有RPM包及repodata本地yum源根目录/opt/offline/softNginx/MySQL/PHP安装包若用官方二进制包则放这里/opt/offline/conf预置配置文件nginx.conf、my.cnf、php.ini/opt/offline/scripts部署脚本初始化、启动、校验脚本软件版本选择上CentOS 7.9自带的PHP是5.4太老实际项目基本都要7.x。我的常规组合是Nginx 1.20、MySQL 5.7或8.0、PHP 7.4。这里有个关键决策点——PHP 7.4在CentOS 7上需要额外启用remi源或epel源来获取所以准备物料时要把这些源的包一起down下来。MySQL 8.0对glibc要求较高如果目标机是很老的硬件环境5.7更稳妥。物料下载的核心命令在联网准备机上执行# 安装下载工具 yum install -y yum-utils createrepo # 创建仓库目录 mkdir -p /opt/offline/rpm-repo # 下载nginx、mysql、php及其依赖--resolve 解析全部依赖 yumdownloader --resolve --destdir/opt/offline/rpm-repo \ nginx mysql-server php php-fpm php-mysql php-gd php-mbstring # 生成repodata索引 createrepo /opt/offline/rpm-repo--resolve这个参数是灵魂它会把依赖树整个拉下来少了它你到目标机上必然缺包。下载完用createrepo生成索引目标机的yum才能识别这是一个合法仓库。2.3 依赖完整性的预校验思路物料拷到目标机之前我强烈建议先在准备机上做一次模拟安装校验。方法是在准备机上临时把本地源配好执行yum install --downloadonly或者干脆yum deplist看依赖是否齐全。更彻底的做法是找一台干净的同版本虚拟机断网后实际装一遍能装通再往客户现场搬。这一步花半小时能避免现场抓瞎两小时。还有一个容易被忽略的点SELinux和防火墙。CentOS 7.9默认SELinux是enforcing离线环境下如果没提前处理Nginx访问PHP文件、MySQL写数据目录都可能被拦。我的做法是部署脚本里统一把SELinux设为permissivesetenforce 0并改配置文件防火墙按需放行80/443/3306而不是等到出问题再回头查。这个决策的理由是内网交付环境通常已有边界防护主机层再叠一层SELinux规则排查成本远大于收益。3. 核心组件安装与配置细节拆解3.1 本地yum源的落地配置目标机上第一步是把本地源配起来。把/opt/offline/rpm-repo整个目录拷到目标机同路径然后新建/etc/yum.repos.d/local.repo[local] nameLocal Offline Repo baseurlfile:///opt/offline/rpm-repo enabled1 gpgcheck0gpgcheck0是因为离线包通常没导入GPG key关掉校验避免报错。配好后执行yum clean all yum makecache如果能看到仓库元数据加载成功说明源通了。这里有个细节如果目标机原来配了在线源建议把/etc/yum.repos.d/下其他repo文件临时改名或移走否则yum会尝试连外网超时拖慢整个安装过程。提示yum makecache报repodata不存在时八成是createrepo没执行或者目录层级不对。baseurl指向的必须是包含repodata子目录的那一层。3.2 Nginx安装与关键配置项源通了之后yum install -y nginx基本一路顺风。装完先别急着启动几个配置项要提前改。Nginx默认的nginx.conf里user是nginxworker_processes是1生产环境我一般改成worker_processes auto让它按CPU核数自动分配。worker_connections默认1024内网高并发场景可以提到4096。真正影响LNMP联动的是location ~ \.php$这一段。默认配置里fastcgi_pass指向127.0.0.1:9000这要求PHP-FPM监听9000端口。我倾向于保持TCP监听而不是unix socket原因是跨进程排查时ss -lntp能直接看到端口状态比查socket文件直观。对应的PHP-FPM配置在/etc/php-fpm.d/www.conf要确认listen 127.0.0.1:9000user和group与Nginx保持一致都是nginx否则会出现权限拒绝导致502。fastcgi_param SCRIPT_FILENAME这一行必须指向$document_root$fastcgi_script_name这是Nginx把PHP文件路径传给PHP-FPM的关键。很多PHP页面返回空白或下载文件的问题根源就是这行配错或者被注释掉了。3.3 MySQL初始化与安全配置yum install -y mysql-server装完后CentOS 7上MySQL 5.7/8.0首次启动会自动初始化数据目录。启动命令systemctl start mysqld然后从日志/var/log/mysqld.log里找临时root密码8.0才有5.7默认空密码。拿到后执行mysql_secure_installation改密码、删匿名用户、禁止root远程登录。这里有个离线环境特有的坑mysql_secure_installation的密码强度策略在8.0里默认是MEDIUM要求大小写数字符号齐全随便设个简单密码会报错。如果项目允许可以在my.cnf里加validate_password.policyLOW降低要求但生产环境我不建议这么干老老实实设个复杂密码更省心。数据目录权限也要留意/var/lib/mysql的属主必须是mysql用户如果之前有人手动改过权限启动会失败。字符集方面my.cnf里加上character-set-serverutf8mb4和collation-serverutf8mb4_general_ci避免中文乱码——这是国内项目的高频需求提前配好省得后面改表。3.4 PHP-FPM与扩展的联动PHP装完核心包后php-fpm服务要单独启动。扩展方面php-mysql连接MySQL、php-gd图像处理、php-mbstring多字节字符串、php-xml基本是标配物料准备阶段就要一起down下来。装完用php -m确认扩展加载用php -v确认版本。PHP-FPM的进程管理参数值得调一调。pm dynamic配合pm.max_children、pm.start_servers这几个值默认配置偏保守。内网小项目max_children设20-30够用具体按内存估算每个PHP进程约占30-50MBmax_children乘以单进程内存不能超过可用内存的70%。这个计算过程很多人跳过结果并发一上来就OOM。Nginx和PHP-FPM都起来后在web根目录放一个info.php内容?php phpinfo(); ?浏览器访问验证。能出页面说明整条链路通了出502说明PHP-FPM没起或端口不对出403说明权限或SELinux问题直接下载文件说明Nginx没把php交给FPM处理。这四种现象基本覆盖了90%的联动故障记住这个对照关系排查效率极高。4. 完整实操流程与现场记录4.1 从零到服务可用的执行顺序我把整个部署拆成可脚本化的阶段实际现场按这个顺序走每步都有校验点系统基线检查cat /etc/redhat-release确认7.9uname -m确认x86_64getenforce看SELinux状态systemctl status firewalld看防火墙。这一步是防止在错误的环境上白干。物料落地拷贝/opt/offline目录校验文件完整性md5sum对比准备机生成的清单。本地源配置写local.repoyum clean all yum makecacheyum repolist确认local源可见。组件安装yum install -y nginx mysql-server php php-fpm php-mysql php-gd php-mbstring观察是否有依赖报错。配置下发把预置的nginx.conf、my.cnf、www.conf覆盖到对应位置。服务启动与开机自启systemctl enable --now nginx php-fpm mysqld。联动验证放info.phpcurl -I http://127.0.0.1/info.php看返回200。安全收尾mysql_secure_installation清理测试文件SELinux置permissive。这个顺序的关键在于先校验环境再动手以及配置先于启动。我见过先启动服务再改配置结果改完忘了reload排查半天以为是配置没生效。4.2 关键配置文件的实操内容Nginx的server块我通常这样写直接可抄server { listen 80; server_name _; root /var/www/html; index index.php index.html; location / { try_files $uri $uri/ 404; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }try_files这行保证静态文件找不到时返回404而不是把请求漏给PHP安全性和性能都更好。server_name _表示匹配所有域名内网IP访问场景够用。PHP-FPM的www.conf关键几行listen 127.0.0.1:9000 user nginx group nginx pm dynamic pm.max_children 30 pm.start_servers 5 pm.min_spare_servers 5 pm.max_spare_servers 10pm.max_children 30是按一台4GB内存机器估的30乘40MB约1.2GB留足余量。这个值不是拍脑袋是可用内存×0.7÷单进程内存算出来的你可以按自己机器调整。MySQL的my.cnf最小可用配置[mysqld] datadir/var/lib/mysql socket/var/lib/mysql/mysql.sock character-set-serverutf8mb4 collation-serverutf8mb4_general_ci max_connections200max_connections200对中小项目足够调太高反而占内存。字符集两行是中文项目的刚需。4.3 服务自启与开机验证systemctl enable --now一条命令同时完成开机自启和立即启动比先enable再start省事。三个服务都起来后systemctl status nginx php-fpm mysqld逐个确认active (running)。然后做一次重启验证——reboot之后再看三个服务是否自动起来。这一步很多人省略结果交付后客户一断电重启服务全挂现场尴尬。重启验证是离线交付的必做项别偷懒。重启后如果某个服务没起来journalctl -u 服务名 -n 50看最近日志八成是配置语法错误或者依赖服务没起比如PHP-FPM依赖网络就绪。nginx -t可以单独校验Nginx配置语法这个命令在改完配置后养成习惯执行一下。5. 常见问题排查与避坑经验实录5.1 高频故障速查表现象可能原因排查命令解决yum报无法解析依赖物料缺包或repodata未生成yum deplist 包名回准备机补down重新createrepo访问PHP返回502PHP-FPM未启动或端口不符ss -lntp | grep 9000启动php-fpm核对listen配置访问PHP返回403文件权限或SELinuxls -lZ 文件chmod/chownsetenforce 0访问PHP直接下载Nginx未匹配php locationnginx -T看配置补location ~ .php$段MySQL启动失败数据目录权限或残留锁tail /var/log/mysqld.log修属主清ib_logfile重试中文乱码字符集未设utf8mb4show variables like char%改my.cnf重启这张表是我这些年现场排查沉淀下来的覆盖了绝大多数情况。遇到问题先对号入座比盲目翻日志快得多。5.2 几个只有踩过才知道的坑第一个坑yum makecache成功但yum install仍报找不到包。这种情况通常是包名写错或者包在子目录里而repodata没递归索引。createrepo默认只索引当前目录如果RPM分散在子目录要加-g或者把包都挪到同一层。我现在的习惯是物料目录扁平化所有rpm放一层省得索引出问题。第二个坑PHP-FPM启动报Address already in use。9000端口被占可能是之前装过别的PHP或者有残留进程。ss -lntp | grep 9000找到占用进程kill掉再启动。如果确实有别的服务要用9000就改PHP-FPM的listen端口同时同步改Nginx的fastcgi_pass两边必须一致。第三个坑MySQL 8.0在离线环境初始化卡住。8.0首次启动初始化数据目录耗时较长如果机器IO慢systemctl start mysqld可能超时被systemd判定失败。这时候别急着重装journalctl看日志如果还在初始化就等它跑完或者调大TimeoutStartSec。我遇到过一次等了近两分钟才初始化完差点误判。第四个坑SELinux导致的玄学权限问题。文件权限明明对Nginx就是读不了。ls -Z看安全上下文/var/www/html下的文件上下文应该是httpd_sys_content_t。离线环境不方便装semanage工具最省事的办法就是setenforce 0并改/etc/selinux/config为permissive重启生效。这个决策前面提过内网环境权衡下来是合理的。5.3 交付前的自检清单现场交付前我固定跑一遍这个清单逐项打勾三个服务systemctl is-enabled都是enabled重启后三个服务自动起来curl -I http://127.0.0.1/返回200info.php能正常显示PHP信息MySQL能用业务账号登录并建库防火墙放行了业务端口测试文件已清理root密码已改这个清单看着简单但每一条都对应过一次真实的翻车。尤其是重启验证和测试文件清理前者防交付后掉链子后者防信息泄露。清单化执行比凭记忆靠谱。6. 我个人在离线交付中的几点体会离线部署LNMP这件事技术门槛其实不高难在没有退路——在线环境出问题可以搜、可以重装离线环境里每一步都得提前想清楚。我最大的体会是物料准备的完整性决定了现场效率的上限。准备阶段多花一小时把依赖拉全、在干净虚拟机上预演一遍现场就能少折腾半天。反过来物料缺一个包现场可能就要打电话回公司让人远程传文件尴尬又低效。另一个体会是配置的预置化。把nginx.conf、my.cnf、www.conf这些提前写好放在conf目录现场直接覆盖比现场手敲靠谱得多。手敲容易漏行、错字符覆盖则是一次性的、可复现的。配合部署脚本整套流程能压缩到二十分钟以内。最后分享一个小技巧物料目录里放一个README写清楚每个包的版本、来源、下载日期以及部署步骤和回滚方法。这东西平时没人看但半年后别人接手或者自己回头维护时它就是救命文档。离线环境最怕的就是当时怎么装的没人记得一份简单的记录能省掉大量考古时间。这套流程我用了很多次每次微调一点点现在基本能做到一次到场、一次装通。