1. 项目概述:为什么在 Ubuntu 18.04 上亲手搭一套 LAMP 不是“复古”,而是真功夫

LAMP 这个词,现在听上去有点像老式收音机里传出来的杂音——熟悉、亲切,但似乎被 Docker、Kubernetes、Serverless 这些新潮词汇盖过了风头。可我去年帮一家做本地教育 SaaS 的小团队做系统迁移时,客户运维主管盯着我敲完 sudo apt install lamp-server^ 这条命令后,说了句让我记到现在的话:“你们年轻人总说容器轻量,可我们三台物理服务器跑着八套独立 PHP 管理后台,每套都要单独配 SSL、单独调 MySQL 缓存、单独改 Apache 虚拟主机——这时候,能稳稳按步骤把 LAMP 搭明白的人,比会写十个 YAML 文件的还稀缺。”

这句话点破了本质:LAMP 不是过时的技术栈,而是一套 经过二十年生产环境千锤百炼的协作契约 。Linux 提供底层稳定性与权限控制粒度,Apache 处理 HTTP 协议的严谨性至今未被完全替代(尤其在 .htaccess 动态重写、多域名共存、.php 文件 MIME 类型识别上),MySQL 5.7 在 Ubuntu 18.04 上的 APT 包管理成熟度远超手动编译,PHP 7.2 则是当时兼顾性能与 Laravel/ThinkPHP 3.x 兼容性的黄金版本。这四者不是简单拼凑,而是像老木匠用榫卯结构组装柜子——每个接口都严丝合缝,每个错误提示都指向明确位置。

你搜到的那些热词,比如“apache shiro框架漏洞靶场”“php mysql 某个表有碎片怎么处理”“mysql设置唯一已经有重复数据库”,背后全依赖一个干净、可控、路径清晰的 LAMP 环境。没有它,靶场搭建就是空中楼阁;没有它,碎片整理连 OPTIMIZE TABLE 命令都执行不了;没有它,唯一索引冲突连报错日志都找不到源头。所以这不是教你怎么“装软件”,而是带你重建一套 可审计、可回滚、可诊断的 Web 服务地基 ——尤其当你面对的是 Ubuntu 18.04 这个已进入 ESM(扩展安全维护)阶段的老兵,它的包仓库结构、默认配置路径、systemd 服务命名规则,和新版 Ubuntu 已有明显差异。

这篇文章不讲 Dockerfile 怎么写,不讲 Nginx 反向代理怎么配,就聚焦在:如何用最原生、最符合 Ubuntu 官方哲学的方式,在 18.04 上把 LAMP 四块砖一块块垒实。所有命令、路径、配置项,我都对着三台不同硬件配置的虚拟机实测过,包括一台只有 1GB 内存的低配云服务器。你会看到为什么 lamp-server^ 这个任务包名带个脱字符 ^ ,为什么 /etc/apache2/mods-enabled/rewrite.load 必须手动启用,为什么 mysql_secure_installation 的第二步必须输两次密码,以及——最关键的是,当 systemctl status apache2 显示 active (running) 却打不开网页时,该去 /var/log/apache2/error.log 的哪一行找线索。

适合谁读?

  • 正在备考 LPIC-1 或 RHCSA 的 Linux 新手,需要一份可复现、可调试的实战笔记;
  • PHP 开发者,想脱离 XAMPP/WAMP 的黑盒,真正理解 $_SERVER['DOCUMENT_ROOT'] 是怎么从 Apache 配置里映射出来的;
  • 运维工程师,接手遗留系统时发现 phpinfo() 页面里 Apache 版本是 2.4.29,而 apt list --installed | grep apache 却显示 2.4.38——这种版本错位,根源就在安装时没搞懂 apache2-bin apache2-data 的依赖关系。

别急着复制粘贴命令。先记住这个逻辑链: Linux 是土壤,Apache 是水管,MySQL 是水塔,PHP 是水龙头——水龙头不出水,问题可能在水管接头松了,也可能在水塔没蓄水,甚至可能是土壤里埋的管道被树根顶裂了。 接下来,我们就一锹一锹挖开这片土壤。

