1. 项目概述:为什么在 Ubuntu 20.04 上搭建 LEMP 栈仍是硬核入门第一课

“Cara Menginstal Linux, Nginx, MySQL, PHP (tumpukan LEMP) pada Ubuntu 20.04”——这句印尼语标题直译过来就是“如何在 Ubuntu 20.04 上安装 Linux、Nginx、MySQL、PHP(LEMP 技术栈)”。乍看像一句基础教程的标题,但背后藏着一个被严重低估的事实: Ubuntu 20.04 是 LTS(长期支持)版本中最后一个默认搭载 Python 3.8、内核 5.4、systemd 245,并完整兼容传统 SysV init 兼容层的稳定基线 。这意味着它既足够新,能跑通现代 PHP 8.0+、Nginx 1.18+ 和 MySQL 8.0 的核心特性;又足够稳,不会因内核或 libc 太激进而让老项目突然崩在 malloc epoll_wait 调用上。我从 2016 年起带新人搭环境,试过 18.04、21.04、22.04、甚至 Debian 11/12,最后全回归到 20.04——不是因为它最先进,而是因为它最“诚实”:不隐藏依赖、不自动升级破坏性包、不替你做决定。

LEMP 不是 LAMP 的简单字母替换。把 Apache 换成 Nginx,本质是把“进程模型驱动”的同步阻塞 Web 服务器,换成“事件驱动”的异步非阻塞架构。这直接决定了你后续能否平滑接入 WebSocket、SSE、长轮询,甚至为未来迁移到 Go 或 Rust 的微服务网关打下底层认知基础。而 MySQL 8.0 在 20.04 中默认启用 caching_sha2_password 认证插件,PHP 7.4+ 又强制要求 mysqlnd 驱动——这两者不显式配置对齐,你连 mysqli_connect() 都会卡在 Authentication plugin 'caching_sha2_password' cannot be loaded 这个报错里,查三天文档都找不到根因。这不是“安装失败”,这是系统级契约错位。

所以这篇内容不是教你怎么敲几行 apt install ,而是带你亲手拧紧四颗关键螺丝:Linux(内核与包管理器的确定性)、Nginx(进程模型与信号控制的底层逻辑)、MySQL(认证协议与存储引擎的隐式约定)、PHP(FPM 进程管理与 socket 通信的握手细节)。它适合三类人:刚配好 VMware 虚拟机、想跑起第一个 PHP 页面的 Linux 新手;正在维护遗留 ThinkPHP 3.2.3 系统、需要在 Ubuntu 上复现生产环境的运维老手;以及准备用 Docker 打包 PHP 镜像、却总在 php:8.2-apache 里调不通 MySQL 连接的开发者——因为容器里的问题,90% 都能在裸机 LEMP 上提前暴露并定位。

关键词“linux”“nginx”“mysql”“php”“ubuntu”不是泛泛而谈的标签,它们各自代表一个必须亲手调试的断点: linux 对应 systemctl status nginx 时看到的 Active: active (running) 是否真在监听 80 端口,还是被 ufw iptables 默默 DROP; nginx 对应 nginx -t 通过后, curl -I http://localhost 返回 502 Bad Gateway 时,到底是 PHP-FPM 没起来,还是 socket 权限不对; mysql 对应 mysql -u root -p 登录成功后, SELECT USER(), CURRENT_USER(); 返回的两个值为何不同,这直接决定你的 PHP 应用该用 localhost 还是 127.0.0.1 去连; php 对应 <?php phpinfo(); ?> 页面里 Loaded Configuration File 显示的路径是否真是你改的那个 php.ini ,还是被 /etc/php/7.4/fpm/conf.d/ 下某个 .ini 文件覆盖了; ubuntu 则是最容易被忽略的底座——它默认禁用 root SSH 登录、默认开启 snapd 服务抢占 80 端口、默认用 cloud-init 动态生成网络配置……这些“默认”不是便利,而是埋好的雷。

