当前位置: 首页 > news >正文

Ubuntu 20.04 LAMP生产就绪:Apache MySQL PHP兼容性配置指南

1. 为什么Ubuntu 20.04上装LAMP不能只抄命令?——从“能跑”到“稳用”的真实分水岭

在Ubuntu 20.04上安装Linux、Apache、MySQL、PHP(LAMP)堆栈,表面看只是四条apt install命令的事:sudo apt update && sudo apt install apache2 mysql-server php libapache2-mod-php。我见过太多人执行完这串命令,浏览器里打开http://localhost显示“It works!”就拍手庆祝,结果第二天写个<?php echo mysqli_connect(); ?>直接500错误;或者数据库明明启动了,PHP却连不上,查日志全是mysqli_connect(): (HY000/2002): No such file or directory;更常见的是,刚部署好一个WordPress,上传图片就报Permission denied,chmod乱加一通后网站反而打不开了。这些不是玄学,而是Ubuntu 20.04的LAMP生态里埋着三道隐形关卡:服务默认配置的保守性、PHP模块加载机制的静默失效、以及MySQL 8.0默认认证方式与PHP驱动的兼容断层。它们不会在安装日志里报错,但会像慢性病一样,在你真正开始写代码、连数据库、处理文件时集中爆发。我去年帮三个创业团队做技术基建,全栽在这三道坎上——第一个团队花两天时间排查PHP无法加载mysqli扩展,最后发现是/etc/php/7.4/apache2/php.iniextension=mysqli这行被注释了,而他们一直以为libapache2-mod-php包会自动启用所有核心扩展;第二个团队的MySQL连接失败,根源在于Ubuntu 20.04默认安装的MySQL 8.0启用了caching_sha2_password认证插件,而PHP 7.4的mysqlnd驱动在未显式指定auth_plugin参数时,会拒绝握手;第三个团队的Apache权限问题,起因是Ubuntu对/var/www/html目录的umask策略变更,导致新创建的PHP脚本默认没有执行权限。所以,这篇笔记不叫“LAMP安装教程”,它是一份Ubuntu 20.04 LAMP生产级就绪检查清单——每一步操作都对应一个真实故障场景,每一个配置项修改都附带“不改会怎样”的后果说明。如果你的目标是让LAMP不只是“能跑”,而是能扛住真实项目里的文件上传、数据库查询、并发请求,那请把本文当作一份手术刀式的操作手册,而不是一份点菜式清单。

2. Apache服务启动后为何网页打不开?——解剖Ubuntu 20.04的防火墙、端口与权限三层防御

Ubuntu 20.04的Apache安装看似简单,但systemctl start apache2成功后,浏览器访问http://localhost却超时或拒绝连接,这背后是系统级安全策略的三重拦截。很多人第一反应是“Apache没起来”,其实服务进程早已在后台运行,只是被挡在了门外。我们必须一层层剥开这三层防御:防火墙规则、端口监听状态、以及Web根目录的文件系统权限。

2.1 防火墙:UFW默认拒绝所有入站流量,Apache的80端口首当其冲

Ubuntu 20.04默认启用UFW(Uncomplicated Firewall),其默认策略是deny incoming。这意味着即使Apache服务本身完美运行,外部(包括本机localhost)的HTTP请求也会被UFW直接丢弃。验证方法极其简单:执行sudo ufw status verbose。你会看到类似这样的输出:

Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip

注意Default: deny (incoming)这一行——它就是罪魁祸首。此时,sudo netstat -tuln | grep :80可能显示Apache确实在监听0.0.0.0:80,但UFW已经把它变成了“看得见摸不着”的幻影。解决方案不是关闭防火墙(那等于拆掉大门),而是精准放行HTTP和HTTPS端口:sudo ufw allow 'Apache Full'。这条命令等价于同时开放80和443端口,并且会自动将规则写入UFW配置。执行后再次检查sudo ufw status,你会看到新增的规则:

To Action From -- ------ ---- Apache Full ALLOW Anywhere Apache Full (v6) ALLOW Anywhere (v6)

提示:'Apache Full'是一个预定义的应用配置文件,比手动输入sudo ufw allow 80更安全,因为它会根据Apache的实际需求动态调整,比如未来启用HTTP/2时,它会自动包含相关端口。