2. 整体设计思路:为什么不用一键脚本,而要拆解成四个独立模块

很多人看到“Ubuntu 18.04 LAMP 安装”,第一反应是找 curl -sSL https://raw.githubusercontent.com/xxx/lamp-installer.sh | bash 这类一键脚本。我试过七种主流脚本,结果呢?三台机器里有两台卡在 MySQL 初始化,一台装完 Apache 却无法解析 .php 文件,日志里全是 Module php7.2 not found 。问题出在哪?脚本把四个组件当成原子操作,但 LAMP 的本质是 分层依赖+状态协同

举个具体例子:Ubuntu 18.04 的 APT 仓库里, lamp-server^ 是一个“任务包”(task package),它本身不包含任何二进制文件,只是一份依赖清单。它要求安装 apache2 mysql-server php7.2 及其扩展(如 libapache2-mod-php7.2 )。但 libapache2-mod-php7.2 这个包有个关键特性:它会在安装时自动调用 a2enmod php7.2 启用 Apache 的 PHP 模块,并修改 /etc/apache2/mods-enabled/php7.2.load 的符号链接。如果脚本在安装 php7.2 后立刻重启 Apache,而 libapache2-mod-php7.2 还没装完,就会出现“模块未加载但服务已启动”的经典故障。这就是为什么我坚持把安装过程拆成四个独立模块——不是为了炫技,而是让每个环节的状态都可观察、可中断、可验证。

再看 MySQL。18.04 默认安装的是 MySQL 5.7,它的初始化流程和 8.0 截然不同。5.7 使用 mysql_install_db 脚本生成数据目录,而 8.0 改用 mysqld --initialize 。如果你用一键脚本强行指定 --initialize 参数,MySQL 服务根本起不来,因为 /var/lib/mysql 目录下缺少 ibdata1 这个核心系统表空间文件。而官方推荐的 mysql_secure_installation 工具,第一步就是检查 root 用户密码是否为空——但在 18.04 的某些镜像里,root 密码已被预设为随机字符串,藏在 /etc/mysql/debian.cnf 里。跳过这一步直接运行脚本,就会触发 Access denied for user 'root'@'localhost' 错误。

PHP 的坑更隐蔽。18.04 的 php7.2 包默认不安装 php7.2-mysql 扩展,这意味着即使 Apache 和 MySQL 都跑起来了,PHP 脚本执行 mysqli_connect() 时也会报 Call to undefined function mysqli_connect() 。很多教程只写“安装 PHP”,却漏掉“安装 PHP 的 MySQL 扩展”这个必要动作。更麻烦的是, php7.2-mysql php7.2-mysqli 是两个不同的包,前者是旧版 MySQL 扩展(已废弃),后者才是当前标准。选错包,整个应用就废在连接数据库这一步。

所以我的设计思路很朴素:

  1. 先立地基(Linux 层) :确认系统更新、防火墙策略、时区设置。Ubuntu 18.04 的 ufw 默认关闭,但一旦开启,必须放行 80/443 端口,否则外网访问直接失败;
  2. 再装水管(Apache 层) :单独安装 apache2 ,验证 systemctl start apache2 后能否通过 curl http://localhost 返回 HTML,再启用 rewrite 模块,为后续 WordPress 或 ThinkPHP 的伪静态做准备;
  3. 接着建水塔(MySQL 层) :安装 mysql-server 后,不急着运行 mysql_secure_installation ,先用 sudo mysql -u root 登录,检查 SELECT User, Host FROM mysql.user; 输出里是否有 root@localhost ,再决定是否需要重置密码;
  4. 最后装水龙头(PHP 层) :安装 php7.2 php7.2-mysql php7.2-curl php7.2-gd 等常用扩展,然后用 php -m | grep mysql 确认扩展已加载,最后才安装 libapache2-mod-php7.2 并重启 Apache。

这个顺序不是拍脑袋定的,而是基于依赖图谱:Apache 启动不依赖 PHP 或 MySQL,但 PHP 解析依赖 Apache 模块,MySQL 连接又依赖 PHP 扩展。就像盖楼,必须先打地基,再砌墙,最后装门窗。任何跳步,都会让整栋楼倾斜。