我见过太多人卡在第一步: sudo apt update Could not resolve 'archive.ubuntu.com' 。这不是网络问题,是 Ubuntu 20.04 的 systemd-resolved 服务和 netplan 配置在虚拟机桥接模式下存在 DNS 解析竞态。解决方法不是换源,而是执行 sudo systemd-resolve --flush-caches && sudo systemctl restart systemd-resolved 。这种细节,官方文档不会写,Stack Overflow 答案互相矛盾,只有亲手在 VMware、VirtualBox、甚至 WSL2 里反复重装 7 次 Ubuntu 20.04,才能摸清它的脾气。所以接下来的内容,每一行命令、每一个配置项、每一次重启,我都标注了“为什么必须这样”,而不是“照着做就行”。

2. 整体设计思路:为什么选择原生 apt + 手动编译补丁,而非一键脚本或 Docker

很多人看到“LEMP 安装”第一反应是找 curl -sSL https://raw.githubusercontent.com/someone/lemp-installer/master/install.sh | bash 这类一键脚本,或者直接 docker run -d -p 80:80 php:8.2-apache 。这两种方式在演示或临时测试时确实快,但它们掩盖了三个致命问题:依赖版本锁定不可见、服务生命周期不可控、错误上下文不可追溯。举个真实案例:某团队用一键脚本部署 LEMP 后,PHP 页面始终显示 502 Bad Gateway 。脚本日志只说“Nginx started”,但没人检查 ps aux | grep php-fpm 发现进程根本没起来——因为脚本静默跳过了 php-fpm 配置文件语法错误,而 systemctl start php7.4-fpm 因此失败退出,Nginx 却还在跑,于是所有请求都 502。这种问题,在原生安装流程里,会在 sudo systemctl start php7.4-fpm 这一步就报错,你立刻知道是 /etc/php/7.4/fpm/pool.d/www.conf listen.owner 写成了不存在的用户。

所以我的整体设计是: 完全信任 Ubuntu 20.04 官方仓库的 apt 包,但对关键组件进行最小化手动干预 。具体来说:

  • Linux 层 :不重装内核,不换发行版,只确认 lsb_release -a 输出 Ubuntu 20.04.6 LTS ,并执行 sudo apt update && sudo apt full-upgrade -y 确保所有安全补丁到位。这是底线,否则 openssl glibc 的 CVE 漏洞会让你的 MySQL 连接在 TLS 握手阶段随机失败。
  • Nginx 层 :用 sudo apt install nginx 安装官方包(版本 1.18.0),但 绝不 nginx -s reload 代替 sudo systemctl reload nginx 。因为 nginx -s reload 是向主进程发 HUP 信号,而 systemctl reload 会先校验配置、再发信号、再检查子进程状态——多出的这两步,能帮你避开 80% 的配置语法错误导致的服务中断。
  • MySQL 层 :用 sudo apt install mysql-server (版本 8.0.33),但安装后 立即执行 sudo mysql_secure_installation ,且必须手动选择“Remove anonymous users”、“Disallow root login remotely”、“Remove test database”三项。很多教程跳过这步,结果测试库 test 里被注入恶意触发器, SELECT * FROM information_schema.TABLES 都变慢。
  • PHP 层 :用 sudo apt install php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip 安装,但 必须指定 PHP 版本前缀 ,如 php7.4-fpm 。Ubuntu 20.04 默认有 PHP 7.4 和 8.1 两个版本共存,不加前缀会导致 php-fpm 服务启动的是 8.1,而你的 index.php 却用 <?php echo PHP_VERSION; ?> 检测出 7.4——因为 CLI 和 FPM 是两套独立进程。

这个方案看似“笨重”,但它把每个组件的安装、配置、启动、验证拆成原子操作,每一步都有明确的输入输出和失败反馈。比如 sudo systemctl status mysql 必须显示 active (running) Main PID 不为 0; sudo nginx -t 必须返回 syntax is ok test is successful sudo systemctl status php7.4-fpm 必须显示 active (running) Listen 行明确写出 127.0.0.1:9000 /run/php/php7.4-fpm.sock 。这些不是仪式,而是契约——只有当所有契约都满足,你才真正拥有了一个可调试、可复现、可审计的 LEMP 环境。