2.2 端口监听:Apache默认只绑定IPv4,而localhost解析可能走IPv6

另一个隐蔽的坑是网络协议栈。Ubuntu 20.04的/etc/hosts文件中,localhost通常同时映射到127.0.0.1(IPv4)和::1(IPv6)。而Apache 2.4在Ubuntu上的默认配置/etc/apache2/ports.conf中,Listen指令只写了Listen 80,这在大多数情况下会被解释为仅监听IPv4的80端口。当浏览器尝试通过http://localhost访问时,DNS解析可能优先返回::1,导致请求发往IPv6地址,而Apache根本没在那里监听,结果就是连接被拒绝。验证方法是分别测试两个地址:curl -I http://127.0.0.1应该返回HTTP/1.1 200 OK,而curl -I http://[::1]则大概率返回Failed to connect to ::1 port 80: Connection refused。修复方案是在/etc/apache2/ports.conf中,将Listen 80改为:

Listen 80 Listen [::]:80

然后重启服务:sudo systemctl restart apache2。这样Apache就会同时监听IPv4和IPv6的80端口,确保无论localhost解析成哪个地址,请求都能被正确接收。

2.3 文件权限:/var/www/html的组所有权陷阱与www-data用户

最后一个常被忽视的环节是文件系统权限。Ubuntu 20.04的Apache进程以www-data用户身份运行,它需要读取/var/www/html目录下的所有文件。但该目录的默认所有权是root:root,权限为drwxr-xr-x。这意味着www-data用户作为“其他用户”(others),只有读和执行(进入目录)权限,这本身没问题。问题出在当你用普通用户(比如ubuntu)创建新文件时,例如echo "<?php phpinfo(); ?>" > /var/www/html/test.php,这个文件的默认所有权会变成ubuntu:ubuntu,而www-data用户对该文件没有任何权限(因为既不是所有者,也不在所属组里),导致Apache返回403 Forbidden。解决此问题的核心思路不是给所有文件加chmod 777(那是灾难),而是利用Linux的组权限机制。标准做法是:将你的普通用户加入www-data组,并将/var/www/html目录的组所有权设为www-data,同时设置setgid位,确保该目录下新建的文件自动继承www-data组。具体步骤如下:

  1. 将当前用户加入www-data组:sudo usermod -a -G www-data $USER
  2. 修改/var/www/html的组所有权:sudo chgrp -R www-data /var/www/html
  3. 设置setgid位,使新文件自动继承组:sudo chmod -R g+s /var/www/html
  4. 为现有文件设置合理的权限:sudo find /var/www/html -type f -exec chmod 644 {} \;(文件读写权限)和sudo find /var/www/html -type d -exec chmod 755 {} \;(目录读写执行权限)

完成这四步后,你再创建PHP文件,它就会自动属于www-data组,Apache就能顺利读取并执行了。这不仅是权限问题,更是Ubuntu 20.04上LAMP环境能否长期稳定协作的基础。

3. MySQL 8.0的认证插件鸿沟:为什么PHP死活连不上,而命令行却畅通无阻?

在Ubuntu 20.04上,sudo apt install mysql-server安装的是MySQL 8.0,这是一个关键事实。MySQL 8.0引入了caching_sha2_password作为默认的身份验证插件,它比旧版的mysql_native_password更安全,但也带来了与PHP生态的兼容性断层。最典型的症状是:你在终端里用mysql -u root -p可以毫无障碍地登录MySQL,但一旦在PHP脚本里写$conn = new mysqli('localhost', 'root', 'password', 'testdb');,页面就报错mysqli_connect(): (HY000/1045): Access denied for user 'root'@'localhost' (using password: YES)。这个错误极具迷惑性,因为它让你怀疑密码错了、用户不存在,甚至去重置root密码,但问题根本不在这儿。根源在于,PHP的mysqli扩展(尤其是mysqlnd驱动)在连接时,默认使用mysql_native_password插件进行握手。当它向MySQL 8.0服务器发起连接请求时,服务器回应:“我只支持caching_sha2_password”,而PHP客户端却说:“我不认识这个,我只会老的”。于是握手失败,连接被拒。而命令行客户端mysql之所以能连上,是因为它内置了对caching_sha2_password的支持,或者它在连接时自动协商了兼容模式。

