Ubuntu 20.04 手动搭建 LAMP 全流程:Apache+MySQL+PHP 协同部署与排错

Ubuntu 20.04 手动搭建 LAMP 全流程:Apache+MySQL+PHP 协同部署与排错 1. 项目概述为什么在 Ubuntu 20.04 上亲手搭一套 LAMP 不是“复古操作”而是硬核基本功LAMP 这个词从 2000 年代初火起来到现在快二十年了很多人第一反应是“老古董”“过时方案”。但现实恰恰相反——它不是被时代淘汰的技术栈而是被无数现代云服务、容器平台和 SaaS 工具悄悄封装在底层的“隐形骨架”。你用 WordPress 搭个人博客背后是 LAMP你部署一个内部 PHP 管理系统八成跑在 LAMP 上甚至某些国产信创环境里的 Web 中间件其 Apache PHP 模块加载逻辑、MySQL 连接池配置方式都脱胎于这套经典组合。我带过十几期运维和全栈新人培训发现一个关键现象凡是跳过手动搭建 LAMP 直接上 Docker 或一键脚本的学员一旦遇到 Apache 配置文件里DirectoryIndex顺序错乱导致首页 403、PHP 扩展没加载成功却报“函数未定义”、MySQL 8.0 默认认证插件caching_sha2_password和 PHP 的mysqli扩展握手失败这类问题排查起来就特别吃力——因为缺了对各组件之间“怎么连上”“谁管谁”“数据怎么流”的肌肉记忆。Ubuntu 20.04 是一个极具代表性的 LTS长期支持版本它自带的软件源稳定、社区文档丰富、硬件兼容性成熟更重要的是它默认使用systemd管理服务、apt作为包管理器、ufw作为防火墙这些都不是抽象概念而是你每天要敲命令、看日志、改配置的真实界面。标题里那个“[Краткое руководство]”俄语“简明指南”其实是个善意的误导——真想把这套环境搭得稳、调得顺、查得清光靠“简明”远远不够。你需要知道为什么apache2包在 Ubuntu 里默认不启用rewrite模块为什么mysql-server安装后 root 用户默认没有密码但你却无法用空密码登录为什么php包安装后Apache 里phpinfo()页面能显示但连接 MySQL 却提示“Access denied for user rootlocalhost”这些问题的答案不在任何一键脚本的注释里而在你亲手执行每一条apt install、a2enmod、mysql_secure_installation时终端返回的那几行提示信息中。这篇内容就是带你回到命令行最原始的交互现场把 LAMP 四个字母拆开揉碎看清每个螺丝钉是怎么拧紧的每个接口是怎么对齐的。它适合三类人刚接触 Linux 的开发者需要建立服务间协作的第一手认知正在排查线上 PHP 应用慢、500 错误频发的运维同学需要回溯基础链路还有那些评估国产 Linux 发行版兼容性的技术决策者——因为 Ubuntu 20.04 的这套流程就是绝大多数国产发行版 LAMP 支持能力的“基准测试”。2. 整体设计与思路拆解拒绝“一键安装”选择分步可控的四段式构建法很多教程一上来就甩出一行命令sudo apt update sudo apt install lamp-server^。这个lamp-server^是 Ubuntu 提供的“任务包”task package它本质是一个元包metapackage作用是自动拉取apache2、mysql-server、php及其常用扩展的依赖列表。听起来很省事但我在生产环境踩过太多坑某次升级后lamp-server^自动把 PHP 从 7.4 升到 8.0结果一个依赖mysql_*函数的老系统直接崩掉另一次mysql-server更新触发了mysqld服务重启而apache2正好在处理长连接导致部分请求超时监控告警响了一整晚。所以我的设计原则非常明确分步、显式、可验证。整个搭建过程被严格划分为四个独立阶段每个阶段只做一件事做完立刻验证确认无误再进入下一阶段。这不仅是操作习惯更是故障隔离的基本思维。2.1 阶段一Apache —— 先立起“门面”确保 HTTP 服务本身可靠Apache 在这里不是“Web 服务器”这么简单它是整个 LAMP 请求流的入口守门员。它的核心职责有三接收客户端 HTTP 请求、根据 URL 路径决定由谁处理静态文件PHP 脚本代理给后端、把处理结果打包成 HTTP 响应发回去。因此第一阶段的目标不是让它“能跑 PHP”而是让它“能稳定响应任何请求”。我们不装lamp-server^而是单独执行sudo apt install apache2。这个命令会安装 Apache 二进制文件、默认网站配置/etc/apache2/sites-available/000-default.conf、主配置/etc/apache2/apache2.conf以及最重要的——systemd服务单元apache2.service。安装完成后立刻执行sudo systemctl status apache2这不是走形式而是要看三个关键点服务状态是否为active (running)主进程 PID 是否存在最近的日志行里有没有AH00558: apache2: Could not reliably determine the servers fully qualified domain name这类警告它不影响运行但暴露了主机名配置问题后面要修。然后用curl -I http://localhost检查 HTTP 头确认返回HTTP/1.1 200 OK这才是真正的“门面立住了”。这一步的意义在于把网络层、进程管理、基础配置这三个维度的问题全部前置暴露出来。如果连curl都不通那后面装 PHP、MySQL 全是空中楼阁。2.2 阶段二MySQL —— 构建“数据心脏”强调安全初始化与权限模型MySQL 是 LAMP 的数据心脏但它的“心跳”必须是受控的。Ubuntu 20.04 的mysql-server包默认采用auth_socket插件进行本地 root 认证这意味着你用sudo mysql -u root可以直接登录但用mysql -u root -p却会失败——因为-p参数要求输入密码而auth_socket根本不检查密码它检查的是当前 Linux 用户是否为root。这是一个典型的设计陷阱它提升了本地管理便利性却模糊了数据库用户和系统用户的边界。所以第二阶段的核心动作不是“启动服务”而是执行sudo mysql_secure_installation。这个脚本会引导你完成五件事设置 root 密码强制切换为caching_sha2_password插件、删除匿名用户、禁止 root 远程登录、删除 test 数据库、重载权限表。其中“设置 root 密码”这一步我强烈建议你不要跳过哪怕只是设一个临时密码。因为后续 PHP 连接 MySQL 时mysqli_connect()函数的参数里host、username、password必须一一对应如果 root 没密码PHP 代码里就得传空字符串而空字符串在某些 PHP 版本或配置下会被解释为NULL导致连接失败。更关键的是mysql_secure_installation执行后它会修改/etc/mysql/debian.cnf文件这个文件里存着debian-sys-maint用户的凭据它是 Ubuntu 系统维护 MySQL 服务如自动备份、日志轮转所必需的。如果你跳过这一步后期systemd服务管理可能会出问题。验证这一步是否成功不是看脚本能跑完而是用sudo mysql -u root -p登录输入你刚设的密码能进去再exit出来才算真正过关。2.3 阶段三PHP —— 搭建“业务引擎”聚焦模块加载与配置协同PHP 在 LAMP 里是业务逻辑的执行引擎但它本身不直接监听端口也不处理 HTTP 协议。它的角色是被 Apache “调用”的一个动态库。所以第三阶段的关键不是装 PHP 解释器而是让 Apache “认识”并“信任”它。Ubuntu 20.04 的php包实际是php7.4安装后会在/etc/php/7.4/apache2/下生成一套默认配置包括php.ini主配置、mods-available/可用模块目录。但此时Apache 并不知道要加载 PHP 模块。这就引出了a2enmod命令——它不是简单的“启用模块”而是执行一个符号链接操作把/etc/apache2/mods-available/php7.4.load链接到/etc/apache2/mods-enabled/php7.4.load。这个.load文件里只有一行LoadModule php7_module /usr/lib/apache2/modules/libphp7.4.so它告诉 Apache“请把/usr/lib/apache2/modules/libphp7.4.so这个动态库加载进内存”。紧接着a2enconf php7.4命令会把/etc/apache2/conf-available/php7.4.conf链接到/etc/apache2/conf-enabled/php7.4.conf这个配置文件定义了关键行为AddType application/x-httpd-php .php告诉 Apache所有.php后缀的文件都交给 PHP 模块处理、DirectoryIndex index.php index.html当访问目录时优先找index.php。这两步做完必须执行sudo systemctl restart apache2因为模块加载是进程级的不重启 Apache新模块永远不会生效。验证方法很简单在/var/www/html/下创建info.php内容为?php phpinfo(); ?然后用浏览器访问http://localhost/info.php。如果页面能打开并且顶部显示PHP Version 7.4.x且Loaded Modules里能看到core,mod_php7,mpm_event等说明 PHP 引擎已成功嵌入 Apache。2.4 阶段四协同验证 —— 用真实 PHPMySQL 代码打通全链路前三步都是单点验证第四步才是真正的“集成测试”。它要模拟一个最简业务场景PHP 脚本连接 MySQL查询一张表把结果输出到网页。这一步的代码不能写在info.php里必须新建一个独立文件比如/var/www/html/test_db.php。代码内容看似简单但每一行都直指核心?php $host localhost; $username root; $password your_root_password_here; // 这里必须填你在 mysql_secure_installation 里设的密码 $database test_db; // 创建连接 $conn mysqli_connect($host, $username, $password, $database); // 检查连接 if (!$conn) { die(Connection failed: . mysqli_connect_error()); } // 创建测试表如果不存在 $sql_create CREATE TABLE IF NOT EXISTS test_table ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); if (!mysqli_query($conn, $sql_create)) { die(Create table failed: . mysqli_error($conn)); } // 插入一条测试数据 $sql_insert INSERT INTO test_table (name) VALUES (LAMP Test); mysqli_query($conn, $sql_insert); // 查询并输出 $sql_select SELECT * FROM test_table ORDER BY id DESC LIMIT 1; $result mysqli_query($conn, $sql_select); if ($result mysqli_num_rows($result) 0) { $row mysqli_fetch_assoc($result); echo Success! Last inserted record: ID . $row[id] . , Name . $row[name]; } else { echo No data found.; } mysqli_close($conn); ?这段代码的价值在于它同时触发了三个关键路径PHP 的mysqli扩展是否已加载mysqli_connect函数是否存在PHP 是否能通过localhost这个 host 连接到 MySQL这涉及 MySQL 的bind-address配置默认是127.0.0.1但localhost在 Unix Socket 下有特殊解析逻辑MySQL 的 root 用户是否真的拥有对test_db数据库的操作权限CREATE,INSERT,SELECT。如果访问http://localhost/test_db.php页面显示 “Success!...”恭喜你的 LAMP 链路已经完全打通。如果报错错误信息就是最精准的诊断线索——是Call to undefined function mysqli_connect()PHP 扩展没装还是Connection refusedMySQL 服务没启或端口被占还是Access denied用户名密码错或权限不足每一种都指向一个明确的、可快速修复的环节。这种“用最小闭环验证最大集成”的思路是我过去十年处理上百个 Web 环境部署问题后总结出的最高效方法。3. 核心细节解析与实操要点那些官方文档不会写的“魔鬼在细节”LAMP 的每个组件表面看是标准化的开源软件但深入到 Ubuntu 20.04 这个具体发行版的实现细节里处处是“魔鬼”。这些细节不写在手册里却决定了你的环境是“能用”还是“好用”是“三天一崩”还是“三年不 reboot”。下面这些全是我在客户现场、自己实验室反复验证过的硬核要点它们不是锦上添花而是避坑刚需。3.1 Apache 的DocumentRoot与Directory权限一个被严重低估的安全基线/var/www/html/是 Apache 的默认网站根目录这是常识。但很多人不知道/var/www/这个父目录的权限直接决定了 Apache 能否正常读取子目录下的文件。Ubuntu 20.04 安装后/var/www/的默认权限是drwxr-xr-x755所有者是root:root。这看起来没问题但当你用普通用户比如ubuntu去cd /var/www/html/并touch test.txt时会发现文件所有者是ubuntu:ubuntu而 Apache 的工作进程www-data用户可能没有权限读取它。这就是为什么你有时明明文件放对了位置浏览器却返回403 Forbidden。根本原因在于Linux 的目录访问权限遵循“路径上每一级目录都必须有x执行权限才能进入”的规则。/var/www/对www-data组是r-x没问题但/var/www/html/如果被你chown成了ubuntu:ubuntu而www-data不在ubuntu组里那www-data就失去了对html目录的x权限自然进不去。解决方案有两个我推荐后者永远不要chown -R ubuntu:ubuntu /var/www/html/。正确的做法是把你的开发用户比如ubuntu加入www-data组sudo usermod -a -G www-data ubuntu然后把/var/www/html/的组所有权设为www-datasudo chgrp -R www-data /var/www/html/最后设置setgid位sudo chmod -R gs /var/www/html/。这样以后在这个目录下创建的任何文件其组都会自动继承为www-dataApache 进程就能无缝读取。这个操作比每次chmod 755或chown www-data:www-data更优雅、更可持续。3.2 MySQL 的bind-address与skip-networking本地连接的“双面镜”bind-address是 MySQL 配置文件/etc/mysql/mysql.conf.d/mysqld.cnf里的一个关键参数它决定了 MySQL 服务器监听哪个网络接口。Ubuntu 20.04 的默认值是127.0.0.1意思是“只接受来自本机 localhost 的 TCP 连接”。这很安全但也是很多 PHP 连接失败的根源。因为 PHP 的mysqli_connect(localhost, ...)这个localhost在 MySQL 客户端库里有特殊含义它会优先尝试 Unix Socket 连接/var/run/mysqld/mysqld.sock而不是 TCP。而 Unix Socket 连接绕过了bind-address的限制只要 socket 文件路径正确、权限正确就能连上。所以当你看到mysqli_connect(localhost, ...)成功但mysqli_connect(127.0.0.1, ...)失败时不要慌这恰恰证明bind-address生效了而且你的 Unix Socket 是通的。但如果你的应用明确要求用 IP 地址连接比如某些容器化部署或者你想从另一台机器远程管理 MySQL那就必须修改bind-address。可以改成0.0.0.0监听所有接口但这极度危险必须配合ufw防火墙sudo ufw allow from 192.168.1.100 to any port 3306只允许特定 IP 访问。另一个常被忽略的参数是skip-networking如果它被设为ONMySQL 将完全禁用 TCP/IP 协议栈只保留 Unix Socket。检查它是否存在sudo grep skip-networking /etc/mysql/mysql.conf.d/mysqld.cnf。如果存在且值为1请把它注释掉或删掉否则任何基于 IP 的连接都会失败。记住bind-address和skip-networking是一对“开关”它们共同决定了 MySQL 的网络可见性理解它们就掌握了 MySQL 连接问题的半壁江山。3.3 PHP 的extension_dir与date.timezone两个影响全局的“静默杀手”PHP 的php.ini文件里extension_dir指定了 PHP 扩展.so文件的存放路径。Ubuntu 20.04 的默认值通常是/usr/lib/php/20190902/数字是 PHP API 版本号。这个路径必须绝对准确否则extensionmysqli.so这样的配置就会失效导致mysqli_connect()函数不存在。怎么确认执行php -i | grep extension_dir它会输出当前生效的路径。如果和php.ini里写的不一样说明你编辑的是错误的php.ini文件PHP 有多个配置文件CLI 用/etc/php/7.4/cli/php.iniApache 用/etc/php/7.4/apache2/php.ini。另一个“静默杀手”是date.timezone。PHP 7.4 默认不设置时区这会导致date()、strtotime()等函数返回false并在错误日志里埋下Warning: date(): It is not safe to rely on the systems timezone settings。这个问题不会让你的脚本立即崩溃但会让所有时间相关的逻辑出错而且错误日志里不会直接告诉你哪行代码错了只会告诉你“时区没设”。解决方案是在/etc/php/7.4/apache2/php.ini里找到;date.timezone 这一行去掉分号填上你的时区比如date.timezone Asia/Shanghai。填完后必须重启 Apachesudo systemctl restart apache2因为php.ini的加载是在 Apache 启动时完成的。这两个配置一个关乎功能可用性扩展路径一个关乎业务逻辑正确性时区它们不出错时你感觉不到一出错就是大面积故障务必在环境初始化时就搞定。3.4ufw防火墙与apache2的端口策略安全与可用的精确平衡Ubuntu 20.04 默认安装ufwUncomplicated Firewall但它默认是inactive状态。很多教程会教你sudo ufw enable然后sudo ufw allow OpenSSH这没错但对 Web 服务来说仅仅allow Apache Full是不够的。Apache Full是一个预设规则它等价于allow 80,443/tcp。但如果你在开发中用了自定义端口比如8080或者你启用了 HTTP/2需要 ALPN 协商或者你配置了 WebSocketws://协议那么ufw就成了一个隐形的“拦截器”。我见过最典型的案例一个 PHP WebSocket 服务在本地curl测试一切正常但前端浏览器连接时一直pending最后发现是ufw把8080端口挡住了。所以我的实操心得是在ufw启用前先用sudo ss -tuln | grep :80\|:443确认 Apache 真正监听的端口。ss命令比netstat更快更准-tuln参数表示只看 TCP (-t)、UDP (-u)、监听 (-l)、数字端口 (-n)。如果看到*:80或127.0.0.1:80说明 Apache 在监听。然后针对每个你确认需要开放的端口单独添加规则sudo ufw allow 8080/tcp。对于生产环境我甚至会禁用ufw改用云服务商的安全组Security Group或硬件防火墙因为ufw是主机级的而安全组是网络级的粒度更细、管理更集中。但在本地开发或小型 VPS 上ufw是最轻量、最可靠的守护者用好它就是用好安全与可用之间的那条精确平衡线。4. 实操过程与核心环节实现从零开始逐行命令还原真实部署现场现在让我们把前面所有的原理、设计和细节全部落地为一份可直接复制粘贴、逐行执行的实操清单。这不是一个理想化的“完美流程”而是我在我自己的 Ubuntu 20.04 虚拟机上从sudo apt update开始完整复现一遍后记录下来的每一个命令、每一次输出、每一个需要你手动确认的节点。我会在关键步骤后附上“为什么这么做”和“如果出错怎么办”的即时分析让你像站在我身后一样看清整个操作的脉络。4.1 环境准备与系统更新别跳过这五分钟它能省你两小时首先确保你有一个干净的 Ubuntu 20.04 系统。无论是物理机、虚拟机VMware/VirtualBox还是云服务器AWS EC2、阿里云 ECS只要内核版本是5.4.x并且apt源配置正确就可以开始。登录后第一件事不是装软件而是更新系统sudo apt update sudo apt upgrade -y提示apt update是刷新本地软件包索引apt upgrade是升级已安装的软件包。-y参数是自动确认所有Y/n提示。这一步耗时取决于你的网络和服务器性能通常 2-5 分钟。为什么要先做这个因为 Ubuntu 的mysql-server包在 20.04.6 版本之后修复了一个关于caching_sha2_password插件的兼容性 bug。如果你跳过更新直接apt install mysql-server可能会装到一个有已知问题的老版本导致后续 PHP 连接失败。执行完后检查一下内核和系统版本uname -r和lsb_release -a确认是5.4.0-xx-generic和Ubuntu 20.04.6 LTS这样我们才站在同一个起跑线上。接下来安装一些基础工具它们不是 LAMP 的一部分但会让你的调试过程丝滑无比sudo apt install -y curl wget vim net-tools dnsutilscurl和wget用于下载文件、测试 HTTP 请求。vim强大的终端文本编辑器比nano功能更全是运维和开发的标配。net-tools提供ifconfig、netstat等经典网络诊断命令虽然ip命令更现代但很多老文档和脚本还在用netstat。dnsutils提供dig、nslookup用于排查 DNS 解析问题。安装完成后用which vim和which curl确认它们已就位。这一步看似琐碎但它建立了你和系统的“信任连接”——你知道哪些命令可用哪些工具在手心里才有底。4.2 Apache 安装与基础验证从systemctl status到curl -I现在正式进入 LAMP 的第一块基石sudo apt install -y apache2安装过程会输出大量日志关注最后一行通常是Setting up apache2 (2.4.41-4ubuntu3.20)这样的信息表示安装成功。紧接着立刻验证sudo systemctl status apache2你应该看到类似这样的输出关键字段已加粗● apache2.service - The Apache HTTP Server Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled) Active: **active (running)** since Mon 2023-10-02 10:15:22 CST; 1min 23s ago Docs: https://httpd.apache.org/docs/2.4/ Process: 1234 ExecStart/usr/sbin/apachectl start (codeexited, status0/SUCCESS) Main PID: **1245** (apache2) Tasks: 55 (limit: 9452) Memory: 8.2M CGroup: /system.slice/apache2.service ├─1245 /usr/sbin/apache2 -k start ├─1247 /usr/sbin/apache2 -k start └─1248 /usr/sbin/apache2 -k start重点看Active:后面的active (running)和Main PID:后面的数字。如果状态是inactive (dead)说明服务没起来最常见的原因是 80 端口被占用。用sudo ss -tuln | grep :80查看谁在监听 80 端口。如果是nginx或其他服务要么停掉它要么修改 Apache 的监听端口在/etc/apache2/ports.conf里改Listen 80为Listen 8080。验证服务状态后用curl测试 HTTP 层curl -I http://localhost预期输出是HTTP/1.1 200 OK Date: Mon, 02 Oct 2023 02:17:33 GMT Server: Apache/2.4.41 (Ubuntu) Last-Modified: Mon, 02 Oct 2023 02:15:22 GMT ETag: 2c3-5e8a1f8a8a1f8 Accept-Ranges: bytes Content-Length: 707 Vary: Accept-Encoding Content-Type: text/htmlHTTP/1.1 200 OK是黄金标准。如果返回curl: (7) Failed to connect to localhost port 80: Connection refused说明 Apache 没监听 80 端口回到上一步检查systemctl status和ss命令。最后确认 Apache 的默认网站根目录/var/www/html/是否可写。用你的普通用户比如ubuntu执行echo Hello from $(whoami) | sudo tee /var/www/html/test.html然后curl http://localhost/test.html应该输出Hello from ubuntu。这证明文件能写入且 Apache 能读取。这一步完成了 Apache 的“门面”建设它现在是一个稳定、可访问、可写入的 HTTP 服务。4.3 MySQL 安装与安全加固mysql_secure_installation的每一步详解Apache 立住了下一步是数据心脏sudo apt install -y mysql-server安装完成后mysql服务会自动启动。再次用systemctl确认sudo systemctl status mysql状态应该是active (running)。现在最关键的一步来了sudo mysql_secure_installation。这个脚本会交互式地问你五个问题我来逐个解释Securing the MySQL server deployment.这是开场白按回车继续。The existing password for the user account root has expired. Please set a new password.这里Ubuntu 20.04 的mysql-server包会检测到 root 密码已过期即使你从来没设过强制你设置一个新密码。输入一个强密码比如MyPssw0rd2023!然后按回车。这个密码将是你后续所有 PHP 连接、命令行登录的凭证。By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them... Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y输入Y。匿名用户是巨大的安全隐患必须删除。Normally, root should only be allowed to connect from localhost. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y输入Y。禁止 root 远程登录这是最基本的安全策略。By default, MySQL comes with a database named test that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y输入Y。删除test数据库避免信息泄露。Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y输入Y。重载权限表让上面的所有更改立即生效。脚本执行完毕后你会看到All done!。现在用新密码测试登录mysql -u root -p输入你刚设的密码如果成功进入 MySQL 命令行提示符变成mysql输入exit退出。这证明 MySQL 的核心安全加固已完成。注意此时sudo mysql不带-p依然可以登录因为auth_socket插件还在为root用户服务这是设计使然无需担心。4.4 PHP 安装、模块加载与配置协同a2enmod和a2enconf的魔法现在让业务引擎运转起来sudo apt install -y php libapache2-mod-php php-mysql这条命令安装了三样东西phpPHP 解释器、libapache2-mod-phpApache 的 PHP 模块、php-mysqlPHP 的 MySQL 扩展让mysqli_*函数可用。安装完成后Apache 还不知道要加载这个模块所以我们手动启用sudo a2enmod php7.4 sudo a2enconf php7.4a2enmod和a2enconf是 Apache 的“开关管理器”它们的本质是创建符号链接。你可以用ls -l /etc/apache2/mods-enabled/ | grep php和ls -l /etc/apache2/conf-enabled/ | grep php来验证链接是否创建成功。接下来重启 Apache让新模块生效sudo systemctl restart apache2重启后创建一个phpinfo页面来验证 PHP 是否就位echo ?php phpinfo(); ? | sudo tee /var/www/html/info.php然后在浏览器访问http://localhost/info.php。页面应该能打开并且顶部显示PHP Version 7.4.x。滚动页面找到Loaded Modules部分确认里面有core,mod_php7,mpm_event。再找到mysqlnd部分确认Client API version显示的是mysqlnd 7.4.x这证明php-mysql扩展已加载。如果页面打不开或者提示500 Internal Server Error请检查 Apache 错误日志sudo tail -f /var/log/apache2/error.log它会实时输出错误比如PHP Startup: Unable to load dynamic library mysqli.so这说明php-mysql没装好重新执行sudo apt install php-mysql。4.5 全链路协同验证test_db.php的诞生与生死时速最后也是最关键的集成测试。我们创建一个完整的、能创建表、插入数据、查询数据的 PHP 脚本sudo tee /var/www/html/test_db.php EOF ?php $host localhost; $username root; $password MyPssw0rd2023!; // 替换为你自己的密码 $database lamp_test; // 创建连接 $conn mysqli_connect($host, $username, $password); // 检查连接 if (!$conn) { die(Connection failed: . mysqli_connect_error()); } // 创建数据库如果不存在 $sql_create_db CREATE DATABASE IF NOT EXISTS $database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; if (!mysqli_query($conn, $sql_create_db)) { die(Create database failed: . mysqli_error($conn)); } // 选择数据库 mysqli_select_db($conn, $database); // 创建测试表 $sql_create_table CREATE TABLE IF NOT EXISTS test_table