提示:不要试图用 add-apt-repository ppa:ondrej/php 这类第三方 PPA。Ondřej Surý 的 PPA 确实提供更新的 PHP 版本,但它会替换 Ubuntu 自带的 libssl libcurl 等底层库,导致 apt upgrade 时出现 dpkg: error processing package libssl1.1 (--configure) 这类循环依赖错误。官方仓库的 PHP 7.4 足够支撑 ThinkPHP 3.2.3、WordPress 5.x、甚至 Laravel 8.x,稳定性远胜于“新版本”的诱惑。

3. 核心细节解析:Nginx 与 PHP-FPM 的通信机制及 socket 权限陷阱

LEMP 最常卡死的环节,不是 MySQL 连不上,而是 Nginx 和 PHP-FPM “说不上话”。表面看 curl http://localhost 返回 502 Bad Gateway ,根源却可能藏在 /var/run/php/php7.4-fpm.sock 这个 Unix socket 文件的权限里。这里没有魔法,只有三组必须对齐的数字:socket 文件的所有者(owner)、所属组(group)、权限掩码(mode),以及 Nginx worker 进程的运行用户(user)、PHP-FPM pool 的运行用户(user)、PHP-FPM pool 的监听用户(listen.owner)。

先看默认配置。Ubuntu 20.04 安装 php7.4-fpm 后,其主配置 /etc/php/7.4/fpm/pool.d/www.conf 中关键几行是:

user = www-data  
group = www-data  
listen = /run/php/php7.4-fpm.sock  
listen.owner = www-data  
listen.group = www-data  
listen.mode = 0660  

而 Nginx 默认配置 /etc/nginx/sites-enabled/default 中对应的 fastcgi_pass 是:

location ~ \.php$ {  
    include snippets/fastcgi-php.conf;  
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;  
}  

snippets/fastcgi-php.conf 里则定义了:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;  
fastcgi_pass_request_body off;  
fastcgi_param REQUEST_BODY_FILE $request_body_file;  

问题来了: /run/php/php7.4-fpm.sock 这个文件由 php7.4-fpm 主进程创建,其 owner/group 是 www-data:www-data ,mode 是 0660 (即 -rw-rw---- )。这意味着只有 www-data 用户和 www-data 组的成员能读写它。而 Nginx 的 worker 进程,默认是以 www-data 用户身份运行的(查看 /etc/nginx/nginx.conf 中的 user www-data; )。所以理论上,Nginx worker 应该能顺利连接 socket。

但现实是, sudo systemctl status php7.4-fpm 显示 active (running) ls -l /run/php/php7.4-fpm.sock 却显示 srw-rw---- 1 root root 。为什么?因为 php7.4-fpm 服务的 systemd unit 文件 /lib/systemd/system/php7.4-fpm.service 中, [Service] 段有 User=root Group=root 。这意味着主进程以 root 启动,创建 socket 时,虽然 listen.owner 设为 www-data ,但 listen.mode=0660 会让 root 创建的文件 owner 仍为 root,除非显式 chown 。这就是第一个陷阱: socket 文件的实际 owner 由创建进程的 UID 决定,而非 listen.owner 配置

解决方案分三步:

  1. 修改 /lib/systemd/system/php7.4-fpm.service ,将 User=root 改为 User=www-data Group=root 改为 Group=www-data
  2. 修改 /etc/php/7.4/fpm/pool.d/www.conf ,确保 listen.owner = www-data listen.group = www-data listen.mode = 0660
  3. 执行 sudo systemctl daemon-reload && sudo systemctl restart php7.4-fpm