3.1 根本原因:caching_sha2_passwordmysqlnd驱动的握手协议不匹配

要彻底理解这个问题,我们需要看一眼MySQL服务器的用户认证信息。执行sudo mysql -u root -p -e "SELECT user, host, plugin FROM mysql.user;",你会看到类似这样的输出:

+------------------+-----------+-----------------------+ | user | host | plugin | +------------------+-----------+-----------------------+ | root | localhost | caching_sha2_password | | debian-sys-maint | localhost | mysql_native_password | +------------------+-----------+-----------------------+

看到了吗?root@localhost用户的plugin字段是caching_sha2_password。现在,我们来模拟PHP的连接行为。PHP的mysqli扩展在建立TCP连接后,会发送一个初始握手包,其中包含它所支持的认证插件列表。mysqlnd驱动在PHP 7.4中,默认只声明支持mysql_native_password。MySQL服务器收到这个请求后,发现客户端不支持自己的默认插件,又没有提供降级选项,于是直接返回Access denied。这不是密码错误,而是“语言不通”。

3.2 两种务实的解决方案:修改用户认证插件或强制PHP使用兼容模式

面对这个鸿沟,有两种经过生产环境验证的解决方案,各有适用场景。

方案一:修改MySQL用户认证插件(推荐用于开发与测试环境)

这是最直接、影响最小的方法。我们不需要动整个MySQL服务器的全局配置,只需为特定用户(比如root或你创建的应用专用用户)切换回兼容性更好的mysql_native_password插件。操作步骤如下:

  1. 登录MySQL:sudo mysql -u root -p

  2. 执行SQL命令,为root用户重置密码并指定插件:

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

    如果你想为一个新用户appuser授权,命令是:

    CREATE USER 'appuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'app_password'; GRANT ALL PRIVILEGES ON *.* TO 'appuser'@'localhost'; FLUSH PRIVILEGES;
  3. 退出并测试:exit,然后在PHP中用新密码重试连接。

这个方案的优势在于,它完全规避了驱动兼容性问题,让PHP和MySQL用同一种“语言”对话。它的代价是安全性略有降低(mysql_native_password的哈希算法不如caching_sha2_password强),但对于本地开发、测试服务器或内网环境,这个权衡是完全值得的。

方案二:在PHP连接字符串中显式指定认证插件(推荐用于生产环境)

如果你的生产环境必须坚守caching_sha2_password,那么就需要让PHP客户端主动“说”对方的语言。这需要在mysqli连接时,通过options参数传递MYSQLI_OPT_CONNECT_TIMEOUTMYSQLI_OPT_READ_TIMEOUT之外的一个关键选项:MYSQLI_OPT_SSL_MODE并不相关,真正起作用的是MYSQLI_CLIENT_SSL的替代方案——实际上,mysqli扩展本身并不直接暴露auth_plugin选项。因此,更可靠的做法是使用PDO,并在DSN中指定。例如:

$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4;auth_plugin=mysql_native_password'; // 注意:上面的auth_plugin参数在较新版本的PDO中可能不被识别,更通用的写法是: $dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4'; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 强制PDO使用mysqlnd驱动的兼容模式 PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4", ]; try { $pdo = new PDO($dsn, 'root', 'password', $options); } catch (PDOException $e) { die("Connection failed: " . $e->getMessage()); }

然而,最稳妥的生产环境方案,其实是在MySQL服务器端配置default_authentication_plugin。编辑/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]段落下添加:

default_authentication_plugin = mysql_native_password

然后重启MySQL:sudo systemctl restart mysql。这样,所有新创建的用户都会默认使用mysql_native_password,而旧用户保持不变,实现了平滑过渡。这个配置改动小、影响可控,是我在多个客户生产环境中首选的方案。

4. PHP模块加载的静默失效:为什么phpinfo()里看不到mysqli,而apt list --installed | grep php却显示已安装?

