简介:在Linux服务器部署Web环境时,如何快速装好Apache、PHP与MySQL并让三者协同工作,是很多运维新手的常见难题。这份PDF以CentOS 7为背景,完整梳理了LAMP环境的搭建流程:先调整防火墙与SELinux,再依次安装Apache、PHP和MySQL,最后完成PHP与Apache的解析整合。文档包含常用的systemctl服务管理命令、iptables端口规则、httpd.conf与php.conf的关键配置示例,并借助FilesMatch、SetHandler等指令解释PHP解析原理,还点出ServerName、apachectl configtest、php.ini等易错细节,便于读者边看边练、按图索骥。资源为单个PDF文档,共1个文件,大小约955KB,内容紧凑,可直接下载后对照操作。已有108人学习下载,适合正在学习Linux运维或需要自主搭建Web服务器的开发人员,按照文档步骤实践,能少走不少弯路,快速构建可用的LAMP环境。
1. CentOS 7 上搭 Apache+PHP+MySQL,先算清楚三笔版本账
拿到一台 2C4G 的 CentOS 7 服务器,要交付动态网站,最常见的动作就是把 Apache、PHP、MySQL 这三件套跑起来。这套组合成熟、资料多、坑也稳定:CentOS 7 默认源里的 mysql 指向 MariaDB,默认 PHP 是已经停止维护的 5.4,Apache 则被 SELinux 管得死死的。标题里这三个词看起来直接,实际上每一步都在跟「默认值」做斗争。这篇文章直接从一台干净的最小化 CentOS 7 开始,把仓库选择、防火墙、SELinux、PHP-FPM 整合、MySQL 初始化和虚拟主机串成一条能照着敲的命令链,适合运维、后端开发和刚接手服务器的朋友,装完不是「能打开测试页」,而是能直接上业务。
2. 初始化 CentOS 7:仓库、防火墙与 SELinux 对 Apache 的约束
2.1 装软件之前先确认系统状态
最小化安装的 CentOS 7 连 httpd 都没有,但网络和 SELinux 状态必须先摸清。登录后依次执行下面三条命令,每一条的输出都决定了后面怎么做:
cat /etc/redhat-release ip addr | grep inet getenforce第一条看系统版本,确认是 7.x 而不是 8 或 9;第二条看网卡 IP 是否正常拿到;第三条输出 Enforcing 表示 SELinux 开着,Permissive 或 Disabled 表示没开。CentOS 7 默认是 Enforcing 的,如果你在云厂商镜像上看到 Disabled,那是镜像商改的,本地环境通常会遇到完整的 SELinux 限制。
这一步还应该顺手确认防火墙状态。CentOS 7 默认用 firewalld,最小化安装有时不装,所以先看再动手:
systemctl status firewalld如果输出 active (running) 就继续往下走,如果显示 inactive 或被 mask,说明这台机器没有防火墙在跑,后面端口放行那一步可以跳过,但建议还是把它装回来。一个裸奔的 80 端口挂到公网上,扫描器一小时之内就能找上门。
2.2 仓库选择的优先级:EPEL、Remi 和 MySQL 官方源
CentOS 7 自带 base 仓库里的 php 是 5.4 版,这个版本连 PHP 官方都不再提供安全补丁,而且 php-mysql 扩展面向的是老式 MySQL 认证,连 MySQL 8 默认的 caching_sha2_password 会遇到障碍。所以三件套里 PHP 和 MySQL 都不能直接用系统源,这也是整套配置里最容易踩的坑。
我一般的做法是先装 EPEL 和 Remi,再把 Remi 的 php74 模块启用,最后单独接 MySQL 官方源。几个仓库的分工可以用一张表说清:
| 仓库 | 提供内容 | 是否必装 |
|---|---|---|
| base | httpd、系统基础库 | 系统自带 |
| EPEL | 大量扩展包,Remi 的前置依赖 | 建议 |
| Remi | PHP 5.4 到 8.x 的各类版本 | 装新版 PHP 必须 |
| MySQL 官方源 | mysql-community-server | 用 MySQL 而非 MariaDB 时必须 |
安装命令如下:
yum install -y epel-release rpm -Uvh https://rpms.remirepo.net/enterprise/remi-release-7.rpm yum install -y yum-utils yum-config-manager --enable remi-php74这里有个容易忽略的细节:remi-release-7.rpm装完后,Remi 仓库默认处于禁用状态,必须用yum-config-manager --enable remi-php74显式打开指定的 PHP 版本模块。这样做的意义是避免yum install php时把系统自带的 5.4 装上——启用 remi-php74 后,yum 解析依赖时会优先选择更高版本的 PHP 7.4 包。注意顺序别颠倒,remi-release必须依赖 EPEL 里的包,所以 EPEL 要装在前面。
2.3 防火墙放行 80 和 443 端口的正确姿势
Apache 默认监听 80,如果不放行,本机 curl 能看到页面、外部浏览器却始终打不开,这是检索热词里「apache配置域名无法访问」最常见的原因之一。用 firewalld 放行的标准做法是:
firewall-cmd --permanent --add-service=http --add-service=https firewall-cmd --reload firewall-cmd --list-all--permanent把规则写入持久配置,--reload让运行中的规则立即生效。很多人漏掉第二步,结果临时规则生效、永久规则没写进文件,一重启服务器又回到原点。--list-all用来确认 services 列表里已经出现 http 和 https。
如果你的环境用的是 iptables 服务而没装 firewalld,那就检查/etc/sysconfig/iptables并显式增加-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT。这个办法老派但可靠,云服务器还要记得在安全组里同步放行,那是防火墙之外的另一层过滤。
2.4 SELinux 对 Apache 站点目录的隐性限制
很多教程让你直接setenforce 0关掉 SELinux,这是偷懒。保持 Enforcing 的情况下,Apache 读取/var/www/html以外的目录会遇到 403 Forbidden,错误日志里会看到Permission denied: AH00035: access to /index.html denied。原因不是文件权限,而是 SELinux 的文件上下文标签不对。
常规解法是用 semanage 给自定义目录打上正确的类型标签:
yum install -y policycoreutils-python semanage fcontext -a -t httpd_sys_content_t "/data/www(/.*)?" restorecon -Rv /data/wwwsemanage fcontext -a注册一条目录规则,restorecon -Rv把已有文件的上下文实际应用成规则里的值。注意那条路径末尾的(/.*)?必须写全,否则子目录不会继承 httpd_sys_content_t 类型。如果 PHP 还要连 MySQL,再执行一条:
setsebool -P httpd_can_network_connect_db 1这条布尔值控制 httpd 进程能否发起对数据库端口的网络连接。不开的话,PHP 里mysqli_connect会报Connection refused或No such file or directory,你查半天 MySQL 配置都找不到原因,最后发现是 SELinux 把网络出口给拦了。
3. 用 yum 装好 Apache、PHP、MySQL 并启动三个服务
3.1 安装 httpd 并验证默认页面
前置工作做完,三件套的安装反而很快。Apache 直接走系统源:
yum install -y httpd systemctl start httpd systemctl enable httpd ss -lntp | grep :80 curl -I http://127.0.0.1/ss -lntp用来确认 httpd 确实监听在 0.0.0.0:80 或具体 IP 的 80 端口,比netstat更轻量也更现代。curl -I发一个 HEAD 请求,能看到HTTP/1.1 403 Forbidden是正常的,原因是最精简安装下/var/www/html里没有 index.html,Apache 默认禁止直接列目录。等后面放一个测试文件进去,状态码会自然变成 200。
如果curl -I直接Connection refused,通常是 httpd 没起来。用journalctl -u httpd --no-pager -n 30看最近的日志,八成是 80 端口被别的进程占了,或者配置文件里有个语法错误。
3.2 PHP 版本选择:为什么要用 Remi 的 PHP 7.4
接第 2 章的仓库配置,现在直接装 PHP 及常用扩展:
yum install -y php php-fpm php-mysqlnd php-gd php-xml php -v这里php -v的输出值得盯着看。如果显示PHP 7.4.x,说明 remi-php74 仓库起作用了;如果显示PHP 5.4.16,一定是第 2 章的yum-config-manager --enable remi-php74没执行成功。可以用yum repolist确认 remi-php74 出现在已启用列表里。
选择 PHP 7.4 而不是系统默认的 5.4,理由很实际:PHP 5.4 在 2015 年就结束安全支持,生产环境用它等于裸奔;更关键的是 PHP 5.4 自带的 mysql 扩展依赖老式 MySQL 客户端库,连接 MySQL 8 默认的 caching_sha2_password 认证会直接失败。7.4 的 mysqlnd 驱动原生支持这种认证方式,不需要给 MySQL 降级或改认证插件。
3.3 MySQL 与 MariaDB 的分叉:按标题装真正的 MySQL
CentOS 7 的 base 仓库里yum install mysql装的其实是 MariaDB,咱们标题写的是 MySQL,那就走官方源把真正的 MySQL 装进来:
rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm yum install -y mysql-community-server systemctl start mysqldMySQL 5.7 以后的版本有个显著变化:安装后 root 有一串临时密码,写在日志里。启动后马上执行:
grep 'temporary password' /var/log/mysqld.log输出类似root@localhost: xxxxxxxx,这串密码只在第一次登录时有效。接着运行官方安全脚本:
mysql_secure_installation它会引导你改 root 密码、删匿名用户、禁用 root 远程登录。注意新密码必须符合 validate_password 策略,至少 8 位、包含大小写字母和数字,你要是想设弱密码就会被挡住。实在不想用这个脚本,可以手动登录后执行ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';。
3.4 三个服务一起设置开机自启
systemctl enable httpd php-fpm mysqld systemctl list-unit-files | grep -E 'httpd|php-fpm|mysqld'enable的本质是在/etc/systemd/system/multi-user.target.wants/下建立符号链接,下次开机自动拉起这三个服务。list-unit-files用来确认三者的 enabled 状态,看到enabled就说明这一步没问题。这里想强调一个排序:MySQL 通常最先启动,因为 php-fpm 启动时不需要连库,但业务请求一旦进来,数据库必须在场。
4. 把 Apache+PHP+MySQL 串起来:虚拟主机与 PHP 连库验证
4.1 选 mod_php 还是 php-fpm:CentOS 7 生产环境的答案
装完三件套只是第一步,接下来要决定 Apache 怎么跟 PHP 协作。CentOS 7 上存在两条路线:一是传统的mod_php,PHP 作为 Apache 的一个模块加载;二是php-fpm,PHP 作为独立 FastCGI 进程跑,Apache 通过mod_proxy_fcgi转发请求。
| 对比项 | mod_php | php-fpm |
|---|---|---|
| 进程模型 | 与 httpd 进程同生共死 | 独立进程池,与 httpd 解耦 |
| 内存占用 | 每个 httpd 进程都带一份 PHP | PHP 进程单独统计,可精确控制 |
| 稳定性 | PHP 崩溃拖垮整个 httpd | PHP worker 崩了,httpd 不受影响 |
| 多版本共存 | 极难实现 | 不同端口/不同 sock 开多套 pool |
我的经验是生产环境一律用 php-fpm。原因很直接:mod_php 模式下 PHP 解释器嵌在 httpd 进程里,某个脚本把内存吃满,清理时连带 httpd 一起崩;fpm 模式下 PHP 进程和 Web 服务分离,PHP 出错最多是这一秒的请求 500,Apache 还能继续扛静态资源。上一章装的php-fpm包已经提供了完整环境,现在只需要把它接到 Apache 上。
4.2 虚拟主机配置:ServerName、DocumentRoot 与 PHP 转发
先创建站点目录和日志目录:
mkdir -p /data/www/example/{html,logs}然后写虚拟主机配置文件:
cat > /etc/httpd/conf.d/example.conf <<'EOF' <VirtualHost *:80> ServerName example.com ServerAlias www.example.com DocumentRoot /data/www/example/html <Directory /data/www/example/html> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" </FilesMatch> ErrorLog /data/www/example/logs/error.log CustomLog /data/www/example/logs/access.log combined </VirtualHost> EOFServerName决定 Apache 收到 Host 头时匹配哪个虚拟主机,ServerAlias用来加别名域名。DocumentRoot指向站点根目录,路径改成/data/www后必须做第 2 章 2.4 节的 SELinux 标签,否则访问必然 403。Options -Indexes禁止列目录,AllowOverride All开放.htaccess的使用权限,这对很多 PHP 框架的伪静态规则是必须的。
FilesMatch那一行是整个配置的核心:所有.php结尾的请求被转发给/run/php-fpm/www.sock这个 Unix socket。CentOS 7 的 httpd 2.4.6 默认没有启用 mod_proxy_fcgi 模块,需要先执行:
sed -i 's/#LoadModule proxy_module/LoadModule proxy_module/; s/#LoadModule proxy_fcgi_module/LoadModule proxy_fcgi_module/' /etc/httpd/conf.modules.d/00-proxy.conf apachectl configtest配置检测输出Syntax OK后,重启 httpd:
systemctl restart httpd到这里,在/data/www/example/html/下放一个index.html,通过浏览器访问http://服务器IP/应该能看到页面。如果打不开,按顺序查三件事:防火墙有没有放行 80(第 2.3 节)、SELinux 有没有给/data/www打标签(第 2.4 节)、域名有没有解析到这台服务器。这正好对应热词里反复出现的「apache配置域名无法访问」,大部分案例都死在这三关。
4.3 用 PDO 验证 PHP 到 MySQL 的整条链路
三件套能不能协同工作,得用一个真实的 PHP 脚本验证。在站点目录下创建db_test.php:
<?php $dsn = 'mysql:host=127.0.0.1;port=3306;dbname=test;charset=utf8mb4'; $user = 'webuser'; $pass = 'Str0ngPass123'; try { $pdo = new PDO($dsn, $user, $pass); $stmt = $pdo->query("SELECT VERSION()"); echo 'MySQL version: ' . $stmt->fetchColumn(); } catch (PDOException $e) { echo 'Connection failed: ' . $e->getMessage(); }这里优先用 PDO 而不是 mysqli,一方面 PDO 是 PHP 官方推荐的数据库抽象层,换了数据库不用改业务代码;另一方面charset=utf8mb4能避免中文乱码——热词里不少「php序列化中文」「mysql排序」的问题,根源就是连接字符集设错了,而不是代码逻辑的问题。host 写127.0.0.1是关键,PHP 连本机 MySQL 时走 TCP 协议,如果写localhost会让 mysqlnd 尝试走 Unix socket,而 socket 文件路径在不同的 MySQL 发行版里有差异,容易排查半天。
命令行先验一遍语法:
php -l /data/www/example/html/db_test.php然后浏览器访问http://服务器IP/db_test.php,看到MySQL version: 8.x.x说明 Apache→PHP→MySQL 整条链路通了。如果报SQLSTATE[HY000] [2002] Connection refused,先确认mysqladmin -uroot -p status能不能连,能连就检查httpd_can_network_connect_db布尔值。
4.4 建库建用户:密码策略与 MySQL 8 认证插件的坑
PHP 用 root 连数据库是不可接受的,建一个专用账号并遵循最小权限:
CREATE DATABASE app DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'app'@'localhost' IDENTIFIED BY 'Str0ngPass123'; GRANT ALL PRIVILEGES ON app.* TO 'app'@'localhost'; FLUSH PRIVILEGES;utf8mb4是必须的,MySQL 8 里 utf8 已经是 utf8mb3 的别名,存 emoji 或生僻字会直接报错。GRANT ... ON app.*只给 app 库的权限,不给全局权限;如果后续有独立的日志库,就再建一个账号,别复用。
MySQL 8 默认认证插件是caching_sha2_password,PHP 7.4 的 mysqlnd 原生支持,但 PHP 5.4 不支持。如果你被迫用旧版 PHP,只能创建用户时显式指定:
CREATE USER 'app'@'localhost' IDENTIFIED WITH mysql_native_password BY 'Str0ngPass123';这是在「升级 PHP」和「降级认证」之间做的取舍。平时看到PDO::__construct(): The server requested authentication method unknown to the client这类报错,基本就是这个原因。
5. 上线前 10 分钟:httpd 自检与 PHP-FPM 慢日志定位
改动 httpd 或 php-fpm 配置后,先做语法自检再重载服务,能避免把正在运行的服务搞挂:
apachectl configtest php-fpm -t两者都输出Syntax OK才允许下一步。重载时用systemctl reload而不是restart,reload 只重新读取配置、不中断现有连接,对在线业务友好得多。可以写成一个组合命令:
apachectl configtest && systemctl reload httpd && systemctl reload php-fpm&&保证前一条命令成功才执行后一条,语法错时不会触发重载。
PHP-FPM 慢日志是定位线上性能问题的利器。编辑/etc/php-fpm.d/www.conf:
request_slowlog_timeout = 3 slowlog = /var/log/php-fpm/www-slow.logrequest_slowlog_timeout = 3表示脚本执行超过 3 秒就被视为慢请求,PHP-FPM 会把当时的函数调用栈写到slowlog指定的文件里。改完重启 php-fpm,过一段时间tail -f /var/log/php-fpm/www-slow.log,能看到类似require() /data/www/example/html/index.php的调用链。这时候去查对应的函数,十有八九是 SQL 查询没有索引或循环里发 HTTP 请求。
压测和日志要配合着用,单开压测看不出问题:
ab -n 2000 -c 50 http://127.0.0.1/index.php tail -f /var/log/httpd/error_log /var/log/php-fpm/www-error.logab的-n是总请求数,-c是并发数,50 并发对一个 2C4G 的实例是比较温和的起始值。跑压测的时候盯着两个日志文件,如果 error_log 里大量出现connection refused而 php-fpm 的日志没反应,检查 fpm 的pm.max_children,默认值通常只有 5,并发一起来就排队超时。定位单个请求卡顿更直接的办法是抓一条实时请求:
curl -s -w "time_total: %{time_total}s\n" -o /dev/null http://127.0.0.1/index.phptime_total超过 1 秒再配合慢日志里的调用栈,就能确定瓶颈是在框架初始化、数据库查询还是外部 API 调用上。这套「自检—慢日志—压测」组合,基本覆盖了 90% 的 Apache+PHP+MySQL 性能问题。
本文还有配套的精品资源,点击获取