1. 项目概述:为什么在Ubuntu 20.04上亲手部署LAMP不是“过时操作”,而是工程师的底层肌肉训练
LAMP——Linux、Apache、MySQL、PHP——这组缩写词像一把老式机械钥匙,插进服务器世界的锁孔里,咔哒一声,门就开了。它不是什么炫酷的新概念,但直到今天,在中小型Web服务、内部管理系统、教学实验环境甚至很多初创公司的MVP产品中,它依然是最稳、最透明、最可控的底座。你可能听过Docker一键拉起LAMP镜像、云平台点几下就生成环境,但真正让你看清请求怎么从浏览器穿过网络层、被Apache接收、交给PHP解析、再从MySQL取数据返回的全过程,只有亲手在Ubuntu 20.04这样的干净系统上,一行命令、一个配置、一次重启地搭一遍,才能建立那种“手指能感知到进程心跳”的直觉。
我带过不少刚转行的开发者,他们能熟练写Laravel路由、调用Composer包,但一问“Apache的MPM模块是什么?为什么Prefork和Event模式在PHP处理上表现天差地别?”就卡壳;一说“MySQL的innodb_buffer_pool_size设成多少合适?依据是物理内存还是可用内存?为什么不能直接填80%?”就去查文档。这些不是考题,是日常排查502错误、慢查询、内存溢出时,你脑子里最先闪过的判断锚点。而Ubuntu 20.04这个版本,恰好站在一个极佳的平衡点上:它已进入长期支持(LTS)周期中期,软件源稳定可靠,Apache 2.4.41、MySQL 8.0.33、PHP 7.4(官方源默认)或8.1(通过ondrej PPA可得)全部成熟可用,既避开了20.04刚发布时的零星bug,又没滑向22.04带来的某些兼容性断层。更重要的是,它不预装任何Web服务组件——没有隐藏的systemd单元、没有自动启动的MySQL实例、没有被修改过的Apache默认站点,你面对的就是一张白纸。这种“裸机感”,对建立清晰的技术栈认知图谱,价值远超省下的那五分钟安装时间。
所以,这不是一篇教你怎么“快速跑起来”的快餐教程,而是一份按真实运维节奏写的部署手记:从系统初始化检查开始,到每个服务的启动验证、端口监听确认、基础安全加固,再到PHP与MySQL的连通性实测,最后落脚在三个极易被忽略却高频踩坑的细节上——Apache的MPM模块切换、MySQL 8.0默认认证插件导致的PHP连接失败、以及PHP-FPM在非FPM模式下的静默失效。每一个步骤背后,我都告诉你“为什么必须做”、“不做会怎样”、“别人是怎么栽在这里的”。如果你的目标是能独立维护一台生产边缘的Web服务器,或者想把本地开发环境彻底掌控在自己手里,而不是依赖IDE自动弹出的“XAMPP已启动”提示框,那么接下来的内容,就是你该花时间细读的部分。
2. 整体设计思路与方案选型逻辑:为什么坚持APT+手动配置,而非Docker或Snap
在动手敲下第一条
apt update
之前,得先理清整个部署的骨架逻辑。很多人看到“LAMP安装”,第一反应是找一键脚本或Docker Compose文件。这没错,但对学习和深度掌控而言,它们是捷径,也是迷雾。我的方案选择非常明确:
完全基于Ubuntu 20.04官方软件源(主仓库+universe)和高度可信的第三方PPA(Ondřej Surý的PHP PPA),全程使用
apt
包管理器安装,所有配置文件手工编辑,服务状态逐项验证
。这个选择不是守旧,而是由三个硬性需求驱动的。
第一个需求是
可追溯性
。当你在生产环境遇到一个Apache子进程CPU飙升到90%,
ps aux | grep apache
看到一堆
/usr/sbin/apache2 -k start
进程,你得立刻知道:这是Prefork还是Event MPM在工作?它的
MaxRequestWorkers
设了多少?
ServerLimit
是否被突破?如果用Docker,你得先进容器、查
/etc/apache2/mods-enabled/
、再看
/etc/apache2/apache2.conf
里的
<IfModule mpm_event_module>
块;而原生安装,
apache2ctl -V | grep -i mpm
一条命令就给出答案,所有配置都在
/etc/apache2/
下,路径清晰,修改即生效,无需重建镜像。我试过用Docker部署LAMP做压力测试,当
ab -n 10000 -c 100 http://localhost/
跑出大量
apr_socket_recv: Connection reset by peer (104)
时,排查方向直接锁定在宿主机的
net.core.somaxconn
和容器内
ListenBacklog
的匹配关系上——这种跨层问题,在原生环境里根本不存在。
第二个需求是
资源确定性
。Ubuntu 20.04的
apache2
包默认启用
mpm_prefork
模块,这是为传统PHP-CGI模式设计的,每个请求独占一个进程,内存开销大但稳定性高;而
mpm_event
则更适合PHP-FPM,用少量线程处理大量并发连接。如果你用
apt install lamp-server^
这个元包,它会自动装
mpm_prefork
,但后续你要切到
mpm_event
,就得手动禁用prefork、启用event、调整
/etc/apache2/mods-available/mpm_event.conf
里的
ThreadsPerChild
和
MaxRequestWorkers
。这个过程看似多几步,但它强迫你理解Apache的多路复用模型。反观Docker镜像,很多LAMP镜像默认就配好了
mpm_event
+
php-fpm
,你
docker exec -it
进去看到一切正常,但一旦流量突增,
MaxRequestWorkers
被耗尽,错误日志里全是
server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
,而你却不知道这个值在哪改、改了要不要重启整个容器——因为镜像的启动脚本可能把配置固化了。
第三个需求是
安全基线可控性
。MySQL 8.0在Ubuntu 20.04源里默认使用
caching_sha2_password
认证插件,而PHP 7.4的
mysqlnd
驱动在未显式指定
auth_plugin=mysql_native_password
时,会尝试用新插件连接,结果就是
mysqli_connect(): The server requested authentication method unknown to the client [caching_sha2_password]
。这个问题在Docker里常被“解决”为直接在
docker-compose.yml
里加
command: --default-authentication-plugin=mysql_native_password
,但这等于绕过了MySQL 8.0的安全增强。我们的方案是:安装后立即执行
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_strong_password';
,并创建一个专用应用用户,明确指定认证插件。这个操作在原生环境里就是一条SQL,在Docker里却要侵入镜像构建层或覆盖启动命令,增加了维护复杂度。
所以,整个设计不是为了“复古”,而是为了
让每一层抽象都对你透明
。你不需要记住
docker run -d -p 8080:80 -v /myapp:/var/www/html php:apache
这条命令,而是要清楚知道
/var/www/html
目录的属主必须是
www-data
,
/etc/apache2/sites-available/000-default.conf
里的
DocumentRoot
指向哪,
/etc/mysql/mysql.conf.d/mysqld.cnf
里的
bind-address
为什么不能是
127.0.0.1
(当你要从外部访问时)。这种透明,是自动化工具无法替代的工程师基本功。
3. 核心细节解析与实操要点:从系统准备到服务验证的七道关卡
部署LAMP绝不是
apt install
四次就完事。它是一条精密的流水线,每个环节都有其不可跳过的校验点。我把整个过程拆解为七个关键关卡,每一道都对应一个必须亲手确认的状态,漏掉任何一环,后续都可能变成深夜救火的导火索。
3.1 关卡一:系统初始化与防火墙策略确认
Ubuntu 20.04默认启用
ufw
(Uncomplicated Firewall),但它的默认策略是“全拒绝入站”,这意味着即使Apache成功启动,外部机器也打不开
http://your-server-ip
。很多人卡在这一步,反复检查Apache配置,却忘了防火墙。正确的做法是:
# 检查ufw状态
sudo ufw status verbose
# 如果是inactive,先启用
sudo ufw enable
# 允许SSH(防止远程断连)
sudo ufw allow OpenSSH
# 允许HTTP和HTTPS
sudo ufw allow 'Apache Full'
# 再次确认状态,输出应显示"Apache Full"规则为"ALLOW IN"
sudo ufw status numbered
提示:
sudo ufw allow 'Apache Full'这条命令之所以安全,是因为它实际等价于sudo ufw allow 80/tcp && sudo ufw allow 443/tcp,只开放Web必需端口,而非sudo ufw allow 80这种不指定协议的模糊写法。后者在某些内核版本下可能意外放行UDP 80端口,带来潜在风险。
同时,必须确认系统时间准确。NTP服务在Ubuntu 20.04中由
systemd-timesyncd
提供,但默认可能未启用:
# 启用并启动NTP同步
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd
# 验证同步状态,"System clock synchronized: yes"是关键
timedatectl status
时间不同步会导致SSL证书验证失败、MySQL二进制日志时间戳错乱,甚至PHP的
date()
函数返回错误值。我曾遇到一个案例:客户服务器时间比标准时间快15分钟,导致其WordPress后台定时发布的文章全部提前15分钟上线,排查三天才发现是NTP没开。
3.2 关卡二:Apache安装与MPM模块精准切换
Ubuntu 20.04的
apache2
包默认安装
mpm_prefork
,这是安全的选择,但对PHP性能不友好。我们要切换到
mpm_event
,因为它能用更少的内存处理更多并发。但切换不是简单启停:
# 1. 禁用prefork,启用event
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
# 2. 必须启用rewrite模块,否则WordPress等CMS的伪静态会失效
sudo a2enmod rewrite
# 3. 启用headers模块,用于设置CSP、HSTS等安全头
sudo a2enmod headers
# 4. 重启Apache
sudo systemctl restart apache2
关键在于
mpm_event
的配置。编辑
/etc/apache2/mods-available/mpm_event.conf
:
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestWorkers 150
MaxConnectionsPerChild 0
</IfModule>
这里
MaxRequestWorkers
(原
MaxClients
)是核心参数,它决定了Apache能同时处理多少个请求。计算公式是:
MaxRequestWorkers = (总内存 - 系统预留内存) / 每个Apache进程平均内存
。假设你有2GB内存,系统预留512MB,每个Apache线程平均占10MB,则
MaxRequestWorkers ≈ (2048-512)/10 = 153
,我们取整为150。这个值必须小于
ServerLimit
(默认为16),否则Apache启动会报错。
ThreadsPerChild
设为25,意味着每个子进程创建25个线程,
StartServers
设为2,启动时就创建2个子进程,共50个线程待命。
注意:切勿盲目复制网上的“万能配置”。我见过有人把
MaxRequestWorkers设成1000,结果服务器内存瞬间被吃光,swap疯狂交换,top里apache2进程RSS列全是100MB+。一定要根据你的物理内存和应用实际内存占用来算。
3.3 关卡三:MySQL 8.0安装与认证插件强制降级
Ubuntu 20.04的
mysql-server
包安装的是MySQL 8.0,其默认认证插件
caching_sha2_password
与PHP 7.4/8.0的
mysqlnd
驱动存在兼容性问题。解决方案不是降级MySQL,而是为root用户和应用用户显式指定旧插件:
# 安装MySQL(会自动启动)
sudo apt install mysql-server
# 登录MySQL(首次安装root无密码,直接回车)
sudo mysql
# 在MySQL shell中执行:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'YourStrongRootPass123!';
FLUSH PRIVILEGES;
# 创建一个专用应用用户(强烈建议,永不使用root连接应用)
CREATE USER 'webapp'@'localhost' IDENTIFIED WITH mysql_native_password BY 'WebAppPass456!';
GRANT ALL PRIVILEGES ON *.* TO 'webapp'@'localhost';
FLUSH PRIVILEGES;
EXIT;
这里
IDENTIFIED WITH mysql_native_password
是强制指定插件的关键。如果不加这一句,MySQL 8.0创建的用户默认就是
caching_sha2_password
。
FLUSH PRIVILEGES;
是必须的,它重载权限表,让更改立即生效。很多教程漏掉这句,导致用户以为改了密码,实际权限没刷新,连接时依然报错。
3.4 关卡四:PHP安装与扩展精准启用
Ubuntu 20.04官方源的PHP是7.4,但很多新项目需要PHP 8.1。我们采用Ondřej Surý的PPA,这是Debian/Ubuntu社区公认的最可靠PHP源:
# 添加PPA
sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
# 安装PHP 8.1及常用扩展
sudo apt install php8.1 php8.1-cli php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-xmlrpc php8.1-zip
# 验证PHP版本和加载的扩展
php -v
php -m | grep -E "(mysql|curl|gd)"
注意
php8.1-mysql
这个扩展,它提供了
mysqli
和
pdo_mysql
两个驱动。很多新手只装
php-mysql
,结果在PHP代码里用
new mysqli()
时报
Class 'mysqli' not found
,就是因为没装对版本的扩展。
php8.1-cli
确保命令行PHP可用,
php8.1-curl
是API调用必备,
php8.1-gd
是图片处理基础。
3.5 关卡五:Apache与PHP的深度绑定验证
仅仅安装PHP还不够,必须让Apache知道如何处理
.php
文件。这需要两步:启用
php8.1
模块,并配置MIME类型:
# 启用PHP模块(Ubuntu 20.04会自动创建符号链接)
sudo a2enmod php8.1
# 编辑Apache默认站点配置
sudo nano /etc/apache2/sites-available/000-default.conf
在
<VirtualHost *:80>
块内,添加以下内容:
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
AllowOverride All
是关键,它允许
.htaccess
文件生效,WordPress的永久链接、CodeIgniter的URL重写都依赖它。
Require all granted
替代了旧版的
Order allow,deny
,是Apache 2.4的语法。
然后,创建一个测试文件:
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
sudo chown www-data:www-data /var/www/html/info.php
访问
http://your-server-ip/info.php
,页面顶部应显示“PHP Version 8.1.x”,且在“Loaded Modules”行能看到
core
,
mod_so
,
http_core
,
mpm_event
,
php8.1
等。如果看不到
php8.1
,说明模块没启用成功;如果页面显示PHP代码原文而非解析结果,说明MIME类型没配对,需检查
/etc/apache2/mods-enabled/php8.1.load
是否存在。
3.6 关卡六:MySQL-PHP连通性实测脚本
光有
phpinfo()
不够,必须实测PHP能否真正连接MySQL。创建一个
test_db.php
:
sudo nano /var/www/html/test_db.php
内容如下:
<?php
$host = 'localhost';
$user = 'webapp';
$pass = 'WebAppPass456!';
$db = 'testdb';
// 创建测试数据库
$conn = new mysqli($host, $user, $pass);
if ($conn->connect_error) {
die("MySQL连接失败: " . $conn->connect_error);
}
echo "MySQL连接成功!<br>";
// 创建数据库
if ($conn->query("CREATE DATABASE IF NOT EXISTS $db") === TRUE) {
echo "数据库 $db 创建成功<br>";
} else {
echo "创建数据库错误: " . $conn->error . "<br>";
}
// 选择数据库并创建表
$conn->select_db($db);
$sql = "CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100)
)";
if ($conn->query($sql) === TRUE) {
echo "表 users 创建成功<br>";
} else {
echo "创建表错误: " . $conn->error . "<br>";
}
// 插入测试数据
$sql = "INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com')";
if ($conn->query($sql) === TRUE) {
echo "测试数据插入成功<br>";
} else {
echo "插入数据错误: " . $conn->error . "<br>";
}
// 查询并显示
$result = $conn->query("SELECT * FROM users");
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo "ID: " . $row["id"]. " - 名字: " . $row["name"]. " - 邮箱: " . $row["email"]. "<br>";
}
} else {
echo "0 结果";
}
$conn->close();
?>
访问
http://your-server-ip/test_db.php
,应看到一连串“成功”提示和最终的查询结果。如果卡在“MySQL连接失败”,检查
/var/log/mysql/error.log
,常见原因是
webapp
用户没有
localhost
权限(需确认
CREATE USER 'webapp'@'localhost'
中的
localhost
拼写正确,不是
127.0.0.1
)。
3.7 关卡七:基础安全加固三板斧
LAMP跑通只是起点,安全加固是必选项:
-
Apache隐藏版本号 :编辑
/etc/apache2/conf-available/security.conf,找到ServerTokens和ServerSignature行,改为:ServerTokens Prod ServerSignature Off然后
sudo a2enconf security && sudo systemctl reload apache2。这会让响应头Server: Apache,而非Server: Apache/2.4.41 (Ubuntu),减少攻击面。 -
MySQL移除匿名用户 :登录MySQL后执行:
DELETE FROM mysql.user WHERE User=''; FLUSH PRIVILEGES; -
PHP禁用危险函数 :编辑
/etc/php/8.1/apache2/php.ini,找到disable_functions行,添加:disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source这些函数若被Web Shell利用,可直接执行系统命令。改完后
sudo systemctl restart apache2。
这三步做完,你的LAMP环境才真正具备了生产边缘部署的基本安全水位。
4. 实操过程与核心环节实现:从零开始的完整终端记录与参数详解
现在,让我们把前面所有逻辑,变成一份可逐行复制、粘贴、执行的完整终端操作记录。我会在每条命令后,用
# 注释
解释它的作用、背后的原理,以及如果执行失败,你该看哪里的日志。这不是脚本,而是你亲手操作时的实时笔记。
4.1 第一阶段:系统准备与更新(耗时约2分钟)
# 1. 更新软件包索引,确保获取最新版本信息
sudo apt update
# 注释:这步下载的是软件包列表(Packages.gz),不是安装包本身。如果卡住,检查网络或DNS(如nslookup archive.ubuntu.com)
# 2. 升级已安装的软件包到最新版本(关键!避免已知漏洞)
sudo apt upgrade -y
# 注释:-y参数自动确认,避免交互。升级后建议重启,尤其涉及内核更新时。检查是否需要重启:`[ -f /var/run/reboot-required ] && echo "需重启" || echo "无需重启"`
# 3. 安装基础工具(vim是编辑器,curl是调试利器,unzip解压常用)
sudo apt install -y vim curl unzip
# 注释:vim比nano功能强得多,尤其在编辑长配置文件时。curl用于测试HTTP响应,如`curl -I http://localhost`看HTTP头
# 4. 检查并启用UFW防火墙(如前所述)
sudo ufw status verbose | grep -q "Status: inactive" && sudo ufw enable
sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
# 注释:这两条`allow`命令会自动在`/etc/ufw/applications.d/`里注册规则。`ufw status numbered`可查看规则编号,便于删除
4.2 第二阶段:Apache安装与MPM配置(耗时约3分钟)
# 1. 安装Apache2
sudo apt install -y apache2
# 注释:安装后Apache自动启动。验证:`sudo systemctl is-active apache2`应返回`active`;`sudo ss -tlnp | grep :80`应看到`apache2`进程监听80端口
# 2. 切换MPM模块
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo a2enmod rewrite
sudo a2enmod headers
# 注释:`a2dismod/a2enmod`本质是创建/删除`/etc/apache2/mods-enabled/`下的符号链接。`ls -l /etc/apache2/mods-enabled/`可确认
# 3. 编辑MPM Event配置
sudo nano /etc/apache2/mods-available/mpm_event.conf
# 注释:将文件内容替换为前文给出的配置块。重点是`MaxRequestWorkers 150`和`ThreadsPerChild 25`。保存退出
# 4. 重启Apache并验证MPM
sudo systemctl restart apache2
apache2ctl -V | grep -i mpm
# 注释:输出应为`Server MPM: event`。如果还是`prefork`,说明`a2dismod mpm_prefork`没成功,检查`/etc/apache2/mods-enabled/`里是否还有`mpm_prefork.load`
# 5. 测试默认页面
curl -s http://localhost | head -20
# 注释:应看到HTML源码,包含`<title>Apache2 Ubuntu Default Page</title>`。这是Apache工作的铁证
4.3 第三阶段:MySQL 8.0安装与用户配置(耗时约4分钟)
# 1. 安装MySQL服务器
sudo apt install -y mysql-server
# 注释:安装过程会自动生成root密码(存储在`/etc/mysql/debian.cnf`),但Ubuntu 20.04默认允许`sudo mysql`无密码登录,这是安全设计
# 2. 登录MySQL并执行安全配置
sudo mysql << 'EOF'
-- 强制root使用旧认证插件
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'RootPass2024!';
-- 创建应用用户
CREATE USER 'lampuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'LampUserPass2024!';
-- 授予所有数据库权限(生产环境请缩小范围)
GRANT ALL PRIVILEGES ON *.* TO 'lampuser'@'localhost';
-- 刷新权限
FLUSH PRIVILEGES;
-- 删除匿名用户(安全加固)
DELETE FROM mysql.user WHERE User='';
FLUSH PRIVILEGES;
EOF
# 注释:这里用`<< 'EOF'`语法执行多行SQL,避免交互。单引号内的EOF表示不展开变量,安全
# 3. 验证MySQL连接
mysql -u lampuser -p'LampUserPass2024!' -e "SELECT VERSION();"
# 注释:应输出MySQL版本号,如`8.0.33-0ubuntu0.20.04.2`。如果报错`Access denied`,检查用户名密码是否拼写正确,或`'lampuser'@'localhost'`中的`localhost`是否误写为`127.0.0.1`
4.4 第四阶段:PHP 8.1安装与扩展启用(耗时约3分钟)
# 1. 添加Ondřej Surý的PHP PPA
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
# 注释:PPA地址`ppa:ondrej/php`是社区维护的,更新及时,比官方源的PHP版本新。`add-apt-repository`会自动创建`/etc/apt/sources.list.d/ondrej-ubuntu-php-focal.list`
# 2. 安装PHP 8.1及核心扩展
sudo apt install -y php8.1 php8.1-cli php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-xmlrpc php8.1-zip
# 注释:`php8.1-mysql`是关键,它提供`mysqli`和`pdo_mysql`。`php8.1-cli`确保`php -v`命令可用
# 3. 验证PHP安装
php -v
# 注释:输出应为`PHP 8.1.x (cli) ...`。如果还是7.4,说明没装对版本,检查`apt list --installed | grep php`
# 4. 启用PHP模块给Apache
sudo a2enmod php8.1
# 注释:这会在`/etc/apache2/mods-enabled/`下创建`php8.1.load`和`php8.1.conf`链接
# 5. 重启Apache使PHP生效
sudo systemctl restart apache2
4.5 第五阶段:Apache+PHP+MySQL端到端测试(耗时约2分钟)
# 1. 创建PHP信息页
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/phpinfo.php
sudo chown www-data:www-data /var/www/html/phpinfo.php
# 注释:`chown`确保Apache进程(运行在`www-data`用户下)有权限读取该文件。否则会报403 Forbidden
# 2. 创建数据库连通性测试页
sudo tee /var/www/html/dbtest.php << 'EOF'
<?php
$host = 'localhost';
$user = 'lampuser';
$pass = 'LampUserPass2024!';
try {
$pdo = new PDO("mysql:host=$host;dbname=mysql", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "✅ PDO连接MySQL成功!<br>";
// 查询MySQL版本
$stmt = $pdo->query("SELECT VERSION() as ver");
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo "MySQL版本: " . $row['ver'] . "<br>";
// 创建测试库
$pdo->exec("CREATE DATABASE IF NOT EXISTS test_lamp");
echo "✅ 测试库 test_lamp 创建成功!<br>";
} catch(PDOException $e) {
echo "❌ 连接失败: " . $e->getMessage();
}
?>
EOF
sudo chown www-data:www-data /var/www/html/dbtest.php
# 3. 访问测试页(在本地浏览器或用curl)
curl -s http://localhost/dbtest.php | grep "✅"
# 注释:应看到三行"✅"开头的成功信息。如果看到"❌",检查`/var/log/apache2/error.log`,里面会有详细的PHP错误堆栈
4.6 第六阶段:安全加固与最终验证(耗时约3分钟)
# 1. 隐藏Apache版本号
echo "ServerTokens Prod
ServerSignature Off" | sudo tee -a /etc/apache2/conf-available/security.conf
sudo a2enconf security
sudo systemctl reload apache2
# 注释:`reload`比`restart`更轻量,只重载配置,不中断现有连接
# 2. 禁用PHP危险函数
sudo sed -i 's/;disable_functions =.*/disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source/' /etc/php/8.1/apache2/php.ini
sudo systemctl restart apache2
# 注释:`sed -i`直接修改文件。改完必须重启Apache,因为PHP模块是在Apache启动时加载的
# 3. 最终综合验证:用一个命令检查所有服务状态
echo "=== 服务状态汇总 ==="
sudo systemctl is-active apache2 && echo "✅ Apache: active" || echo "❌ Apache: inactive"
sudo systemctl is-active mysql && echo "✅ MySQL: active" || echo "❌ MySQL: inactive"
php -v | head -1 && echo "✅ PHP: version OK" || echo "❌ PHP: not found"
curl -s http://localhost/phpinfo.php | grep -q "PHP Version" && echo "✅ PHP page renders" || echo "❌ PHP page broken"
mysql -u lampuser -p'LampUserPass2024!' -e "SELECT 1;" >/dev/null 2>&1 && echo "✅ MySQL CLI works" || echo "❌ MySQL CLI broken"
# 注释:这个汇总脚本会一次性告诉你所有关键点是否OK。任何一项失败,就回到对应章节排查
执行完以上所有命令,你的Ubuntu 20.04服务器上,一个安全、稳定、可验证的LAMP环境就正式交付了。整个过程耗时约15-20分钟,但你获得的,是比任何一键脚本都更扎实的掌控力。
5. 常见问题与排查技巧实录:那些让我熬夜到凌晨三点的坑
在部署LAMP的十年里,我遇到过无数“理论上应该没问题,实际上就是跑不通”的诡异问题。下面列出的,不是教科书式的FAQ,而是我在真实客户现场、个人服务器、甚至朋友电脑上,亲手解决并记录下来的“血泪清单”。每一个问题后面,都附上了我当时用的、最直接有效的排查命令和修复步骤。
5.1 问题一:Apache启动失败,日志里只有
AH00526: Syntax error on line X of /etc/apache2/apache2.conf
这是最经典的“配置语法错误”。原因千奇百怪,但排查路径极其固定:
排查步骤:
-
用
apache2ctl configtest命令进行语法检查:sudo apache2ctl configtest # 输出示例:`Syntax OK` 或 `AH00526: Syntax error on line 123 of /etc/apache2/mods-enabled/php8.1.conf: Invalid command 'php_value', perhaps misspelled or defined by a module not included in the server configuration` -
如果报错指向某个
mods-enabled下的文件,比如php8.1.conf,那就去/etc/apache2/mods-available/里看原始文件,检查是否有拼写错误(如php_value写成php_vlaue)。 -
更隐蔽的情况是,你启用了某个模块,但该模块依赖的另一个模块没启用。例如,
php8.1模块依赖mime模块,如果a2dismod mime了,a2enmod php8.1就会失败。此时apache2ctl -M列出所有已启用模块,看mime是否在其中。
我的实战经验:
有一次,客户服务器报这个错,
configtest
指向
/etc/apache2/sites-enabled/000-default.conf
第45行。我打开一看,是
<Directory /var/www/html>
块里多了一个没闭合的
<Files>
标签。这种低级错误,
vim
的语法高亮都没发现,因为它是嵌套层级问题。后来我养成了一个习惯:每次修改配置,先
sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf.bak.$(date +%s)
备份,再修改,改完立刻
configtest
,绝不贪图省事。
5.2 问题二:PHP页面能打开,但
mysqli_connect()
始终返回
Connection refused
表面看是PHP连不上MySQL,但根源往往在MySQL的监听配置上。Ubuntu 20.04的MySQL 8.0默认
bind-address
是
127.0.0.1
,这没问题;但如果
skip-networking
被意外开启,MySQL就完全不监听TCP端口了。
排查步骤:
-
检查MySQL是否在监听3306端口:
sudo ss -tlnp | grep :3306 # 正常输出:`LISTEN 0 70 *:3306 *:* users:(("mysqld",pid=1234,fd=33))` # 如果没输出,说明MySQL没监听TCP -
检查MySQL配置:
sudo grep -E "^(bind-address|skip-networking)" /etc/mysql/mysql.conf.d/mysqld.cnf # 正常应输出:`
2万+

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