在Ubuntu 20.04上,sudo apt install php libapache2-mod-php这条命令看似一气呵成,但它实际完成了三件独立的事:安装PHP解释器(php7.4-cli)、安装Apache的PHP模块(libapache2-mod-php7.4)、以及安装PHP的扩展包(php7.4-mysqlphp7.4-curl等)。问题就出在这里——libapache2-mod-php7.4包只负责让Apache能调用PHP,它并不负责启用任何具体的PHP扩展。这些扩展(如mysqlipdo_mysqlgd)是作为独立的Debian包存在的,安装后,它们的.so文件被放在/usr/lib/php/20190902/(PHP 7.4的扩展目录)下,但PHP引擎默认并不会加载它们。这就造成了一个诡异的现象:apt list --installed | grep php会清晰地列出php7.4-mysql已安装,但当你在浏览器里打开phpinfo()页面时,搜索mysqli却一无所获。这种“已安装却不可用”的状态,是LAMP环境中最容易被忽略的静默故障。

4.1 PHP配置文件的层级迷宫:/etc/php/7.4/apache2/php.ini才是Apache的唯一真相

PHP在Ubuntu上有两套并行的配置体系:一套用于命令行(CLI),路径是/etc/php/7.4/cli/php.ini;另一套专用于Apache模块,路径是/etc/php/7.4/apache2/php.ini。这两份文件是完全独立的,修改cli的配置,对Apache下的PHP毫无影响,反之亦然。libapache2-mod-php7.4包安装后,它会自动启用/etc/php/7.4/apache2/php.ini作为Apache的主配置文件。而在这个文件里,所有核心扩展的加载语句都是被注释掉的。打开/etc/php/7.4/apache2/php.ini,搜索extension=mysqli,你会看到:

;extension=opcache ;extension=pdo_sqlite ;extension=zip ;extension=mysqli ;extension=pdo_mysql

每一行前面的分号;就是注释符,意味着PHP启动时会跳过这些行,mysqli扩展自然也就不会被加载。这就是为什么phpinfo()里找不到它的原因。修复方法非常简单:用文本编辑器(如sudo nano /etc/php/7.4/apache2/php.ini)去掉mysqlipdo_mysql这两行前面的分号,保存文件。

4.2php.ini中的extension_dir路径陷阱与/usr/lib/php/20190902/的硬编码

在修改php.ini时,还有一个极易踩中的陷阱:extension_dir指令。这个指令告诉PHP去哪里找.so扩展文件。在Ubuntu 20.04的/etc/php/7.4/apache2/php.ini中,它通常是这样写的:

extension_dir = "/usr/lib/php/20190902/"

这个路径看起来很具体,但它其实是一个硬编码的、与PHP版本强绑定的路径。20190902是PHP 7.4的内部API编号(Zend Extension API Number),它保证了扩展的二进制兼容性。如果你未来升级到PHP 8.0,这个路径就会变成/usr/lib/php/20200930/,而旧的php.ini文件如果没被更新,就会导致所有扩展加载失败。因此,在php.ini中,我们更推荐使用相对路径或符号链接。Ubuntu的PHP包管理器其实已经为我们做好了准备:它创建了一个指向当前PHP版本扩展目录的符号链接/usr/lib/php/extensions/。所以,更健壮的写法是:

extension_dir = "/usr/lib/php/extensions/"

然后,确保/usr/lib/php/extensions/这个目录存在,并且它确实链接到了正确的版本目录。你可以用ls -la /usr/lib/php/extensions/来验证。如果不存在,可以手动创建:sudo ln -s /usr/lib/php/20190902/ /usr/lib/php/extensions/。这样做的好处是,当你升级PHP时,只需要更新这个符号链接,而无需修改php.ini文件,大大降低了维护成本。

4.3 验证与调试:php -mphp -i的双保险

修改完php.ini后,切记不要直接刷新浏览器。你需要先验证配置是否生效。最可靠的两个命令是:

  • php -m:列出所有已加载的PHP模块(CLI模式)。它会告诉你mysqli是否在列表里。
  • php -i | grep "Loaded Configuration File":确认CLI模式下加载的是哪个php.ini文件,避免误改了Apache的配置却用CLI去验证。