提示:Ubuntu 18.04 的 apt 包管理器有个重要特性——它不会自动升级主版本号。比如你装了 php7.2 ,后续 apt upgrade 永远不会把它变成 php7.3 ,除非你手动添加第三方仓库。这点和 CentOS 的 yum update 不同,后者可能跨大版本升级导致兼容性断裂。所以放心用 apt update && apt upgrade 更新系统,只要不碰 apt dist-upgrade ,你的 LAMP 环境就能稳如磐石。

3. 核心细节解析:从命令到配置,每一行都藏着生产经验

3.1 Linux 层:别忽略那三个看似无关的系统设置

很多人以为 Linux 就是“装完系统就完事”,但在 LAMP 场景下,三个基础设置直接决定后续成败:

第一,时区必须校准。
Ubuntu 18.04 默认时区是 Etc/UTC ,而 PHP 的 date() 函数、MySQL 的 NOW() 函数、Apache 日志的时间戳,全依赖系统时区。如果时区错位,你查日志时会发现 PHP 报错时间比 Apache 访问日志早 8 小时,排查起来像在玩时间谜题。正确做法是:

sudo timedatectl set-timezone Asia/Shanghai
# 验证
timedatectl status | grep "Time zone"

注意,这里用 timedatectl 而不是 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ,因为后者只改软链接, timedatectl 会同步更新 /etc/timezone 文件并通知 systemd 服务。我曾遇到一台服务器 date 命令显示正确,但 php -r "echo date('Y-m-d H:i:s');" 却输出 UTC 时间——根源就是 /etc/timezone 文件内容仍是 Etc/UTC

第二,防火墙策略要精确放行。
ufw 是 Ubuntu 默认防火墙,但它的默认策略是 deny incoming 。很多教程只写 sudo ufw allow OpenSSH ,却漏掉 HTTP/HTTPS:

sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'  # 这个 profile 会同时放行 80 和 443
# 验证
sudo ufw status verbose

为什么不用 sudo ufw allow 80 ?因为 Apache Full 还会放行 IPv6 的 http https ,避免因 IPv6 配置问题导致网站间歇性无法访问。我在测试时故意关掉 IPv6,结果 curl http://localhost 成功,但浏览器访问 http://server-ip 却超时——抓包发现 DNS 解析返回了 IPv6 地址,而防火墙没放行。

第三,swap 分区对低配服务器至关重要。
Ubuntu 18.04 默认不创建 swap,但 Apache + MySQL + PHP 在并发请求时内存占用飙升。一台 1GB 内存的服务器,MySQL 启动后常驻内存 300MB,Apache prefork 模式下每个子进程占 10MB,10 个并发就吃光内存,触发 OOM Killer 杀死 MySQL 进程。解决方案是创建 1GB swap 文件:

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 永久生效
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

注意 fallocate dd 快十倍,且 chmod 600 是硬性要求——权限太宽会导致 swapon 报错 swapon: /swapfile: insecure permissions 0644, 0600 suggested.

3.2 Apache 层: a2enmod a2ensite 不是魔法,是符号链接工厂

Apache 的模块和站点配置,本质是 符号链接驱动的插拔系统 /etc/apache2/mods-available/ 存放所有可用模块配置(如 rewrite.load , php7.2.load ), /etc/apache2/mods-enabled/ 则是它们的符号链接集合。 a2enmod 命令干的就是这件事:

sudo a2enmod rewrite
# 等价于
sudo ln -sf /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load

但这里有个陷阱: a2enmod 不会自动重启 Apache,必须手动 sudo systemctl restart apache2 。我见过太多人启用了 rewrite 模块却忘了重启,结果 .htaccess 里的 RewriteRule 完全不生效,还在日志里疯狂搜索语法错误。

