简介:一份面向Linux运维初学者与Web环境搭建者的CentOS 7 LAMP环境配置指南,聚焦Apache、PHP、MySQL三个核心组件的安装与联动。文档从准备工作讲起,覆盖firewalld关闭、iptables端口放行、SELinux禁用等基础设置,随后分步说明Apache的安装、httpd.conf常用修改与语法检查,以及PHP解析器的部署和Apache处理.php文件的配置方式,最后给出MySQL服务启动及整合验证的要点。内容以命令行操作为主线,附带配置文件关键片段和验收思路,便于按步骤实践。资源为单个PDF文档,文件大小955KB,目前已有109人学习。对于需要快速搭建Linux动态网站环境、梳理LAMP配置流程的读者而言,可直接对照命令完成部署,并借助文中的检查与重启方法规避常见配置错误。
1. CentOS 7 上把 Apache + PHP + MySQL 装通:一份老 PDF 背后的生产环境刚需
接到一个活:一台 CentOS 7 老服务器,要跑一套很多年前的 PHP 业务系统。系统不换发行版、上容器也不现实,只要求在现网把这套 Apache + PHP + MySQL 搭起来。这个标题看起来像十几年前的老教程,但直到今天,CentOS 7 的生产环境里依然大量跑着 LAMP——要么是迁移成本太高,要么是配套的 PHP 项目只认这套组合。这篇文章就把这套配置从 yum 源选型、服务安装、虚拟主机配置到踩坑排错完整讲透。适合两类人:刚接手 CentOS 7 服务器的运维,以及要在老环境里重新部署 PHP 项目的开发。读完你能独立装完,并且知道 Apache 启动失败、MySQL socket 连不上、PHP 不解析这些问题到底出在哪一层。
2. 装 Apache 与 PHP:yum 源选型、最小命令和 2.4 的语法坑
2.1 先换一个能用的 yum 源:CentOS 7 默认源的 PHP 只有 5.4
CentOS 7 官方源给出的 httpd 是 2.4.6、PHP 是 5.4,这在 2014 年没问题,但放到现在跑新项目就是处处报警告。所以第一步不是装包,而是把 yum 源整理好。常见做法是先备份自带 repo,再换成阿里云镜像源,同时装 EPEL 和 Remi 仓库——Remi 把 PHP 按版本拆成了 php74、php80 这类模块,装哪个版本由你选,默认源里根本没有这个能力。
# 备份原始 repo 配置,避免换源后想回退连后悔药都没有 mkdir -p /etc/yum.repos.d/backup mv /etc/yum.repos.d/CentOS-*.repo /etc/yum.repos.d/backup/ # 用阿里云镜像站的 CentOS 7 repo,手工下载到对应位置 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo yum clean all && yum makecache # 装 EPEL 和 Remi 仓库,Remi 是装新版 PHP 的前提 yum install -y epel-release yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm这段逻辑有个容易被忽略的点:先备份再覆盖,换了源以后如果发现公司内网走代理、或者某些内网包只有默认源才有,可以随时切回去。Centos-7.repo 里我一般只看 base 和 updates 两个源的 baseurl 是否指向 mirrors.aliyun.com,其他源路径错了会直接影响 yum makecache 的完成度。另外,如果服务器在内网,还要在 /etc/yum.conf 里加上 proxy 配置,否则 curl 这步就会卡住。
这里给你一个提示,如果你维护的机器比较多,建议把这个过程写成一条脚本,固定好仓库版本,别每次手工下载。
2.2 Apache 最小安装:httpd.conf 里必须先改的两个地方
装 Apache 本身不复杂,复杂的是装完以后那些“启动失败请检查相关配置”的报错。我一般建议在启动之前就把 httpd.conf 的核心参数改到位,而不是等 systemctl start 报错了再回头猜。
# 先安装 httpd yum install -y httpd # 把默认的 ServerName 注释去掉,改成 localhost 或实际域名 sed -i 's/#ServerName www.example.com:80/ServerName localhost:80/' /etc/httpd/conf/httpd.conf # 改完先做语法检查,再启动 httpd -t systemctl enable --now httpdServerName 不配只是启动时给你一句 “Could not reliably determine the server's fully qualified domain name” 的警告,但如果你后面要做虚拟主机,没有 ServerName 的站点会变成默认兜底站,访问 IP 时串到第一个 vhost 上去,这是最隐蔽的玄学问题之一。httpd -t是apachectl configtest的简写,改任何配置之后先跑一遍这条,能拦下 80% 的语法类启动失败。
接着要看一下内置的默认站点目录配置。CentOS 7 的 httpd.conf 里默认有一个<Directory "/var/www/html">块,Apache 2.4 的访问控制语法跟 2.2 完全不同——2.2 时代的Order Deny,Allow写法已经废弃,2.4 统一用Require指令。
<Directory "/var/www/html"> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory>Require all granted是 2.4 的默认授权写法,等价于以前的全放开;如果你只想让内网访问,改成Require ip 192.168.1.0/24就行。AllowOverride All是为了让 .htaccess 生效的,如果项目里没有用 .htaccess 做路由改写,我建议改成AllowOverride None,省掉每次请求都去查目录级别的配置文件。这个也是新手最容易踩的坑:拿网上的 2.2 配置直接贴,结果站点 403 或者 Apache 起不来。
2.3 装 PHP 并让 Apache 认出来:mod_php 和 PHP-FPM 怎么选
装 PHP 的方式有两条路线,取决于你的场景。单机跑一个小站点,用 mod_php 模块方式最简单;如果计划做多版本 PHP 共存、或者后面要和 Nginx 混用,就上 PHP-FPM。
用 Remi 源装 PHP 7.4 的命令是这样,注意包名前缀带 php74:
# 通过 remi 源安装 PHP 7.4 及常用扩展 yum install -y php74-php php74-php-mysqlnd php74-php-fpm php74-php-gd php74-php-xml # 配置 php.ini 时区,避免 date() 相关函数全部报警告 sed -i 's/;date.timezone =/date.timezone = Asia\/Shanghai/' /etc/opt/remi/php74/php.ini # 启动 php-fpm 并重启 httpd systemctl enable --now php74-php-fpm systemctl restart httpd包名里的 mysqlnd 是 PHP 7.x 的官方 MySQL 驱动,老旧的 php-mysql 包已经不再维护,如果你在装扩展时看到依赖的是 php-mysqlnd 而不是 php-mysql,这是正常现象。php.ini 的时区如果不设,后面跑任何用 date() 的项目都会在日志里刷 “It is not safe to rely on the system's timezone settings”,这个错不算致命但很烦。
如果你不想用 Remi,直接yum install -y php php-mysqlnd也可以,只是版本会被锁在 5.4。对于老项目这未必是坏事——很多遗留系统还真的只跑得动老版本。但新项目我建议直接接受 PHP 7.4 或 8.0,别在 5.4 上给未来的自己挖坑。
装完以后验证 Apache 是否认得了 PHP,用一条命令就够了:
httpd -M | grep php输出里有php7_module之类的行说明模块已加载。选 PHP-FPM 模式的注意:httpd 默认不会执行 PHP,你需要在虚拟主机里加一段 proxy 配置,这个放到后面第 4 章结合实际配置讲。这里先把模块方式跑通,是最稳的第一步。
3. 装 MySQL:从 MariaDB 到官方 MySQL 5.7,连接方式决定后面省不省心
3.1 CentOS 7 的 yum 装出来的“mysql”其实是 MariaDB
在 CentOS 7 上直接yum install mysql-server,装出来的实际是 MariaDB 5.5,这是 CentOS 用 MariaDB 替代 MySQL 之后的结果。如果项目不挑版本、走默认配置就行,我推荐就用它,因为 yum 替你把依赖和服务脚本全部处理好了,安装成本最低。
# 安装 MariaDB 服务端和客户端 yum install -y mariadb-server mariadb # 启动并设置开机自启 systemctl enable --now mariadb # 执行初始化安全设置:设置 root 密码、去匿名账号 mysql_secure_installationmysql_secure_installation是跑完必做的清理步骤,它会让你设置 root 密码、删除匿名用户、移除 test 库。注意翻译一下,它默认允许 root 通过 socket 方式以本地身份登录,这在 CentOS 上是 MariaDB 的默认行为——如果你后面发现 root 明明设了密码但 PHP 连不上,先想 socket 路径,再想密码本身。
如果你的项目明确要求 MySQL 5.7/8.0,那使用 MariaDB 可能会在版本兼容性上翻车,特别是用到 JSON 函数、窗口函数这类功能和 MySQL 有差异的语法。这时候就要走官方仓库装 MySQL,下面细说。
3.2 用官方 yum 仓库装 MySQL 5.7:临时密码那一步别跳过
MySQL 官方提供了一个 yum 仓库,装上它之后yum install mysql-community-server就能拿到真正的 MySQL。我一般用 localinstall 方式安装仓库,让 yum 自己解析依赖。
# 安装 MySQL 官方 yum 仓库(版本号以官方仓库页面为准) yum localinstall -y https://repo.mysql.com/mysql57-community-release-el7-11.noarch.rpm # 安装服务端 yum install -y mysql-community-server # 启动服务 systemctl enable --now mysqld # MySQL 5.7 首次启动自动生成临时 root 密码,去 error log 里捞 grep 'temporary password' /var/log/mysqld.log这里有个关键点:第一次登录必须用这条临时密码,登录后立刻改密码。5.7 默认装了 validate_password 插件,对密码强度有要求——你设123456会直接被拒,至少要大小写字母加数字和符号的组合。很多教程省略了这一步,导致用户后面在“为什么密码对了还是登不上”上浪费大量时间。
首次登录改密码的 SQL 语法是这样:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Your_Strong_Pass_2024';3.3 建业务库和专用账号:别把 root 直接给 PHP 用
数据库启动后,下一步是创建业务库和一个专用账号。这样做的好处是,即使 PHP 代码被注入漏洞,攻击者拿到的也只是一个受限账号,而不是整个数据库。
-- 创建业务库,字符集用 utf8mb4 而不是 utf8 CREATE DATABASE appdb DEFAULT CHARACTER SET utf8mb4; -- 创建专用账号,只授权给 appdb 这一个库 CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'App_Pass_2024'; GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost'; FLUSH PRIVILEGES;utf8mb4比utf8多支持四字节字符,emoji 和生僻字都能正常存。MySQL 的utf8实际只是 utf8mb3,是不完整的 UTF-8,这也是存取中文相关数据时乱码的隐藏源头之一。
然后给 PHP 一个最小连接测试,这一步能确认 PHP、MySQL、网络三层都没有问题:
<?php // 最小连接测试:用 PDO 走 TCP 连接 MySQL $dsn = 'mysql:host=127.0.0.1;dbname=appdb;port=3306;charset=utf8mb4'; $user = 'appuser'; $pass = 'App_Pass_2024'; try { $pdo = new PDO($dsn, $user, $pass); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo 'connect ok: ' . $pdo->query('SELECT VERSION()')->fetchColumn(); } catch (PDOException $e) { // 生产环境别直接回显异常信息,写日志更安全 error_log($e->getMessage()); echo 'connect failed'; }注意 DSN 里 host 写的是 127.0.0.1,这个写法会强制走 TCP 协议;如果写host=localhost,PHP 会尝试通过 Unix socket 连接,而 socket 的路径一旦和 mysqld 实际监听的路径不一致,就会触发经典报错error 2002 (HY000): can't connect to local MySQL server through socket '/tmp/mysql.sock'。这个坑到第 5 章会详细展开。这里先把 TCP 方式跑通,后面再决定要不要切 socket。
4. 把三件套串成站点:虚拟主机、PHP-FPM 拆分和防火墙/SELinux 放行
4.1 配置一个像样的虚拟主机:别把东西直接丢进 /var/www/html
很多人配置完 LAMP 就把项目文件直接丢到/var/www/html下面,这在小项目上没问题,但当你开始跑多个站点、需要各自独立的日志和目录权限时,虚拟主机几乎是必须的。在/etc/httpd/conf.d/下新建一个 vhost 配置文件,一个文件管一个站点,出问题的时候定位范围小很多。
<VirtualHost *:80> ServerName app.example.com DocumentRoot /var/www/app/public ErrorLog /var/log/httpd/app-error.log CustomLog /var/log/httpd/app-access.log combined <Directory "/var/www/app/public"> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> </VirtualHost>几个参数说清楚:Options -Indexes是关闭目录列表,避免访问没有 index.php 的目录时整个文件结构被列出来,这是生产环境的基本卫生习惯;AllowOverride All允许项目里的 .htaccess 生效,如果你用的是 Laravel、ThinkPHP 这类框架,路由重写经常依赖它——但如果你能在 vhost 里直接写完规则,我建议关掉它节省性能。DocumentRoot指向 public 目录是现在 PHP 框架的惯例,把入口限制在 public 子目录,模型、配置、日志这些文件就不会被 web 直接访问。
目录创建和权限这块也有固定做法:
mkdir -p /var/www/app/public echo '<?php phpinfo();' > /var/www/app/public/index.php chown -R apache:apache /var/www/appchown给 apache 用户是因为 httpd 进程是以 apache 身份运行的,如果目录属主是 root,PHP 写入文件时会报权限不足。很多“图片传不上去”“日志写不了”的问题,根子就是这里属主不对。
4.2 走 PHP-FPM 而不是 mod_php:什么时候值得切
第 2 章提到过两种 PHP 运行模式。mod_php 是作为 Apache 模块加载的,简单直接;PHP-FPM 是独立进程池,PHP 进程由 FPM 管理,不再依赖 Apache 的生命周期。切到 FPM 模式后,PHP 的并发配置更灵活,跑多版本 PHP 也不会互相干扰,而且重启 PHP 不用动 Apache。
在 Apache 2.4 里接 PHP-FPM 的常见做法是用mod_proxy_fcgi:
# 在 vhost 里加一段:所有 .php 请求转发给 php-fpm 的 socket ProxyPassMatch ^/(.*\.php(/.*)?)$ "unix:/var/run/php74-php-fpm.sock|fcgi://127.0.0.1:9000"这条配置的写法很讲究:unix:后面是 php-fpm 的 socket 文件路径,这个路径必须和你 php-fpm 配置文件里的listen参数一致。如果你在/etc/opt/remi/php74/php-fpm.d/www.conf里看到listen = /var/run/php74-php-fpm.sock,那这里就填这个路径;如果改成了listen = 127.0.0.1:9000,那配置应该写成fcgi://127.0.0.1:9000。前后不对应会出现 503 或者 500 错误,看日志时注意区分。
给一个最直接的选型建议:如果这台机器就跑一个站点、PHP 版本固定,mod_php 最省心;如果未来要同时跑两套不同 PHP 版本的项目、或者你正在用源码包编译 PHP 扩展做调试,PHP-FPM 更合适——因为 FPM 模式下单独重启 PHP 不会中断 Apache 的连接,调试成本低很多。
4.3 防火墙和 SELinux:两层放行缺一不可
HTTP 服务装完却访问不了,第一步查的不是 httpd 配置,而是防火墙。CentOS 7 默认用了 firewalld,80 端口默认不放行。
# 放行 http 服务并重载防火墙 firewall-cmd --permanent --add-service=http firewall-cmd --reload # 验证端口已放行 firewall-cmd --list-services防火墙看着没问题之后,还有一个更隐蔽的拦路虎:SELinux。CentOS 7 默认是 Enforcing 模式,它会拦 Apache 进程的网络连接。最常见的表现是:网页能打开、PHP 代码能跑,但连接数据库时报权限错误,命令行里用同一个账号连数据库却是正常的。
# 查看当前 SELinux 状态 getenforce # 打开 httpd 访问数据库的布尔值,-P 让重启后依然生效 setsebool -P httpd_can_network_connect_db on # 如果 PHP 还需要连 Redis 等外部网络服务,再放开通用网络连接 setsebool -P httpd_can_network_connect on很多人为了省事直接setenforce 0甚至改配置文件关掉 SELinux,这种操作在公网环境的服务器上我不建议。CentOS 7 默认开启的这套访问控制虽然平时不出声,但真出事的时候能帮你挡不少东西。正确做法是只放开需要的布尔值。
顺带提一个业务接口场景:如果你的站点是给前端提供 JSON 接口的,PHP 里把数组json_encode输出给前端的fetch或axios用,这是常规操作。涉及跨域时,能用 CORS 响应头解决的问题,不要用 jsonp——jsonp 是动态插入 script 的方式,在安全性上比 CORS 脆弱得多,特别是不该出现在需要登录态的接口上。
5. 避坑实录:LAMP 环境下最常翻车的 5 个症状与对应解法
5.1 Apache 启动失败、请检查相关配置:先分清是语法错误还是端口占用
现象:systemctl start httpd报Job for httpd.service failed,旧教程里就一句“请检查相关配置”,没有更多信息。
原因:最常见就两种——httpd.conf 语法错误,或者 80 端口被 Nginx、别的服务占掉了。还有一种情况是 httpd.conf 里的Listen 80写重复了,也会直接起不来。
解决步骤:
# 第一步永远先跑语法检查,别去看日志 httpd -t # 语法没问题再启动,如果还失败,看 systemd 日志 systemctl start httpd journalctl -u httpd -n 30 --no-pager # 如果提示端口被占用,用 ss 找到占用进程 ss -lntp | grep :80httpd -t只检查配置文件语法,不检查端口。journalctl -u httpd是 systemd 时代的日志读法,比去翻/var/log/httpd/error_log更及时、更全。ss输出里最后一列就是占用端口的进程名和 PID,看到是 Nginx 占着 80 就想办法协调端口,别直接 kill。
5.2 MySQL 5.1 启动失败、请检查相关配置:数据目录权限和 my.cnf 的 socket 路径
现象:启动 MySQL/MariaDB 时节点失败,错误提示是笼统的 “请检查相关配置”,具体原因被日志挡在后台。
原因:多数是/var/lib/mysql数据目录的属主不是 mysql,或者/etc/my.cnf里手写了错误的 socket 路径。CentOS 7 里 MariaDB 和 MySQL 是两套体系,按老经验去改 my.cnf 很容易写错。
解决步骤:
# 先看错误日志,别瞎猜 tail -50 /var/log/mysql/mysqld.log 2>/dev/null || tail -50 /var/log/mysqld.log # 数据目录属主不对就改,这是最常见的启动失败原因 chown -R mysql:mysql /var/lib/mysql # 确认 my.cnf 里的 socket 路径,客户端、服务端、PHP 三处必须一致 grep -n 'socket' /etc/my.cnf这里要特别注意:MySQL 官方源和 MariaDB 的日志文件路径是不一样,找不到日志就先去/etc/my.cnf看log-error配置指向哪。改完属主之后重启,如果还失败,再回去看日志——日志排错永远是第一选择,比在网上搜报错片段快得多。
5.3 error 2002: can't connect to local MySQL server through socket:localhost 走 socket、127.0.0.1 走 TCP
现象:PHP 连接数据库时报error 2002 (HY000): can't connect to local MySQL server through socket '/tmp/mysql.sock',命令行用 root 却连得上。
原因:PHP 的 DSN 里写的是host=localhost,PHP 会自然选择用 socket 文件连接,但 mysqld 实际监听的 socket 路径不在/tmp/mysql.sock,两边对不上。
解决思路:先确认 mysqld 在跑,再找出 socket 真实路径,然后把 PHP 侧改成对应的连接方式。
# 确认 mysqld 进程和 socket 文件位置 pgrep -x mysqld find / -name '*.sock' 2>/dev/null | grep -i mysql # PHP 侧最省事的做法:强制走 TCP,绕开 socket 路径问题 $dsn = 'mysql:host=127.0.0.1;port=3306;dbname=appdb;charset=utf8mb4';localhost走 socket、127.0.0.1走 TCP 是 PHP/MySQL 交互里翻车频率最高的经典盲区。如果项目里有的地方写 localhost、有的地方写 127.0.0.1,线上就会出现“这台机器能连那台不能连”的怪现象。代码里的host统一用 127.0.0.1 + 指定port,能让问题少一大半。如果对性能有要求,再考虑走 socket——本地 socket 连接因为省掉 TCP 协议栈的开销,延迟确实能低一些,但前提是 socket 路径统一。
5.4 访问 PHP 文件变成下载或源码被打印:httpd 没找到 PHP 处理器
现象:浏览器访问.php文件,要么弹下载框,要么直接把 PHP 源码文本打在页面上,而不是执行。
原因:装完 PHP 后没有重启 httpd,或者装的是 PHP-FPM 模式,但虚拟主机里没加ProxyPassMatch转发规则。Apache 收到请求后不知道把.php交给谁解析,就当成普通静态文件返回了。
解决步骤:
# 确认 PHP 模块是否被 httpd 加载 httpd -M | grep php # mod_php 模式下输出里应有 php7_module 或对应版本行 # 没有的话,重装 php 与 httpd 兼容的版本 # 命令行先验证 PHP 本身没问题 php -r 'echo "ok\n";' # 最后重启 httpd 生效 systemctl restart httpd如果命令行能输出 ok 但 web 访问还是下载,问题就锁定在 httpd 和 PHP 的集成上。常见排查法:看目录里有没有.htaccess配置了奇怪的处理规则,再看 vhost 里的 Directory 块是不是把 php 文件的执行权限禁掉了。
5.5 SELinux Enforcing 下 PHP 连不上 MySQL,但命令行能连
现象:网页上打开数据库相关页面报权限错误,命令行里用同一个账号连数据库却完全正常。
原因:CentOS 7 默认 SELinux 是 Enforcing,httpd 进程出站的数据库连接被 SELinux 策略拦截。命令行操作是用户态,不经过 httpd 域,所以不受限。
解决步骤:
# 确认这不是玄学,直接查布尔值 getsebool -a | grep httpd_can_network # 打开 httpd 访问数据库的开关,加 -P 让配置重启后保留 setsebool -P httpd_can_network_connect_db on # 如果项目还连 Redis、Memcached 等外部服务 setsebool -P httpd_can_network_connect on这一条是 CentOS 6 迁移到 7 之后最容易踩的隐性差异:CentOS 6 时代很多装机教程第一步就关 SELinux,到了 7 默认开启,老配置原封不动搬过来就失灵。建议每次都先用getsebool -a看看现状,再决定要不要开开关,不要盲目 setenforce 0 关掉整个 SELinux。
6. 验证与收尾:从 phpinfo 探针到压测,把配置钉在纸上
整套配置完成后,别急着交工。先做一条链路的完整验证,再做一个最小压测把性能基线留在手里,后续出问题才有对比依据。
先跑最基础的验证命令:
# 看 HTTP 响应头,确认返回 200 curl -I http://127.0.0.1/ # 确认 PHP 在 web 层能执行,而不是被当静态文件返回 curl http://127.0.0.1/index.php # 看 PHP 已加载的模块,确认 mysqlnd、gd 这些都在 php -m | grep -E 'pdo|mysqlnd|gd'然后做一个顺手的最小压测,用 Apache 自带的 ab 工具:
yum install -y httpd-tools ab -n 100 -c 10 http://127.0.0.1/index.php压测主要看两个数:Requests per second和Failed requests。前者是吞吐量基线,后者是 0 才合格。如果吞吐量很低,而且服务器 CPU 没跑满,八成是 PHP-FPM 进程数不够,去php-fpm.d/www.conf里调pm.max_children和pm.max_requests,这两个参数决定 FPM 能同时撑多少 PHP 进程、每个进程扛多少请求后自动回收。
我自己的习惯是每改一个配置,先cp一份带日期的备份,再跑语法检查,最后才重启服务。比如改 httpd.conf 之前先cp httpd.conf httpd.conf.bak-20240601,改完跑httpd -t,确认没问题再systemctl restart httpd。这个习惯救过我很多次,配置文件被你改坏的时候,一条mv命令就能回到上一个可用状态。这套 LAMP 环境装完后,把 yum 源选型、PHP 版本、数据库账号、selinux 布尔值这几项记到文档里,下次扩容直接照抄,比临时回忆靠谱得多。希望帮到你。
本文还有配套的精品资源,点击获取