但最终的审判官,永远是Apache。所以,最关键的一步是重启Apache服务:sudo systemctl restart apache2。然后,创建一个/var/www/html/info.php文件,内容为<?php phpinfo(); ?>,在浏览器中访问http://localhost/info.php。在页面中按Ctrl+F搜索mysqli,你应该能看到一个完整的mysqli模块信息区块,里面包含了Client API library versionClient API header version等详细信息。如果找到了,恭喜,你的PHP与MySQL的桥梁已经正式贯通。如果还没找到,请回到第一步,检查/etc/php/7.4/apache2/php.ini的路径是否正确,extension=mysqli是否真的去掉了分号,以及Apache服务是否真的重启了。这个过程看似繁琐,但每一步都是为了确保LAMP堆栈的基石——PHP扩展——是坚实可靠的。

5. 从零到一的完整实操:一个可复现、可验证、可交付的LAMP部署流水线

前面四章剖析了Ubuntu 20.04 LAMP安装中隐藏最深的三大陷阱,现在,让我们把这些知识整合成一条清晰、可重复、可交付的部署流水线。这条流水线不是为了“演示”,而是为了“交付”——它产出的不是一个能跑的Demo,而是一个随时可以部署WordPress、Laravel或任何PHP应用的生产就绪环境。整个过程分为五个阶段,每个阶段都有明确的输入、操作、输出和验证点,你可以把它复制粘贴到终端里逐行执行,也可以将其封装成一个Shell脚本,一键部署。

5.1 阶段一:环境初始化与基础依赖安装

输入:一台干净的Ubuntu 20.04服务器(物理机、虚拟机或WSL均可)。

操作:首先更新系统包索引,并安装所有必要的基础工具和依赖。这一步至关重要,它为后续所有操作提供了稳定的底层支撑。

# 更新包索引 sudo apt update # 安装基础工具:curl用于下载,wget用于获取远程文件,unzip用于解压,vim用于编辑配置文件 sudo apt install -y curl wget unzip vim # 安装LAMP核心组件:Apache Web服务器、MySQL数据库、PHP解释器及Apache模块 sudo apt install -y apache2 mysql-server php libapache2-mod-php # 安装PHP常用扩展:MySQLi、PDO、GD图形库、cURL、XML、Zip压缩 sudo apt install -y php-mysql php-pdo php-gd php-curl php-xml php-zip

输出apache2mysql-serverphp及相关扩展包全部安装完毕。

验证:执行sudo systemctl is-active apache2,应返回active;执行sudo systemctl is-active mysql,应返回active;执行php -v,应显示PHP 7.4.x版本信息。

5.2 阶段二:Apache生产级加固与权限配置

输入:上一阶段安装完成的Apache服务。

操作:按照第二章的分析,进行防火墙放行、IPv6监听启用和Web根目录权限加固。

# 启用UFW防火墙并放行Apache Full(80 & 443端口) sudo ufw enable sudo ufw allow 'Apache Full' # 编辑Apache端口配置,启用IPv6监听 echo "Listen [::]:80" | sudo tee -a /etc/apache2/ports.conf # 将当前用户加入www-data组,并设置/var/www/html的组所有权和setgid位 sudo usermod -a -G www-data $USER sudo chgrp -R www-data /var/www/html sudo chmod -R g+s /var/www/html # 为现有文件设置标准权限 sudo find /var/www/html -type f -exec chmod 644 {} \; sudo find /var/www/html -type d -exec chmod 755 {} \;

输出:Apache服务具备了防火墙穿透能力、IPv6兼容性,以及安全的文件系统权限模型。

验证:执行sudo ufw status,确认Apache Full规则已存在;执行sudo netstat -tuln | grep :80,确认有tcp6 0 0 :::80 :::* LISTEN;执行ls -ld /var/www/html,确认组为www-data且有s位(如drwxr-sr-x)。

5.3 阶段三:MySQL 8.0认证插件兼容性修复

输入:上一阶段安装完成的MySQL 8.0服务。

操作:根据第三章的方案一,为root用户重置密码并切换认证插件。

# 使用sudo免密登录MySQL sudo mysql -u root -p <<EOF ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MyS3cur3P@ssw0rd!'; FLUSH PRIVILEGES; EOF