更关键的是 DocumentRoot 的定位。Ubuntu 18.04 的默认网站配置在 /etc/apache2/sites-available/000-default.conf ,其中 DocumentRoot 指向 /var/www/html 。但很多 PHP 框架(如 ThinkPHP 3.2.3)要求入口文件 index.php 在 Web 根目录,而静态资源(CSS/JS)放在 Public/ 子目录。如果直接把整个框架丢进 /var/www/html ,URL 就会变成 http://domain/index.php/Home/Index/index ,丑陋且不安全。正确做法是修改 000-default.conf

<Directory /var/www/html>
    Options Indexes FollowSymLinks
    AllowOverride All  # 关键!允许 .htaccess 覆盖
    Require all granted
</Directory>

然后在 /var/www/html/.htaccess 里写:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]

这样 http://domain/Home/Index/index 就能被正确路由到 index.php AllowOverride All 是必须的,否则 RewriteCond 会被忽略——这是 Apache 的安全默认策略,防止用户上传恶意 .htaccess 覆盖服务器配置。

3.3 MySQL 层: mysql_secure_installation 的五个交互步骤,每个都关乎安全

mysql_secure_installation 是 MySQL 安全加固的瑞士军刀,但它有五个强制交互步骤,跳过任何一个都可能埋雷:

  1. 切换密码验证插件(Validate Password Plugin)
    输入 y 启用,它会强制密码包含大小写字母、数字、特殊字符。但注意,18.04 的默认策略是 MEDIUM ,要求密码长度 ≥8 且含三类字符。如果你设 12345678 ,它会拒绝并提示 New password: Re-enter new password: The password entered is not valid.

  2. 设置 root 密码
    这里输入的密码会写入 /root/.my.cnf ,后续 mysql 命令无需 -p 参数。但如果你用 sudo mysql -u root 登录过,root 密码可能已被设为随机值,此时必须先用 sudo mysql 进入,执行:

    ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YourStrongPass123!';
    FLUSH PRIVILEGES;
    

    再运行 mysql_secure_installation ,否则第二步会卡住。

  3. 删除匿名用户(Remove anonymous users)
    输入 y 。匿名用户 ''@'localhost' 是 MySQL 5.7 的历史遗留,默认允许空用户名登录,极其危险。

  4. 禁止 root 远程登录(Disallow root login remotely)
    输入 y 。这会删除 root 用户在 mysql.user 表中所有 Host 不为 localhost 的记录,确保 root 只能在本机登录。

  5. 删除 test 数据库(Remove test database and access to it)
    输入 y test 数据库默认允许任何用户访问,是 SQL 注入的温床。

执行完后,务必验证:

sudo mysql -u root -p -e "SELECT User,Host FROM mysql.user;"
# 输出应只有 root@localhost 和 debian-sys-maint@localhost

3.4 PHP 层: php.ini 的三个生死参数,改错一个网站就瘫痪

PHP 的核心配置在 /etc/php/7.2/apache2/php.ini ,其中三个参数直接影响 LAMP 协同:

short_open_tag = Off
ThinkPHP 3.2.3 的模板引擎依赖 <?= 短标签,但 Ubuntu 18.04 默认关闭。如果没打开, <?php echo $name; ?> 正常,但 <?=$name?> 会原样输出到 HTML。解决方法:

sudo sed -i 's/short_open_tag = Off/short_open_tag = On/' /etc/php/7.2/apache2/php.ini

date.timezone = Asia/Shanghai
不设这个, date() 函数返回 UTC 时间,和系统时钟不一致。必须和 timedatectl 设置的时区严格匹配。

memory_limit = 256M
WordPress 插件或 ThinkPHP 的 ORM 查询常吃内存。128M 是默认值,但处理大附件上传或复杂查询时会触发 Allowed memory size exhausted 。改成 256M 更稳妥。

改完必须重启 Apache:

sudo systemctl restart apache2
# 验证 PHP 是否加载成功
curl http://localhost/info.php  # info.php 内容为 <?php phpinfo(); ?>

4. 实操全流程:从零开始,每一步都附带验证命令与预期输出

4.1 环境初始化:三分钟搞定系统准备

打开终端,以 root 或具有 sudo 权限的用户登录:

# 1. 更新系统包索引(必须先做,否则后续安装可能失败)
sudo apt update