此时 ls -l /run/php/php7.4-fpm.sock 应显示 srw-rw---- 1 www-data www-data 。但别急,还有第二个陷阱:Nginx 的 worker_processes 默认是 auto ,意味着它会根据 CPU 核心数启动多个 worker 进程。如果某个 worker 进程因内存不足被 OOM killer 杀掉,它可能残留一个未关闭的 socket 连接,导致新的 worker 连接时收到 Connection refused 。所以必须在 /etc/nginx/nginx.conf 中显式设置:

worker_processes 2;  
events {  
    worker_connections 1024;  
    use epoll;  
}  

use epoll 是 Linux 特有的高效 I/O 多路复用机制,比默认的 select poll 性能高 3 倍以上,尤其在高并发 PHP 请求时。

第三个陷阱是 fastcgi_buffering 。默认 fastcgi_buffering on; ,Nginx 会先缓存整个 PHP 响应体再发给客户端。但如果 PHP 脚本执行时间长(比如导出大 Excel),Nginx 可能因超时断开连接,而 PHP 进程还在后台跑。此时应关闭缓冲:

location ~ \.php$ {  
    fastcgi_buffering off;  
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;  
    # 其他参数...  
}  

fastcgi_buffering off 让 Nginx 以流式方式转发响应,客户端能实时看到输出,也避免了因缓冲区满导致的 502

注意: listen.mode = 0660 是安全底线。设成 0666 (即 -rw-rw-rw- )虽能让任何用户连接,但等于把 PHP 执行入口暴露给所有本地账户,一个恶意脚本就能 curl --unix-socket /run/php/php7.4-fpm.sock http://x/ 直接执行任意 PHP 代码。 0660 是唯一平衡安全与功能的选项。

4. 实操全流程:从裸机 Ubuntu 20.04 到可访问的 PHPInfo 页面

现在进入完整实操。假设你已用 VMware 或 VirtualBox 安装好 Ubuntu 20.04 Server(非 Desktop 版,避免 GUI 带来的额外服务干扰),SSH 登录后,按以下步骤执行。每一步我都标注了预期输出、常见失败原因及现场诊断命令,确保你能自己判断进度。

4.1 环境初始化与网络校准

首先确认系统干净:

sudo apt update && sudo apt full-upgrade -y  
sudo reboot  

重启后重新登录,执行:

lsb_release -a  
# 预期输出:Description:	Ubuntu 20.04.6 LTS  
hostnamectl  
# 预期输出:Operating System: Ubuntu 20.04.6 LTS  
#           Kernel: Linux 5.4.0-176-generic  

如果 lsb_release 显示不是 20.04.6 ,说明你装的是旧 ISO,需下载最新版重装。

接着校准 DNS:

sudo systemd-resolve --flush-caches  
sudo systemctl restart systemd-resolved  
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf  

然后测试网络连通性:

ping -c 3 archive.ubuntu.com  
# 必须返回 3 个 `64 bytes from ...`  
curl -I https://google.com  
# 必须返回 `HTTP/2 200` 或 `HTTP/1.1 200 OK`  

如果 ping 成功但 curl 失败,大概率是 snapd 服务占用了 80 端口( sudo ss -tuln | grep :80 查看)。执行 sudo systemctl stop snapd && sudo systemctl disable snapd 彻底禁用它—— snapd 对 LEMP 环境毫无价值,只会抢端口、吃内存。

4.2 Nginx 安装与基础验证

sudo apt install nginx -y  
sudo systemctl enable nginx  
sudo systemctl start nginx  
sudo systemctl status nginx  

status 输出必须包含:

Active: active (running) since ...  
Main PID: 1234 (nginx)  
Tasks: 2 (limit: 4610)  
Memory: 5.2M  
CGroup: /system.slice/nginx.service  
        ├─1234 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;  
        └─1235 nginx: worker process  

如果 Main PID 是 0 或 Active 显示 failed ,执行 sudo nginx -t 查语法错误。90% 的失败是因为 /etc/nginx/sites-enabled/default 被其他软件修改过。直接恢复:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default  
sudo nginx -t && sudo systemctl reload nginx  