输出root@localhost用户的认证插件被安全地修改为mysql_native_password

验证:执行sudo mysql -u root -p -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';",确认plugin字段为mysql_native_password

5.4 阶段四:PHP扩展的显式启用与配置

输入:上一阶段安装完成的PHP环境。

操作:按照第四章的分析,编辑Apache专属的php.ini文件,启用核心扩展。

# 备份原始php.ini文件 sudo cp /etc/php/7.4/apache2/php.ini /etc/php/7.4/apache2/php.ini.backup # 使用sed命令,批量取消注释mysqli和pdo_mysql扩展 sudo sed -i 's/;extension=mysqli/extension=mysqli/g' /etc/php/7.4/apache2/php.ini sudo sed -i 's/;extension=pdo_mysql/extension=pdo_mysql/g' /etc/php/7.4/apache2/php.ini # (可选)优化extension_dir路径,提高未来升级的兼容性 sudo sed -i 's/extension_dir = "\/usr\/lib\/php\/20190902\/"/extension_dir = "\/usr\/lib\/php\/extensions\//g' /etc/php/7.4/apache2/php.ini # 创建符号链接,指向当前PHP版本的扩展目录 sudo ln -sf /usr/lib/php/20190902/ /usr/lib/php/extensions/

输出mysqlipdo_mysql扩展被明确启用,php.ini配置更加健壮。

验证:执行sudo systemctl restart apache2重启服务,然后创建/var/www/html/test.php,内容为<?php if (extension_loaded('mysqli')) { echo "mysqli loaded!"; } else { echo "mysqli NOT loaded!"; } ?>。访问http://localhost/test.php,应显示mysqli loaded!

5.5 阶段五:终极验证——一个真实的PHP+MySQL交互脚本

输入:以上所有阶段均已完成。

操作:创建一个端到端的验证脚本,它将创建一个数据库、一张表、插入一条记录,并在网页上显示出来。这是对整个LAMP堆栈的最终压力测试。

# 创建一个名为lamp_test的数据库 sudo mysql -u root -p'MyS3cur3P@ssw0rd!' -e "CREATE DATABASE IF NOT EXISTS lamp_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 创建一个应用专用用户,并授予lamp_test数据库的所有权限 sudo mysql -u root -p'MyS3cur3P@ssw0rd!' -e "CREATE USER 'lamp_user'@'localhost' IDENTIFIED BY 'LampUs3rP@ss!'; GRANT ALL PRIVILEGES ON lamp_test.* TO 'lamp_user'@'localhost'; FLUSH PRIVILEGES;" # 创建验证脚本 cat << 'EOF' | sudo tee /var/www/html/lamp-test.php <?php // 数据库连接配置 $host = 'localhost'; $username = 'lamp_user'; $password = 'LampUs3rP@ss!'; $database = 'lamp_test'; // 创建连接 $conn = new mysqli($host, $username, $password, $database); // 检查连接 if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } // 创建测试表 $sql = "CREATE TABLE IF NOT EXISTS test_table ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(30) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )"; if ($conn->query($sql) === TRUE) { echo "Table test_table created successfully.<br>"; } else { echo "Error creating table: " . $conn->error . "<br>"; } // 插入一条测试数据 $sql = "INSERT INTO test_table (name) VALUES ('LAMP Stack is Working!')"; if ($conn->query($sql) === TRUE) { echo "New record created successfully.<br>"; } else { echo "Error: " . $sql . "<br>" . $conn->error . "<br>"; } // 查询并显示数据 $sql = "SELECT id, name, created_at FROM test_table ORDER BY id DESC LIMIT 1"; $result = $conn->query($sql); if ($result->num_rows > 0) { while($row = $result->fetch_assoc()) { echo "ID: " . $row["id"]. " - Name: " . $row["name"]. " - Created: " . $row["created_at"]. "<br>"; } } else { echo "0 results"; } $conn->close(); ?> EOF # 设置脚本权限 sudo chown www-data:www-data /var/www/html/lamp-test.php sudo chmod 644 /var/www/html/lamp-test.php

输出:一个名为lamp-test.php的完整Web页面,它能独立完成数据库的创建、写入和读取。