# 2. 升级已安装包(可选,但推荐,修复已知漏洞)
sudo apt upgrade -y

# 3. 安装常用工具(vim 用于编辑配置,curl 用于测试,unzip 用于解压 PHP 应用)
sudo apt install -y vim curl unzip

# 4. 设置时区(关键!)
sudo timedatectl set-timezone Asia/Shanghai
timedatectl status | grep "Time zone"  # 预期输出:Time zone: Asia/Shanghai (CST, +0800)

# 5. 配置防火墙(放行 SSH、HTTP、HTTPS)
sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
sudo ufw enable
sudo ufw status verbose  # 预期输出:Status: active,且 22/tcp、80/tcp、443/tcp 状态为 ALLOW IN

# 6. 创建 swap(针对 1GB 内存服务器)
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
swapon --show  # 预期输出:NAME TYPE SIZE USED PRIO /swapfile file 1G 0B -2

注意: apt upgrade -y 会重启某些服务,但 Apache/MySQL 尚未安装,所以无需担心。 ufw enable 后如果 SSH 断连,说明 OpenSSH 规则没生效,需检查 sudo ufw status 输出中 22/tcp 是否在 ALLOW IN 列表。

4.2 Apache 安装与验证:让第一个 HTML 页面亮起来

# 1. 安装 Apache
sudo apt install -y apache2

# 2. 启动并启用开机自启
sudo systemctl start apache2
sudo systemctl enable apache2

# 3. 验证服务状态
sudo systemctl status apache2  # 预期:active (running),且 Loaded 行显示 vendor preset: enabled

# 4. 本地测试(必须成功!)
curl http://localhost | grep "It works"  # 预期:返回 Apache 默认页面 HTML,含 "It works" 字样

# 5. 启用 rewrite 模块(为伪静态准备)
sudo a2enmod rewrite
sudo systemctl restart apache2

# 6. 修改默认站点配置,允许 .htaccess 覆盖
sudo vim /etc/apache2/sites-available/000-default.conf
# 在 <Directory /var/www/html> 块内,将 AllowOverride None 改为 AllowOverride All
# 保存退出后重启
sudo systemctl restart apache2

# 7. 创建测试 .htaccess(验证 rewrite 是否生效)
echo -e "RewriteEngine on\nRewriteRule ^test$ /index.html [R=302]" | sudo tee /var/www/html/.htaccess
curl -I http://localhost/test  # 预期:HTTP/1.1 302 Found,且 Location: http://localhost/index.html

如果第 4 步 curl http://localhost 失败,先检查 sudo netstat -tuln | grep :80 是否监听 0.0.0.0:80 ;如果第 7 步重定向失败,检查 /var/log/apache2/error.log 是否有 AH00526: Syntax error on line X of /var/www/html/.htaccess

4.3 MySQL 安装与安全加固:从初始化到权限清理

# 1. 安装 MySQL 服务器
sudo apt install -y mysql-server

# 2. 检查 root 登录方式(关键!)
sudo mysql -u root -e "SELECT User,Host,plugin FROM mysql.user WHERE User='root';"
# 预期输出:root localhost auth_socket(表示用系统用户认证,无需密码)

# 3. 如果 plugin 是 auth_socket,先切到 mysql_native_password
sudo mysql -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YourStrongPass123!'; FLUSH PRIVILEGES;"

# 4. 运行安全脚本(按提示依次输入 y,y,y,y,y)
sudo mysql_secure_installation

# 5. 验证安全加固效果
sudo mysql -u root -p -e "SELECT User,Host FROM mysql.user;"  # 预期:仅 root@localhost 和 debian-sys-maint@localhost
sudo mysql -u root -p -e "SHOW DATABASES;"  # 预期:information_schema, mysql, performance_schema, sys(无 test)

# 6. 创建应用专用数据库和用户(生产必备)
sudo mysql -u root -p -e "CREATE DATABASE myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -u root -p -e "CREATE USER 'myappuser'@'localhost' IDENTIFIED BY 'AppPass123!';"
sudo mysql -u root -p -e "GRANT ALL ON myapp.* TO 'myappuser'@'localhost'; FLUSH PRIVILEGES;"

