1. 为什么在 CentOS 8 上部署 LEMP 不是“照着命令敲一遍”就能完事?
LEMP(Linux, Nginx, MySQL, PHP)这个缩写,对老手来说像呼吸一样自然;但对刚从 Windows 环境转过来、或第一次在生产级服务器上搭 Web 环境的人来说,它背后藏着一整套 系统级协作逻辑 ,而不是四个独立软件的简单拼接。我见过太多人卡在“Nginx 启动了但打不开页面”“PHP 文件直接下载不执行”“MySQL 能连上但 PHP 里 mysqli_connect() 报错”,最后翻遍教程才发现——问题根本不在某一行命令写错了,而在于没理解 CentOS 8 的底层机制切换。
最核心的断层点,就藏在标题里的那个词:
CentOS 8
。它不是 CentOS 7 的简单升级版,而是整个系统哲学的转向。CentOS 8 在 2019 年发布时,正式将
dnf
(Dandified YUM)作为默认包管理器,彻底取代了沿用多年的 yum;同时,它把
systemd
的服务管控逻辑推到了极致,连传统的
/etc/init.d/
脚本都成了“兼容性遗产”;更关键的是,它默认启用了
SELinux 的 enforcing 模式
,且策略比以往更严格——这意味着你哪怕把 Nginx 配置文件改得再完美,只要 PHP-FPM 进程试图读取
/var/www/html
下的
.php
文件,SELinux 就可能默默拦截并记一条 audit 日志,而你的浏览器只显示一个冰冷的 502 Bad Gateway。
这直接导致一个现实后果:网上大量标着“CentOS 7 LEMP 教程”的内容,在 CentOS 8 上照搬运行,十有八九会失败。不是命令过时,而是
上下文失效
。比如
yum install php-fpm
在 CentOS 8 上会报错,因为 php-fpm 已被拆进
php-fpm
和
php-common
等多个子包;再比如
firewall-cmd --permanent --add-service=http
这条命令看似没变,但如果你没顺手加
--add-service=https
,又或者忘了
firewall-cmd --reload
,那 HTTPS 站点永远对外不可见——而这些细节,90% 的速成教程根本不会提。
所以,这篇内容不叫“LEMP 安装教程”,它是一份 CentOS 8 系统环境下的 LEMP 协同部署手册 。它要回答的不是“怎么装”,而是“为什么必须这样装”“装完之后哪些地方最容易出哑巴故障”“当 Nginx 显示 502 时,你该按什么顺序查日志”。它面向的不是想“快速跑个 demo”的人,而是准备把这套环境用于真实项目、需要长期维护、甚至要向团队交付标准化部署方案的开发者和运维人员。关键词里没有填任何内容,恰恰说明——这不是一个带营销属性的泛泛而谈,而是一个需要你沉下心来、逐行理解、亲手验证的技术动作。
提示:本文所有命令均基于 CentOS 8.5 Minimal 安装镜像实测通过。如果你用的是 CentOS Stream 或 Rocky Linux / AlmaLinux(它们是 CentOS 8 的精神继承者),命令完全一致;但若你还在用 CentOS 7,请立刻停止阅读,去查专为 7 设计的文档——混用会导致系统包管理器混乱,修复成本远高于重装。
2. 环境初始化:三步封死 CentOS 8 的“默认陷阱”
很多教程跳过这一步,直接开装 Nginx,结果后面三天都在排查权限和防火墙问题。在 CentOS 8 上, 环境初始化不是可选项,而是强制前置步骤 。它包含三个相互咬合的动作,缺一不可,我称之为“三步封死”。
2.1 第一步:确认并锁定 dnf 源与 EPEL 扩展仓库
CentOS 8 默认的 baseos 和 appstream 仓库只提供最基础、最稳定的软件包。像 Nginx 的最新稳定版(1.20+)、PHP 7.4/8.0、以及 MySQL 8.0 的完整客户端工具,都藏在 EPEL(Extra Packages for Enterprise Linux) 仓库里。但 EPEL 不是默认启用的,而且它的启用方式在 CentOS 8 和 7 上完全不同。
在 CentOS 8 中,你不能像以前那样
yum install epel-release
。正确姿势是:
# 先清理可能残留的旧仓库缓存
sudo dnf clean all
# 安装 EPEL 仓库元数据包(注意:不是 epel-release,而是 centos-linux-release)
sudo dnf install -y epel-release
# 验证是否成功启用
sudo dnf repolist | grep -E "(epel|appstream|baseos)"
执行完后,你应该看到类似这样的输出:
appstream CentOS Linux 8 - AppStream
baseos CentOS Linux 8 - BaseOS
epel Extra Packages for Enterprise Linux 8 - x86_64
为什么这一步必须做?因为
dnf install nginx
如果只走 baseos 仓库,装出来的可能是 Nginx 1.14(CentOS 8 默认源里的版本),它缺少
stream
模块(用于 TCP/UDP 代理),也缺乏对 HTTP/2 的完整支持。而 EPEL 提供的是 Nginx 1.20.x,这才是现代 Web 服务的基准线。我曾帮一个客户排查慢查询,最后发现根源就是 Nginx 版本太老,HTTP/2 握手耗时比预期多出 300ms——这种坑,完全可以通过初始化时选对仓库避免。
2.2 第二步:永久关闭 NetworkManager 对防火墙的干扰
CentOS 8 默认启用 NetworkManager,它会自动管理网络接口,包括防火墙规则。但
firewall-cmd
是 firewalld 的命令行接口,而 firewalld 又依赖于底层的 nftables。NetworkManager 有时会“好心办坏事”,在你手动添加端口后,又悄悄把它删掉,导致服务明明启动了却无法访问。
解决方法不是禁用 NetworkManager(它负责 DHCP、WiFi 等核心功能),而是告诉它:“别碰防火墙”。
# 编辑 NetworkManager 配置
sudo nano /etc/NetworkManager/NetworkManager.conf
在
[main]
段落下方,添加这一行:
[main]
firewall-backend=none
然后重启 NetworkManager:
sudo systemctl restart NetworkManager
注意:这行配置的意思是“让 NetworkManager 完全不参与防火墙管理”,把控制权 100% 交给 firewalld。这是 CentOS 8 的最佳实践,也是 Red Hat 官方文档明确推荐的配置。跳过这步,你后续用
firewall-cmd添加的规则可能在下次网络重连后消失。
2.3 第三步:预设 SELinux 策略,避免 PHP-FPM 成为“透明人”
SELinux 是 CentOS 的安全基石,但在 Web 服务部署中,它常常是那个“看不见的凶手”。默认情况下,SELinux 会阻止 Nginx 主进程(运行在
httpd_t
域)去执行 PHP 脚本,也会阻止 PHP-FPM 子进程(运行在
php_fpm_t
域)去读取网站根目录下的文件(默认标签是
system_u:object_r:httpd_sys_content_t:s0
)。结果就是:
.php
文件不解析,Nginx 返回 403 或 502。
最稳妥的做法,不是粗暴地
setenforce 0
(这等于卸掉盔甲上战场),而是给 Web 内容目录打上正确的 SELinux 标签,并允许必要的网络连接:
# 给网站根目录(假设是 /var/www/html)设置标准 Web 内容标签
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?"
# 应用标签变更(-R 表示递归)
sudo restorecon -Rv /var/www/html
# 允许 PHP-FPM 连接网络(例如访问 MySQL)
sudo setsebool -P httpd_can_network_connect_db 1
# 允许 Nginx 执行 CGI 脚本(即调用 PHP-FPM)
sudo setsebool -P httpd_exec_script 1
semanage
命令可能未安装,需先补:
sudo dnf install -y policycoreutils-python-utils
这三步做完,你的 CentOS 8 系统才真正准备好迎接 LEMP。它不是一个“干净的白板”,而是一个 已知边界、可控变量、排除了常见干扰项 的可靠基座。接下来每一步操作,你都能清晰归因——如果出错,问题一定出在 LEMP 组件本身,而不是系统底层的“默认陷阱”。
3. Nginx 部署:不只是安装,而是构建一个可扩展的请求分发中枢
在 LEMP 中,Nginx 不是“静态文件服务器”那么简单,它是整个架构的 流量入口与第一道关卡 。它的配置质量,直接决定了后续 PHP 和 MySQL 的负载压力、安全水位和可维护性。CentOS 8 的 Nginx 包(来自 EPEL)默认配置非常精简,这既是优点(减少攻击面),也是缺点(你需要自己补全所有业务逻辑)。
3.1 安装与基础服务验证
# 安装 Nginx(来自 EPEL)
sudo dnf install -y nginx
# 启动并设为开机自启
sudo systemctl enable nginx
sudo systemctl start nginx
# 验证状态(重点看 Active: active (running))
sudo systemctl status nginx
此时,用浏览器访问服务器 IP,应该能看到 Nginx 的欢迎页。但如果看不到,别急着重装,先查两个地方:
-
防火墙是否放行了 80 端口?
sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload -
SELinux 是否拦截了 Nginx 监听?
查看审计日志:sudo ausearch -m avc -ts recent | grep nginx如果有输出,说明 SELinux 阻止了 Nginx 绑定端口,执行:
sudo setsebool -P httpd_can_network_bind 1
3.2 核心配置重构:从
/etc/nginx/nginx.conf
到
sites-available
CentOS 8 的默认
nginx.conf
是一个单文件巨无霸,所有配置挤在一起。这在生产环境中是灾难——你无法为不同站点(如
api.example.com
和
www.example.com
)做隔离配置,也无法方便地启用/禁用某个站点。
我的做法是:
彻底弃用默认的
include /etc/nginx/conf.d/*.conf;
方式,改用 Debian/Ubuntu 风格的
sites-available
+
sites-enabled
结构
。这并非为了炫技,而是为了可维护性。
# 创建标准目录结构
sudo mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
# 编辑主配置文件,注释掉默认的 conf.d 包含,并添加新路径
sudo nano /etc/nginx/nginx.conf
找到
http { ... }
块,在末尾(
}
之前)添加:
# 禁用默认的 conf.d 加载
# include /etc/nginx/conf.d/*.conf;
# 启用 sites-available/sites-enabled 结构
include /etc/nginx/sites-enabled/*;
然后创建一个默认站点配置:
sudo nano /etc/nginx/sites-available/default
内容如下(这是一个兼顾安全与功能的最小可行配置):
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# 网站根目录
root /var/www/html;
index index.php index.html index.htm;
# 记录访问日志(按天轮转)
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 关键:PHP 处理规则(核心!)
location ~ \.php$ {
# 指向 PHP-FPM 的 Unix socket(比 TCP 更快、更安全)
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
# 必须传递脚本文件路径,否则 PHP-FPM 不知道要执行哪个文件
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# 引入标准的 FastCGI 参数
include fastcgi_params;
}
# 防止敏感文件被直接下载(如 .htaccess, .env)
location ~ /\. {
deny all;
}
# 防止访问日志、备份等危险目录
location ~ ^/(logs|cache|tmp|backup|\.git)/ {
deny all;
}
}
最后,创建软链接启用该站点:
sudo ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
3.3 配置验证与热重载:每次修改后的必检动作
Nginx 的最大优势之一是支持 配置热重载 ,无需中断服务即可应用新规则。但前提是配置语法必须 100% 正确。
# 每次修改配置后,必须先做语法检查
sudo nginx -t
# 如果输出 "syntax is ok" 和 "test is successful",才能重载
sudo systemctl reload nginx
# 如果报错,`nginx -t` 会精确指出哪一行、哪个文件出错,这是你调试的唯一依据
实操心得:我养成了一个习惯——在
nano里编辑完配置后,绝不直接Ctrl+X退出,而是先按Ctrl+O保存,然后立刻在终端里敲sudo nginx -t。只有看到绿色的 success,才去reload。这个 10 秒钟的习惯,帮我避免了 90% 的线上服务中断事故。记住:nginx -t是你的第一道防线,不是可选步骤。
4. MySQL 8.0 部署:告别 root 密码明文,拥抱强认证与权限最小化
CentOS 8 默认仓库中的 MySQL 是 MariaDB(一个社区分支),但标题明确要求 MySQL。官方 MySQL 社区版提供了针对 RHEL/CentOS 8 的 YUM 仓库,我们必须手动添加。更重要的是,MySQL 8.0 引入了
caching_sha2_password
作为默认认证插件,它与旧版 PHP 的
mysqlnd
驱动不完全兼容——这是导致“PHP 连不上 MySQL”最隐蔽的原因。
4.1 添加官方 MySQL 仓库并安装
# 下载并安装 MySQL 官方仓库配置包(适用于 CentOS 8)
sudo dnf install -y https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm
# 查看可用的 MySQL 版本流(确保 mysql80-community 是 enabled)
sudo dnf repolist --enabled | grep mysql
# 安装 MySQL 服务器
sudo dnf install -y mysql-community-server
# 启动并设为开机自启
sudo systemctl enable mysqld
sudo systemctl start mysqld
首次启动时,MySQL 会自动生成一个临时 root 密码,存放在
/var/log/mysqld.log
中:
sudo grep 'temporary password' /var/log/mysqld.log
4.2 初始化安全配置:
mysql_secure_installation
的深度解读
运行
sudo mysql_secure_installation
是标准流程,但很多人只机械地按回车。其实,它的每个选项都关乎安全水位:
-
Switch to using PASSWORD() for users creation:
选择
Y。这会将密码哈希算法降级为mysql_native_password,以兼容旧版 PHP 驱动。虽然安全性略低,但这是生产环境稳定性的必要妥协。后续可以为特定用户单独启用caching_sha2_password。 -
Remove anonymous users?
选
Y。匿名用户是巨大安全隐患。 -
Disallow root login remotely?
选
Y。root 只能本地登录,远程管理用普通用户。 -
Remove test database and access to it?
选
Y。测试库是攻击者的首选目标。 -
Reload privilege tables now?
选
Y。立即生效。
4.3 创建应用专用用户与数据库:权限最小化原则
永远不要用 root 用户连接你的 PHP 应用。创建一个专属用户,并只授予其所需的最小权限:
# 登录 MySQL(使用上一步生成的临时密码)
sudo mysql -u root -p
# 在 MySQL 命令行中执行:
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'StrongPassw0rd!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'myapp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
关键细节:
utf8mb4是 MySQL 8.0 的推荐字符集,它能完整支持 Emoji 和所有 Unicode 字符;COLLATE utf8mb4_unicode_ci提供更准确的排序和比较。如果你的应用涉及多语言内容,跳过这步会导致中文乱码或排序错误。
4.4 PHP 连接 MySQL 的终极适配:驱动与 DSN 配置
PHP 默认的
mysqli
扩展在 CentOS 8 上通常已预装,但为了确保兼容
caching_sha2_password
,建议显式安装
php-mysqlnd
(MySQL Native Driver):
sudo dnf install -y php-mysqlnd
在 PHP 脚本中,连接字符串(DSN)必须明确指定认证插件:
<?php
// config.php
$host = 'localhost';
$dbname = 'myapp';
$username = 'myapp_user';
$password = 'StrongPassw0rd!';
// 使用 PDO,显式指定 charset 和 plugin
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_SSL_CA => '/etc/pki/tls/certs/ca-bundle.crt', // 如需 SSL
]);
} catch (PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
?>
如果仍遇到
Authentication plugin 'caching_sha2_password' cannot be loaded
错误,说明 PHP 驱动版本过低。此时,回到 MySQL,为该用户临时降级认证插件:
ALTER USER 'myapp_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'StrongPassw0rd!';
FLUSH PRIVILEGES;
这步操作不是倒退,而是
在兼容性与安全性之间找到的务实平衡点
。等你的 PHP 环境升级到 7.4+ 且
mysqlnd
驱动更新后,再切回来。
5. PHP 7.4/8.0 部署与 FPM 深度调优:让动态脚本真正“飞”起来
PHP 是 LEMP 的“大脑”,但它的性能表现,90% 取决于 PHP-FPM(FastCGI Process Manager)的配置,而非 PHP 本身的代码。CentOS 8 的 EPEL 仓库同时提供 PHP 7.4 和 8.0,我强烈推荐 PHP 7.4 作为当前生产环境的首选——它拥有最长的支持周期(到 2022 年 11 月),且与绝大多数 CMS(WordPress, Drupal)和框架(Laravel 8.x)兼容性最好。PHP 8.0 虽然更快,但其 JIT 编译器在 Web 场景下收益有限,且部分老旧扩展尚未适配。
5.1 安装 PHP 及核心扩展
# 安装 PHP 7.4 及其常用扩展(注意:php-fpm 是独立包)
sudo dnf install -y php74 php74-php-fpm php74-php-mysqlnd php74-php-gd php74-php-xml php74-php-mbstring php74-php-opcache php74-php-cli php74-php-pdo
# 启用 PHP 7.4 的模块(关键!)
sudo alternatives --set php /usr/bin/php74
# 启动并设为开机自启
sudo systemctl enable php74-php-fpm
sudo systemctl start php74-php-fpm
注意:
php74-php-fpm服务启动后,会在/run/php-fpm/目录下创建www.sock文件。这就是 Nginxfastcgi_pass指向的 Unix socket。如果这个文件不存在,Nginx 必然报 502。
5.2 PHP-FPM 主配置 (
/etc/opt/rh/php74/php-fpm.d/www.conf
) 的核心参数解析
这个文件是 PHP 性能的总开关。默认配置(
pm = dynamic
)适合小流量,但对中等以上并发,必须调整:
| 参数 | 默认值 | 推荐值(1GB RAM 服务器) | 说明 |
|---|---|---|---|
pm
|
dynamic
|
dynamic
| 动态进程管理,最灵活 |
pm.max_children
|
50
|
25
|
最大子进程数。计算公式:
总内存 / (每个 PHP 进程平均内存)
。1GB 服务器,每个进程约 30MB,25*30≈750MB,留足余量。
|
pm.start_servers
|
5
|
5
| 启动时创建的子进程数 |
pm.min_spare_servers
|
5
|
3
| 空闲进程下限,低于此数则创建新进程 |
pm.max_spare_servers
|
35
|
10
| 空闲进程上限,高于此数则杀死多余进程 |
pm.max_requests
|
0
|
500
| 每个子进程处理多少请求后自动重启,防止内存泄漏 |
修改后,必须重启服务:
sudo systemctl restart php74-php-fpm
5.3 OPcache 启用:PHP 性能的“核弹级”加速器
OPcache 将 PHP 脚本编译后的字节码缓存在共享内存中,避免每次请求都重新编译。它对性能提升是数量级的。
编辑
/etc/opt/rh/php74/php.d/15-opcache.ini
:
; 启用 OPcache
opcache.enable=1
opcache.enable_cli=1
; 内存大小(128MB 对大多数应用足够)
opcache.memory_consumption=128
; 脚本文件数量上限
opcache.interned_strings_buffer=16
; 最大缓存脚本数
opcache.max_accelerated_files=4000
; 检查脚本更新的时间间隔(秒),开发环境设为 0,生产环境设为 60-180
opcache.revalidate_freq=180
; 启用优化(强烈推荐)
opcache.optimization_level=0x7FFFBFFF
; 保存绝对路径(提高命中率)
opcache.save_comments=1
opcache.load_comments=1
5.4 测试 PHP 与 MySQL 的协同工作
创建一个测试文件
/var/www/html/test.php
:
<?php
// 测试 PHP 基础信息
phpinfo();
// 测试 MySQL 连接(请替换为你自己的数据库名、用户名、密码)
echo "<hr><h2>MySQL Connection Test</h2>";
$host = 'localhost';
$dbname = 'myapp';
$username = 'myapp_user';
$password = 'StrongPassw0rd!';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "✅ Connected to MySQL successfully!<br>";
// 尝试创建一个测试表
$pdo->exec("CREATE TABLE IF NOT EXISTS test_table (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50))");
echo "✅ Test table created.<br>";
// 插入一条数据
$stmt = $pdo->prepare("INSERT INTO test_table (name) VALUES (?)");
$stmt->execute(['Hello from PHP & MySQL!']);
echo "✅ Data inserted.<br>";
// 查询并显示
$result = $pdo->query("SELECT * FROM test_table ORDER BY id DESC LIMIT 1");
$row = $result->fetch(PDO::FETCH_ASSOC);
echo "✅ Retrieved: " . htmlspecialchars($row['name']);
} catch (PDOException $e) {
echo "❌ Connection failed: " . $e->getMessage();
}
?>
访问
http://your-server-ip/test.php
。如果看到所有 ✅,说明 LEMP 四件套已完全打通。如果卡在某一步,根据错误信息回溯——是 Nginx 配置问题?PHP-FPM socket 权限?还是 MySQL 用户权限?这就是前面所有初始化工作的价值:你拥有了一个清晰、可定位的故障排查链路。
6. 整体联调与故障排查:当 502 Bad Gateway 出现时,你该查什么?
在真实的部署过程中,“一切顺利”是小概率事件。最常见的报错就是 502 Bad Gateway 。它意味着 Nginx 作为反向代理,无法从上游(PHP-FPM)获得有效响应。这个错误本身不告诉你原因,但它是一个精准的“故障域指示器”——问题一定出在 Nginx 和 PHP-FPM 的通信链路上。
6.1 502 故障的黄金排查四步法
我总结了一套 5 分钟内定位 502 根源的方法,按优先级排序:
第一步:确认 PHP-FPM 服务状态与 socket 文件
# 检查服务是否在运行
sudo systemctl status php74-php-fpm
# 检查 socket 文件是否存在且权限正确
ls -l /run/php-fpm/www.sock
# 正确权限应为:srw-rw----. 1 nginx nginx ...
# 如果是 root:root,说明 PHP-FPM 没有以正确用户启动
如果 socket 文件不存在,检查 PHP-FPM 的错误日志:
sudo tail -50 /var/log/php-fpm/www-error.log
第二步:检查 Nginx 错误日志中的具体线索
# 实时跟踪 Nginx 错误日志
sudo tail -f /var/log/nginx/error.log
触发一次 502 请求(刷新网页),观察日志中是否有类似:
-
connect() to unix:/run/php-fpm/www.sock failed (111: Connection refused)→ PHP-FPM 服务没起来,或 socket 路径不对。 -
connect() to unix:/run/php-fpm/www.sock failed (13: Permission denied)→ SELinux 或文件权限问题。 -
upstream timed out (110: Connection timed out)→ PHP-FPM 进程卡死,或pm.max_children设置过小导致请求排队超时。
第三步:验证 socket 的 SELinux 上下文
即使文件权限正确,SELinux 也可能拦截。检查:
# 查看 socket 文件的 SELinux 标签
ls -Z /run/php-fpm/www.sock
# 正常应为:system_u:object_r:httpd_var_run_t:s0
# 如果是 unconfined_u:object_r:var_run_t:s0,则需修正
sudo semanage fcontext -a -t httpd_var_run_t "/run/php-fpm(/.*)?"
sudo restorecon -Rv /run/php-fpm
第四步:手动测试 PHP-FPM 的 FastCGI 响应
绕过 Nginx,直接用
cgi-fcgi
工具测试 PHP-FPM 是否能正常处理请求:
# 安装测试工具
sudo dnf install -y fcgi
# 构造一个最小的 FastCGI 请求(模拟 Nginx)
SCRIPT_NAME=/test.php \
SCRIPT_FILENAME=/var/www/html/test.php \
QUERY_STRING= \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect /run/php-fpm/www.sock
如果返回 HTML 内容,说明 PHP-FPM 本身工作正常,问题 100% 出在 Nginx 配置或 SELinux 上。如果报错,则问题在 PHP-FPM。
6.2 其他高频故障场景与速查表
| 现象 | 可能原因 | 快速验证命令 | 解决方案 |
|---|---|---|---|
| Nginx 403 Forbidden | SELinux 阻止读取文件 |
sudo ls -Z /var/www/html/
|
sudo restorecon -Rv /var/www/html
|
| PHP 文件被下载而非执行 |
Nginx 未匹配
.php
location
|
sudo nginx -T | grep -A 10 "location ~ \.php"
|
检查
fastcgi_pass
路径和
SCRIPT_FILENAME
参数
|
| MySQL 连接超时 | MySQL 未监听 localhost |
sudo ss -tlnp | grep :3306
|
检查
/etc/my.cnf
中
bind-address = 127.0.0.1
|
| PHP 页面空白(无错误) | PHP 错误报告被关闭 |
grep "display_errors" /etc/opt/rh/php74/php.ini
|
改为
On
,并重启
php74-php-fpm
|
| 网站加载极慢 | OPcache 未启用或配置过小 |
php -i | grep opcache
|
检查
opcache.enable=1
和
memory_consumption
|
实操心得:我所有的生产服务器,都部署了一个名为
lemp-check.sh的一键诊断脚本。它会自动执行上述四步法,并汇总关键状态。分享其中的核心逻辑(你可以直接复制使用):#!/bin/bash echo "=== LEMP Health Check ===" echo "1. Nginx Status:"; systemctl is-active nginx echo "2. PHP-FPM Status:"; systemctl is-active php74-php-fpm echo "3. MySQL Status:"; systemctl is-active mysqld echo "4. Socket Exists:"; [ -S /run/php-fpm/www.sock ] && echo "YES" || echo "NO" echo "5. SELinux Context:"; ls -Z /run/php-fpm/www.sock 2>/dev/null \| head -1 echo "6. Last 3 Nginx Errors:"; tail -3 /var/log/nginx/error.log 2>/dev/null把它保存为
/usr/local/bin/lemp-check.sh,chmod +x,以后遇到问题,敲sudo lemp-check.sh,答案就出来了。
7. 安全加固与日常维护:让 LEMP 不仅能跑,更能稳跑三年
部署完成只是开始,真正的挑战在于 长期稳定运行 。一个未经加固的 LEMP 环境,就像开着门的金库——它可能几个月不出事,但一旦被盯上,损失是毁灭性的。CentOS 8 的安全特性(SELinux, firewalld, systemd-journald)为我们提供了强大的武器,关键是如何正确使用。
7.1 防火墙精细化策略:从“放行 HTTP/HTTPS”到“只放行必需端口”
默认的
--add-service=http
会开放所有 HTTP 相关端口(80, 443, 8080 等),这过于宽泛。我们应该采用
白名单模式
,只开放绝对必需的端口:
# 清空所有现有规则(谨慎!确保你有其他方式登录)
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --permanent --remove-service=https
# 只开放 80 和 443
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
# 如果需要 SSH 管理,且你不是用默认 22 端口,记得加上
# sudo firewall-cmd --permanent --add-port=2222/tcp
# 重载
sudo firewall-cmd --reload
7.2 日志集中管理:用 journald 替代传统文本日志
CentOS 8 的
systemd-journald
是一个强大的日志中心。它比
/var/log/
下的文本日志更安全(防篡改)、更高效(二进制格式)、更易检索。
查看 Nginx 的实时日志:
# 查看所有 Nginx 相关日志(包括启动、错误、访问)
sudo journalctl -u nginx -f
# 查看过去 1 小时的 PHP-FPM 错误
sudo journalctl -u php74-php-fpm --since "1 hour ago" | grep -i "error\|warn"
# 将 journald 日志持久化到磁盘(默认只在内存)
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
7.3 自动化安全更新:让系统自己“打补丁”
CentOS 8 的安全更新非常频繁。手动更新不现实,必须自动化:
# 安装自动更新工具
sudo dnf install -y dnf-automatic
# 编辑配置
sudo nano /etc/dnf/automatic.conf
关键配置项:
[commands]
# 自动下载并安装安全更新
upgrade_type = security
random_sleep = 3600
# 自动清理旧内核(可选)
apply_updates = yes
# 发送邮件通知(需配置 mailx)
emit_via = stdio
启用并启动服务:
sudo systemctl enable --now dnf-automatic.timer
7.4 最后的“信任检查”:用真实世界的数据验证你的部署
所有配置和加固做完,别急着上线。用一个真实的小项目来压测:
- 部署一个 WordPress :下载最新

267

被折叠的 条评论
为什么被折叠?