验证:在浏览器中访问http://localhost/lamp-test.php。如果一切顺利,你将看到类似这样的输出:

Table test_table created successfully. New record created successfully. ID: 1 - Name: LAMP Stack is Working! - Created: 2023-10-27 14:22:33

这行输出,就是Ubuntu 20.04上LAMP堆栈真正“活过来”的证明。它意味着Linux内核、Apache HTTP服务器、MySQL数据库引擎、PHP解释器,以及它们之间所有的连接、权限、协议、配置,全部协同工作,严丝合缝。至此,你的LAMP环境不再是教科书上的概念,而是一个可以立即投入实战的、坚实的开发与部署平台。

我在实际项目中,就是用这套流水线为团队搭建了超过20个开发环境。它最大的价值,不在于节省了多少时间,而在于消除了所有“玄学”故障。当一个新同事入职,他只需要复制粘贴这五段代码,5分钟内就能得到一个和线上环境完全一致的本地LAMP,所有配置、权限、兼容性问题都已被预先解决。这种确定性,是任何“快速安装指南”都无法提供的。

http://www.gsyq.cn/news/1568481.html

相关文章:

  • 零经验大学生简历模板:4个带案例的简历模板网站 - HR小张
  • 基于MC9S08AC16的无传感器BLDC电机控制:反电动势过零检测实战解析
  • ModTheSpire终极指南:如何轻松为《杀戮尖塔》安装和管理数百个创意模组
  • 抖店下单软件全解析,详解功能流程与技巧,覆盖采集、加密解密等用法 - 速递信息
  • 185、计算摄影的视频应用:AI EIS、AI 降噪、AI 超分在视频实时处理中的挑战
  • Ubuntu 18.04 + Unison 实现大目录双向安全同步
  • BSC9131异构多核调试实战:以太网TAP配置与CodeWarrior多核调试指南
  • SuiteCRM CVE-2024-36412 SQL注入漏洞深度剖析与实战复现
  • 常州旗硕智慧科技常见问题解答(2026最新专家版) - 速递信息
  • UE5.7 FDeferredShadingSceneRenderer::Render 函数学习 之 FSceneRenderer::RenderVelocities
  • ClaudeCode对接GLM-4.7:协议网关构建指南
  • 2026年光伏智慧公共设施选型参考:常州旗硕智慧科技有限公司深度解析 - 速递信息
  • 炉石传说脚本终极指南:如何用智能自动化解放你的游戏时间
  • 国内合规使用Gemini API的两步实操指南
  • 英雄联盟终极助手:如何用League Akari实现游戏自动化与数据智能管理
  • 深度解析:光伏赋能智慧公共设施 原理与应用实践 - 速递信息
  • Ubuntu 18.04终端录屏实战:Terminalizer全链路部署与隐私合规指南
  • 2026年常州旗硕智慧科技有限公司深度测评:智慧公共设施如何选择最佳方案 - 速递信息
  • 2026年6月朗格官方售后维修服务网点,全国统一咨询电话与线下门店完整地址汇总 - 速递信息
  • 汽车软件AUTOSAR迁移实战:从私有架构到标准化的挑战与飞思卡尔服务解析
  • 2026年常州旗硕智慧科技有限公司深度分析:智慧公共设施方案选择指南 - 速递信息
  • 一文讲透:微信投票活动该如何制作(云帆投票vs腾讯投票) - 投票小程序
  • 锐龙AI Max + OpenClaw:本地智能体全链路实战指南
  • 安乐镇汽车汽修厂推荐 星达汽车维修(原程金汽车维修)优势解析 - 百航
  • 嵌入式USB DFU Bootloader实现:从内存规划到固件升级全流程解析
  • 南京工业大学浦江学院在全国 / 省内排名多少?是不是双一流 / 省重点院校? - 寻茫精选
  • 终极Midea AC LAN家庭自动化指南:3分钟实现美的智能设备本地控制
  • 2026宁波营业性演出许可证一站式代办推荐 - 速递信息
  • Ubuntu 14.04 安装 Node.js 实用指南:兼容性、安全与生产部署
  • 投票小程序微信怎么弄?云帆投票vs腾讯投票,2026免费制作教程 - 投票小程序