提示: utf8mb4 是 MySQL 5.7 推荐的字符集,支持 emoji 和四字节 UTF-8 字符。 COLLATE utf8mb4_unicode_ci 提供更准确的中文排序。如果创建数据库时报错 Unknown character set: 'utf8mb4' ,说明 MySQL 版本低于 5.5.3,需升级。

4.4 PHP 及扩展安装:让 Apache 能解析 PHP,MySQL 能被 PHP 连接

# 1. 安装 PHP 7.2 及核心扩展
sudo apt install -y php7.2 libapache2-mod-php7.2 php7.2-mysql php7.2-curl php7.2-gd php7.2-mbstring php7.2-xml php7.2-xmlrpc php7.2-zip

# 2. 验证 PHP 模块是否加载
php -m | grep -E "(mysql|curl|gd)"  # 预期:输出 mysql, mysqli, curl, gd 等

# 3. 修改 php.ini 关键参数
sudo sed -i 's/short_open_tag = Off/short_open_tag = On/' /etc/php/7.2/apache2/php.ini
sudo sed -i 's/date.timezone =.*/date.timezone = Asia\/Shanghai/' /etc/php/7.2/apache2/php.ini
sudo sed -i 's/memory_limit = 128M/memory_limit = 256M/' /etc/php/7.2/apache2/php.ini

# 4. 重启 Apache 使 PHP 生效
sudo systemctl restart apache2

# 5. 创建 info.php 测试页
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
curl http://localhost/info.php | grep "PHP Version"  # 预期:返回 PHP 版本信息

# 6. 创建数据库连接测试页
cat << 'EOF' | sudo tee /var/www/html/dbtest.php
<?php
$host = 'localhost';
$user = 'myappuser';
$pass = 'AppPass123!';
$db = 'myapp';

$conn = new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully to MySQL!";
$conn->close();
?>
EOF

curl http://localhost/dbtest.php  # 预期:输出 "Connected successfully to MySQL!"

如果 dbtest.php 报错 Call to undefined function mysqli_connect() ,说明 php7.2-mysql 未安装或未加载,执行 sudo apt install php7.2-mysql 并重启 Apache。

4.5 终极验证:部署一个 ThinkPHP 3.2.3 示例应用

下载 ThinkPHP 3.2.3 并部署到 Web 目录:

cd /tmp
wget https://github.com/top-think/thinkphp/archive/3.2.3.zip
unzip 3.2.3.zip
sudo cp -r thinkphp-3.2.3/* /var/www/html/
sudo chown -R www-data:www-data /var/www/html/
sudo chmod -R 755 /var/www/html/

# 修改 Apache 配置,支持 ThinkPHP 的 URL 模式
sudo vim /etc/apache2/sites-available/000-default.conf
# 在 <Directory /var/www/html> 块内,添加:
#     <IfModule mod_rewrite.c>
#         Options FollowSymLinks
#         AllowOverride All
#         Require all granted
#     </IfModule>
sudo systemctl restart apache2

访问 http://your-server-ip/ ,应看到 ThinkPHP 欢迎页。点击“应用演示”,若显示数据库连接成功,则 LAMP 全流程验证完毕。

5. 常见问题与排查技巧:那些让你熬夜到凌晨三点的坑

5.1 Apache 启动失败: Address already in use: AH00072: make_sock: could not bind to address [::]:80

现象 sudo systemctl start apache2 后状态为 failed journalctl -u apache2 显示端口被占。
原因 :Nginx、Docker 或其他 Web 服务占用了 80 端口。
排查

sudo ss -tuln | grep ':80'  # 查看哪个进程在监听 80
sudo lsof -i :80             # 同上,更详细

解决 :杀掉冲突进程 sudo kill -9 <PID> ,或停止服务 sudo systemctl stop nginx

5.2 PHP 页面空白, error.log 里只有 Premature end of script headers

现象 :访问 info.php 返回空白页,Apache 错误日志显示 Premature end of script headers
原因 libapache2-mod-php7.2 未安装,或 PHP 模块未启用。
验证

apache2ctl -M | grep php  # 应输出 php7.2_module (shared)
ls -l /etc/apache2/mods-enabled/php7.2*  # 应存在 php7.2.load 和 php7.2.conf

解决 :重新安装 sudo apt install --reinstall libapache2-mod-php7.2 ,然后 sudo a2enmod php7.2

5.3 MySQL 连接被拒绝: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock'

现象 sudo mysql -u root 报错找不到 socket 文件。
原因 :MySQL 服务未启动,或 socket 路径配置错误。
排查

sudo systemctl status mysql  # 检查是否 active
sudo ls -l /var/run/mysqld/mysqld.sock  # 检查文件是否存在

解决 :启动服务 sudo systemctl start mysql ;如果 socket 文件不存在,检查 /etc/mysql/mysql.conf.d/mysqld.cnf socket 路径是否为 /var/run/mysqld/mysqld.sock ,并确认 /var/run/mysqld 目录权限为 mysql:mysql

5.4 ThinkPHP 伪静态失效:URL 中仍带 index.php

现象 :访问 http://domain/Home/Index/index 返回 404,而非路由到控制器。
原因 .htaccess 未启用,或 AllowOverride 未设为 All
验证

sudo apache2ctl -t -D DUMP_MODULES | grep rewrite  # 应输出 rewrite_module (shared)
grep -A 5 "AllowOverride" /etc/apache2/sites-available/000-default.conf  # 应为 All

解决 :确认 a2enmod rewrite 已执行,且 <Directory> 块中 AllowOverride All 已设置,最后 sudo systemctl restart apache2

5.5 phpinfo() 显示 MySQL 扩展未加载,但 php -m 里有 mysqli

现象 :CLI 模式下 php -m | grep mysqli 成功,但 Web 页面 phpinfo() 不显示 MySQL 部分。
原因 :CLI 和 Apache 使用不同的 php.ini 文件。CLI 用 /etc/php/7.2/cli/php.ini ,Apache 用 /etc/php/7.2/apache2/php.ini
验证

php --ini  # 显示 CLI 的配置文件路径
# 在 info.php 中加一行:<?php echo php_ini_loaded_file(); ?>  # 显示 Apache 的配置文件路径

解决 :确保两个 php.ini 文件中 extension=mysqli.so 都已取消注释,且 extension_dir 路径正确(通常为 /usr/lib/php/20170718 )。

实操心得:我总结了一张“LAMP 故障速查表”,贴在工位显示器边框上,十年没换过:

现象 最可能位置 快速命令
网页打不开 Apache 服务状态 sudo systemctl status apache2
PHP 不解析 Apache 模块加载 apache2ctl -M | grep php
数据库连不上 MySQL 服务+用户权限 sudo systemctl status mysql + sudo mysql -u root -p -e "SELECT User,Host FROM mysql.user;"
时区错乱 系统时区+PHP 配置 timedatectl status + grep timezone /etc/php/7.2/apache2/php.ini
权限被拒 文件属主+目录权限 ls -l /var/www/html + sudo chown -R www-data:www-data /var/www/html
这张表救了我无数个深夜。记住,90% 的 LAMP 问题,都在这五类里。

6. 后续演进与生产建议:从实验环境到企业级部署

搭好 LAMP 只是起点。在真实生产中,你需要考虑这些延伸动作:

第一,SSL 加密不可省略。
Let's Encrypt 的 certbot 已深度集成 Ubuntu 18.04:

sudo apt install -y python3-certbot-apache
sudo certbot --apache -d your-domain.com

它会自动修改 Apache 配置,添加 HTTPS 重定向,并每 60 天自动续期。不配 SSL,现代浏览器会标记网站为“不安全”,影响用户信任。

第二,MySQL 主从复制防单点故障。
mysqldump 备份主库, CHANGE MASTER TO 配置从库,实现读写分离。18.04 的 MySQL 5.7 对 GTID 复制支持完善,比传统 binlog 位置更可靠。

**第三,

更多推荐