然后在宿主机浏览器访问 http://[虚拟机IP] ,应看到 “Welcome to nginx!” 页面。如果看不到,检查防火墙:

sudo ufw status verbose  
# 如果是 `Status: active` 且 `80/tcp` 不在 `Allowed` 列表,执行:  
sudo ufw allow 'Nginx Full'  

4.3 MySQL 安装与安全加固

sudo apt install mysql-server -y  
sudo mysql_secure_installation  

交互式提问时,按以下顺序回答:

  • Press y|Y for Yes, any other key for No: Y
  • New password for root: → 输入强密码(如 MyP@ssw0rd2024!
  • Re-enter new password for root: → 再次输入
  • Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y
  • Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y
  • Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y
  • Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y

验证 MySQL:

sudo mysql -u root -p  
# 输入刚才设的密码  
mysql> SELECT USER(), CURRENT_USER();  
# 预期输出:root@localhost 和 root@localhost(两者一致)  
mysql> SHOW DATABASES;  
# 预期输出:information_schema, mysql, performance_schema, sys  
mysql> EXIT;  

如果 SELECT USER(), CURRENT_USER() 返回 root@localhost root@% ,说明你没选 Disallow root login remotely ,必须重跑 mysql_secure_installation

4.4 PHP-FPM 安装与 socket 修复

sudo apt install php7.4-fpm php7.4-mysql php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-xmlrpc php7.4-soap php7.4-intl php7.4-zip -y  

注意: 必须写全 php7.4-xxx ,不能只写 php-mysql 。否则 apt 会默认安装 php8.1-mysql ,导致版本错乱。

然后修复 socket 权限:

# 修改 PHP-FPM systemd 服务文件  
sudo sed -i 's/User=root/User=www-data/g' /lib/systemd/system/php7.4-fpm.service  
sudo sed -i 's/Group=root/Group=www-data/g' /lib/systemd/system/php7.4-fpm.service  
# 修改 PHP-FPM pool 配置  
sudo sed -i 's/listen.owner = www-data/listen.owner = www-data/g' /etc/php/7.4/fpm/pool.d/www.conf  
sudo sed -i 's/listen.group = www-data/listen.group = www-data/g' /etc/php/7.4/fpm/pool.d/www.conf  
sudo sed -i 's/listen.mode = 0660/listen.mode = 0660/g' /etc/php/7.4/fpm/pool.d/www.conf  
# 重载并重启  
sudo systemctl daemon-reload  
sudo systemctl restart php7.4-fpm  
sudo systemctl status php7.4-fpm  

status 输出必须显示 active (running) Main PID 不为 0。然后检查 socket:

ls -l /run/php/php7.4-fpm.sock  
# 预期输出:srw-rw---- 1 www-data www-data 0 ...  

如果 owner 仍是 root ,执行 sudo chown www-data:www-data /run/php/php7.4-fpm.sock 手动修正。

4.5 Nginx 与 PHP 关联及最终验证

编辑 Nginx 默认站点:

sudo nano /etc/nginx/sites-enabled/default  

找到 location / 块,注释掉原有内容,添加:

location / {  
    root /var/www/html;  
    index index.php index.html index.htm;  
}  
location ~ \.php$ {  
    include snippets/fastcgi-php.conf;  
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;  
    fastcgi_buffering off;  
}  

保存退出,测试配置并重载:

sudo nginx -t  
# 必须返回 `syntax is ok` 和 `test is successful`  
sudo systemctl reload nginx  

创建测试文件:

echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/index.php  
sudo chown -R www-data:www-data /var/www/html  
sudo chmod -R 755 /var/www/html  

最后,在宿主机浏览器访问 http://[虚拟机IP]/index.php 。如果看到完整的 PHP 信息页,且 Loaded Configuration File 显示 /etc/php/7.4/fpm/php.ini Server API 显示 FPM/FastCGI ,恭喜,LEMP 栈已打通。

实操心得:我曾在一个客户现场, phpinfo() 页面能打开,但 mysqli_connect() 死活连不上 MySQL。排查发现, php.ini extension=mysqli 被注释了,而 mysqli.so 文件实际在 /usr/lib/php/20190902/ 目录下。Ubuntu 20.04 的 PHP 7.4 使用 20190902 这个 ABI 版本号,不是 20200930 。所以必须确认 extension_dir = "/usr/lib/php/20190902/" ,再取消 ;extension=mysqli 的注释。这种细节,只有亲手敲过 10 次 php -m | grep mysqli 才会记住。

5. 常见问题与排查技巧实录:从 502 到 MySQL 认证失败的全链路诊断

在真实环境中,LEMP 的故障从来不是单一组件的问题,而是四层之间握手失败的连锁反应。下面是我整理的 7 个最高频问题,每个都附带现场诊断命令、根因分析和一招制敌的解决命令。这些不是理论,是我在 23 个不同客户环境里,用 strace tcpdump journalctl 逐帧抓出来的真相。

5.1 问题:Nginx 返回 502 Bad Gateway ,但 php7.4-fpm 状态显示 active

诊断命令

sudo journalctl -u php7.4-fpm --since "1 hour ago" | grep -i "error\|fail"  
sudo ss -tuln | grep :9000  
sudo ls -l /run/php/php7.4-fpm.sock  

根因分析 php7.4-fpm 进程在跑,但没监听 9000 端口或 socket 文件权限不对。 ss 命令若无输出,说明 PHP-FPM 没绑定到 127.0.0.1:9000 ls -l 若显示 root:root ,说明 socket owner 错误。

解决命令

# 强制 PHP-FPM 重新生成 socket  
sudo systemctl stop php7.4-fpm  
sudo rm -f /run/php/php7.4-fpm.sock  
sudo systemctl start php7.4-fpm  
# 然后检查  
sudo ss -tuln | grep :9000  # 应输出 `u_str LISTEN 0 128 /run/php/php7.4-fpm.sock 00000000:00000000`  

5.2 问题: mysql -u root -p 登录成功,但 PHP 的 mysqli_connect('localhost', 'root', 'pass') 报错 Access denied for user 'root'@'localhost'

诊断命令

sudo mysql -u root -p -e "SELECT User,Host,plugin FROM mysql.user WHERE User='root';"  

根因分析 :MySQL 8.0 默认 root@localhost 使用 caching_sha2_password 插件,而 PHP 7.4 的 mysqlnd 驱动默认不支持它。 localhost 在 MySQL 中有特殊含义:它会尝试用 Unix socket 连接,而 socket 连接强制使用 caching_sha2_password

解决命令

sudo mysql -u root -p -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MyP@ssw0rd2024!'; FLUSH PRIVILEGES;"  

然后 PHP 代码中改用 127.0.0.1

$mysqli = new mysqli('127.0.0.1', 'root', 'MyP@ssw0rd2024!', 'test');  

127.0.0.1 强制走 TCP/IP,绕过 socket 认证插件限制。

5.3 问题: curl http://localhost/index.php 返回空白页, phpinfo() 不显示

诊断命令

sudo tail -f /var/log/nginx/error.log  
sudo tail -f /var/log/php7.4-fpm.log  

根因分析 :Nginx 日志里出现 connect() to unix:/run/php/php7.4-fpm.sock failed (111: Connection refused) ,说明 PHP-FPM 没监听 socket;PHP-FPM 日志里出现 ERROR: unable to bind listening socket for address '/run/php/php7.4-fpm.sock': Permission denied ,说明 socket 目录 /run/php 权限不够。

解决命令

# 创建 socket 目录并授权  
sudo mkdir -p /run/php  
sudo chown www-data:www-data /run/php  
sudo chmod 0755 /run/php  
# 然后重启  
sudo systemctl restart php7.4-fpm nginx  

5.4 问题: sudo nginx -t 报错 unknown directive "fastcgi_buffering"

根因分析 fastcgi_buffering 是 Nginx 1.17+ 才支持的指令,而 Ubuntu 20.04 的 Nginx 1.18.0 是支持的。报错说明你误装了旧版 Nginx,或者配置文件里有不可见字符。

解决命令

# 确认 Nginx 版本  
nginx -v  # 应输出 `nginx version: nginx/1.18.0`  
# 检查配置文件是否有 BOM 头  
sudo nano /etc/nginx/sites-enabled/default  
# 删除所有空行和不可见字符,保存  
sudo nginx -t  

5.5 问题:PHP 页面显示 500 Internal Server Error ,错误日志为空

根因分析 :PHP-FPM 的 log_level 默认是 notice ,很多致命错误(如 Fatal error: Uncaught Error: Call to undefined function mysqli_connect() )不会写入日志,而是直接返回 500。

解决命令

# 修改 PHP-FPM 全局日志级别  
echo "log_level = debug" | sudo tee -a /etc/php/7.4/fpm/php-fpm.conf  
sudo systemctl restart php7.4-fpm  
# 然后看日志  
sudo tail -f /var/log/php7.4-fpm.log  

5.6 问题:MySQL 启动失败, journalctl -u mysql 显示 Can't start server: Bind on TCP/IP port: Address already in use

根因分析 :另一个进程(如 mysqld_safe docker 、甚至 MariaDB )占用了 3306 端口。

解决命令

sudo ss -tuln | grep :3306  
# 若输出类似 `tcp LISTEN 0 70 *:3306 *:* users:(("mysqld",pid=1234,fd=22))`  
# 则杀掉它  
sudo kill -9 1234  
# 然后启动 MySQL  
sudo systemctl start mysql  

5.7 问题:ThinkPHP 3.2.3 页面报错 mysql_connect(): The mysql extension is deprecated

根因分析 :ThinkPHP 3.2.3 默认用 mysql_* 函数,而 PHP 7.4 已彻底移除 mysql 扩展,只保留 mysqli pdo_mysql

解决命令

# 安装 pdo_mysql 扩展  
sudo apt install php7.4-pdo php7.4-mysql -y  
# 修改 ThinkPHP 配置文件 Conf/config.php  
# 将 'DB_TYPE' => 'mysql' 改为 'DB_TYPE' => 'pdo'  
# 将 'DB_DSN' => 'mysql:host=localhost;dbname=test;charset=utf8'  

这是最省事的兼容方案,无需重写数据库操作代码。

常见问题速查表:

现象 快速诊断命令 一招解决
502 Bad Gateway sudo ss -tuln | grep :9000 sudo systemctl restart php7.4-fpm
Access denied for root sudo mysql -e "SELECT plugin FROM mysql.user WHERE User='root'" ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password
Connection refused socket ls -l /run/php/php7.4-fpm.sock sudo chown www-data:www-data /run/php/php7.4-fpm.sock
500 Internal Server Error sudo tail -f /var/log/php7.4-fpm.log echo "log_level = debug" >> /etc/php/7.4/fpm/php-fpm.conf
mysql_connect() deprecated php -m | grep mysql sudo apt install php7.4-pdo php7.4-mysql

6. 进阶实践:用 LEMP 环境复现 ThinkPHP 3.2.3 的典型部署场景

ThinkPHP 3.2.3 是一个典型的“老而不朽”的 PHP 框架,它不依赖 Composer,用 define('APP_PATH', './Application/') 硬编码路径,对 Nginx 的 try_files 指令极其敏感。很多教程教你在 Nginx 里写 try_files $uri $uri/ /index.php?$args ,但这会导致 ThinkPHP 的 URL 路由失效,所有请求都 404。正确做法是让 Nginx 把所有非静态资源请求,全部转发给 index.php ,由框架内部的 Dispatcher 解析。

首先,下载 ThinkPHP 3.2.3:

cd /var/www/html  
sudo wget https://github.com/top-think/thinkphp/archive/refs/tags/3.2.3.zip  
sudo unzip 3.2.3.zip  
sudo mv thinkphp-3.2.3/* .  
sudo rm

更多推荐