CentOS 8 LEMP 部署实战:dnf、SELinux 与 systemd 深度适配指南

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 的欢迎页。但如果看不到,别急着重装,先查两个地方:

  1. 防火墙是否放行了 80 端口?

    sudo firewall-cmd --permanent --add-service=http
    sudo firewall-cmd --permanent --add-service=https
    sudo firewall-cmd --reload
    
  2. 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 是标准流程,但很多人只机械地按回车。其实,它的每个选项都关乎安全水位:

  1. Switch to using PASSWORD() for users creation: 选择 Y 。这会将密码哈希算法降级为 mysql_native_password ,以兼容旧版 PHP 驱动。虽然安全性略低,但这是生产环境稳定性的必要妥协。后续可以为特定用户单独启用 caching_sha2_password
  2. Remove anonymous users? Y 。匿名用户是巨大安全隐患。
  3. Disallow root login remotely? Y 。root 只能本地登录,远程管理用普通用户。
  4. Remove test database and access to it? Y 。测试库是攻击者的首选目标。
  5. 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 文件。这就是 Nginx fastcgi_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 最后的“信任检查”:用真实世界的数据验证你的部署

所有配置和加固做完,别急着上线。用一个真实的小项目来压测:

  1. 部署一个 WordPress :下载最新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值