1. 项目概述:为什么在 Ubuntu 20.04 上搭建 LEMP 栈仍是硬核入门的黄金路径
“Cómo instalar Linux, Nginx, MySQL y PHP (pila LEMP) en Ubuntu 20.04”——这个西班牙语标题直译过来就是“如何在 Ubuntu 20.04 上安装 Linux、Nginx、MySQL 和 PHP(LEMP 技术栈)”。它看似是一条基础操作指南,但背后承载的是 Web 服务部署最经典、最稳健、也最具教学价值的技术范式。我从 2012 年开始带新人做后端部署,至今仍坚持用 Ubuntu 20.04 + LEMP 作为第一课:不是因为它最新,而是因为它足够“稳”,稳到能让你看清每一层组件的职责边界,稳到故障时每一条日志都有明确归因,稳到哪怕你只记得
systemctl status nginx
这一个命令,也能快速定位八成以上服务异常。Linux 是操作系统底座,Nginx 是高性能 HTTP 服务器与反向代理,MySQL 是关系型数据存储核心,PHP 是动态内容执行引擎——这四者组合起来,构成了一套完整、自洽、可独立运行的 Web 应用交付闭环。它不依赖 Docker 容器抽象层,不绕过系统级权限控制,不隐藏进程管理细节,正因如此,当你亲手敲下
apt install nginx
并成功访问
http://localhost
的那一刻,你获得的不是“跑起来了”的模糊快感,而是对“请求如何从网卡进入用户空间、如何被进程监听、如何路由到脚本、如何查库返回 HTML”这一整条链路的具象认知。Ubuntu 20.04 作为长期支持(LTS)版本,内核稳定、软件源成熟、社区文档海量,其 APT 包管理机制对新手极其友好,同时又保留了足够多的手动配置空间——比如你必须手动编辑
/etc/nginx/sites-available/default
才能让 PHP 脚本正确解析,这种“强制动手”的设计,恰恰是避免陷入“一键脚本幻觉”的关键防线。对于刚接触运维、全栈开发或 DevOps 概念的朋友,LEMP 不是过时的古董,而是一把解剖现代 Web 架构的手术刀;对于已在生产环境使用 Kubernetes 的老手,回过头重装一遍 Ubuntu 20.04 的 LEMP,反而能帮你校准对底层资源调度、文件权限继承、日志轮转机制的真实理解。这不是怀旧,而是夯实地基。
2. 整体架构设计与方案选型逻辑:为什么是 LEMP,而不是 LAMP 或 MEAN?
2.1 LEMP 与 LAMP 的本质差异:不只是 Web 服务器替换
很多人看到 LEMP 就下意识认为“不过是把 Apache 换成 Nginx”,这种理解过于表层。LAMP(Linux-Apache-MySQL-PHP)和 LEMP(Linux-Nginx-MySQL-PHP)的分水岭,不在“谁监听 80 端口”,而在于 请求处理模型的根本性重构 。Apache 采用的是“进程/线程驱动模型”:每个并发连接默认对应一个独立进程或线程,内存占用高、上下文切换开销大,适合处理少量长连接或复杂 .htaccess 规则;而 Nginx 是“事件驱动异步非阻塞模型”,单个 worker 进程可同时处理数万连接,内存占用极低,尤其擅长静态文件分发、反向代理和负载均衡。我在实际项目中做过对比测试:同一台 2 核 4G 的云服务器,部署 WordPress 博客,Apache 在 300 并发时 CPU 占用飙升至 95%,响应延迟超过 2 秒;换成 Nginx + PHP-FPM 后,并发提升至 1200,CPU 稳定在 40% 以下,平均响应时间压到 80ms。这不是参数调优的结果,而是模型差异带来的天然优势。因此,选择 LEMP,本质上是选择了更符合现代 Web 高并发、低延迟诉求的架构范式。更重要的是,Nginx 本身不直接解析 PHP,它通过 FastCGI 协议将 PHP 请求转发给独立的 PHP-FPM(PHP FastCGI Process Manager)进程池处理。这种解耦设计带来三大好处:一是 PHP 进程崩溃不会导致整个 Web 服务器宕机;二是可以为不同站点配置不同的 PHP 版本和资源限制;三是便于横向扩展——你可以把 PHP-FPM 部署在另一台机器上,Nginx 只负责流量分发。而 Apache 的 mod_php 模块是将 PHP 解释器直接嵌入到 Apache 进程中,耦合度高,隔离性差。所以,LEMP 的“E”不是简单的字母替换,它是架构演进的必然选择。
2.2 为什么锁定 Ubuntu 20.04?LTS 版本的“稳”字诀
Ubuntu 20.04(代号 Focal Fossa)是 Canonical 发布的长期支持版本,官方提供长达 5 年的安全更新(至 2025 年 4 月),这是它被广泛用于生产环境和教学场景的核心原因。我见过太多新手因为贪图“新”而选择 Ubuntu 22.04 或 23.10,结果在安装 PHP 扩展时遇到
php-dev
包缺失、
pecl
命令报错、或 Nginx 配置语法不兼容等问题——这些都不是技术难点,而是版本迭代带来的碎片化成本。Ubuntu 20.04 的软件源经过充分验证:Nginx 版本为 1.18.x(稳定版),MySQL 为 8.0.28(LTS 兼容版),PHP 为 7.4(虽已 EOL,但 Ubuntu 20.04 的安全补丁仍持续维护至 2024 年底),所有组件间的 ABI(应用二进制接口)和配置路径高度统一。例如,PHP-FPM 的主配置文件固定在
/etc/php/7.4/fpm/php-fpm.conf
,Nginx 的站点配置默认存放在
/etc/nginx/sites-available/
,MySQL 的数据目录默认为
/var/lib/mysql
——这种确定性,让学习者能把精力聚焦在“原理”而非“找路径”上。另外,Ubuntu 20.04 对硬件兼容性极佳,无论是物理服务器、VMware 虚拟机、VirtualBox,还是 WSL2(Windows Subsystem for Linux),都能开箱即用。我自己的开发环境就常年运行着三台 Ubuntu 20.04 虚拟机:一台专跑 LEMP 基础环境,一台模拟数据库主从,一台测试 Ansible 自动化部署。它们像三块稳定的基石,支撑起所有上层实验。选择 20.04,不是拒绝进步,而是用时间换来的确定性,是工程师最值得信赖的“免调试环境”。
2.3 MySQL 与 PostgreSQL 的取舍:为什么教程坚持用 MySQL
网络热词里频繁出现 “postgresql 和 mysql 区别”,这恰恰说明初学者容易陷入“选型焦虑”。但在 LEMP 入门场景下,MySQL 是无可争议的首选。首先,PHP 的原生扩展
mysqli
和
PDO_MySQL
支持度最高、文档最全、出错提示最友好。一个典型的错误
Call to undefined function mysqli_connect()
,你立刻就能意识到是
php-mysql
扩展没装;而换成 PostgreSQL,
pg_connect()
报错时,新手往往要花半小时排查
php-pgsql
包名、扩展加载顺序、甚至 PostgreSQL 服务是否启动。其次,MySQL 的权限模型更直观:
CREATE USER 'app'@'localhost' IDENTIFIED BY 'pass'; GRANT ALL ON mydb.* TO 'app'@'localhost';
这几行命令,清晰表达了“谁、在哪、能做什么”,而 PostgreSQL 的
role
、
schema
、
database
多层权限体系,对零基础者过于抽象。再者,MySQL 的安装过程极度简化:
apt install mysql-server
后,系统会自动运行
mysql_secure_installation
向导,引导你设置 root 密码、删除匿名用户、禁用远程 root 登录——这是一个完整的安全基线初始化流程。而 PostgreSQL 安装后,默认以
postgres
系统用户运行,你需要手动
sudo -u postgres psql
进入控制台,再执行
\password
修改密码,步骤多一层,心理门槛就高一分。当然,PostgreSQL 在事务严谨性、JSONB 支持、地理空间查询等方面有不可替代的优势,但那属于进阶课题。LEMP 入门的目标,是让你在 2 小时内看到
<?php echo "Hello, LEMP!"; ?>
正确输出,而不是纠结于 ACID 特性的实现细节。先跑通,再优化,这是所有工程实践的第一铁律。
3. 核心组件安装与配置详解:从系统初始化到服务联调
3.1 系统准备与基础环境加固:别跳过这 5 分钟
在敲下第一个
apt
命令前,必须完成三项基础操作,它们决定了后续所有配置的成败。第一,更新系统包索引并升级已安装软件:
sudo apt update && sudo apt upgrade -y
。这一步看似简单,实则关键——Ubuntu 20.04 初始镜像可能包含旧版内核或存在已知漏洞的软件包,不升级就安装 Nginx,可能导致其无法绑定 80 端口(因内核网络栈 bug)。第二,安装常用工具集:
sudo apt install -y curl wget vim git gnupg2 ca-certificates lsb-release software-properties-common
。其中
vim
是必备编辑器(Ubuntu 默认
vi
是精简版,不支持方向键),
curl
和
wget
用于下载外部资源,
gnupg2
是后续添加第三方仓库签名所必需。第三,配置防火墙:
sudo ufw enable
开启 UFW(Uncomplicated Firewall),然后放行必要端口:
sudo ufw allow OpenSSH
(确保远程连接不中断),
sudo ufw allow 'Nginx Full'
(放行 80/443),
sudo ufw status verbose
查看状态。> 提示:切勿跳过防火墙配置!我曾因忘记放行 80 端口,在虚拟机里反复刷新
http://localhost
却始终显示“连接被拒绝”,最后发现是 UFW 默默拦截了所有入站流量。第四,创建非 root 管理员用户(强烈建议):
sudo adduser deploy
,然后
sudo usermod -aG sudo deploy
将其加入 sudo 组。所有后续操作应在此用户下进行,避免
root
权限滥用风险。第五,配置时区与 locale:
sudo timedatectl set-timezone Asia/Shanghai
(根据实际地区调整),
sudo locale-gen zh_CN.UTF-8
(生成中文 locale,避免某些 PHP 扩展编译报错)。这五步耗时不到 5 分钟,却能规避 80% 的“安装失败”类问题,是经验之谈,不是形式主义。
3.2 Nginx 安装与基础配置:不止是“启动服务”
Nginx 的安装非常直接:
sudo apt install nginx -y
。但安装完成后,绝不能止步于
sudo systemctl start nginx
。你需要立即验证三个关键点:第一,检查服务状态:
sudo systemctl status nginx
,确认输出中包含
active (running)
且无红色
failed
字样;第二,验证端口监听:
sudo ss -tlnp | grep :80
,应看到
nginx: master process /usr/sbin/nginx
进程正在监听
0.0.0.0:80
;第三,本地访问测试:
curl -I http://localhost
,返回
HTTP/1.1 200 OK
表示成功。如果失败,90% 的原因是 Apache 已占用了 80 端口(
sudo ss -tlnp | grep :80
查看),此时需
sudo systemctl stop apache2 && sudo systemctl disable apache2
彻底卸载 Apache。接下来是核心配置环节。Nginx 的主配置文件是
/etc/nginx/nginx.conf
,但你不应该直接修改它,而应遵循“全局配置 + 站点配置”分离原则。Ubuntu 20.04 默认启用
/etc/nginx/sites-enabled/default
(软链接到
/etc/nginx/sites-available/default
)。打开该文件:
sudo vim /etc/nginx/sites-available/default
。找到
server
块,重点修改三处:1)
root /var/www/html;
—— 这是网站根目录,保持默认即可;2)
index index.html index.htm index.nginx-debian.html;
—— 这是默认首页文件列表,我们稍后要加入
index.php
;3)最关键的是 PHP 处理段,找到注释掉的
location ~ \.php$ { ... }
块,取消注释并修改为:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
这里
fastcgi_pass
指向 PHP-FPM 的 Unix socket 文件路径,必须与后续 PHP-FPM 配置严格一致。
snippets/fastcgi-php.conf
是 Ubuntu 提供的标准 FastCGI 参数模板,已预设好
SCRIPT_FILENAME
等关键变量。修改后,务必执行
sudo nginx -t
测试配置语法,只有输出
syntax is ok
和
test is successful
才能
sudo systemctl reload nginx
重载配置。> 注意:
reload
不等于
restart
。
reload
是平滑重启,旧连接不受影响;
restart
会中断所有活跃连接。生产环境务必用
reload
。
3.3 MySQL 安装与安全初始化:超越默认 root 密码
MySQL 的安装命令是
sudo apt install mysql-server -y
,但安装完成后,
必须立即执行安全加固
。Ubuntu 20.04 的
mysql-server
包会自动运行
mysql_secure_installation
脚本,但很多新手在交互式提问中一路按回车,导致留下严重安全隐患。正确的做法是:
sudo mysql_secure_installation
,然后逐项回答:1)
Press y|Y for Yes, any other key for No:
→ 输入
Y
;2)
New password for root:
→
设置一个强密码
(至少 8 位,含大小写字母、数字、符号),切勿留空;3)
Remove anonymous users?
→
Y
;4)
Disallow root login remotely?
→
Y
(禁止 root 远程登录,这是基本安全红线);5)
Remove test database and access to it?
→
Y
;6)
Reload privilege tables now?
→
Y
。完成此步骤后,root 用户只能通过本地 socket 连接(
sudo mysql -u root -p
),无法通过 TCP 远程登录。接着,创建一个专用的应用数据库和用户:
sudo mysql -u root -p
进入 MySQL 控制台,执行:
CREATE DATABASE lemp_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'lemp_user'@'localhost' IDENTIFIED BY 'StrongPass123!';
GRANT ALL ON lemp_demo.* TO 'lemp_user'@'localhost';
FLUSH PRIVILEGES;
这里
utf8mb4
是关键!它支持完整的 Unicode 字符(包括 emoji),而旧版
utf8
在 MySQL 中仅支持 3 字节字符,会导致微信昵称、特殊符号存储乱码。
COLLATE utf8mb4_unicode_ci
指定排序规则,确保中文、英文混合排序正确。创建完用户后,退出控制台(
exit
),并用新用户测试连接:
mysql -u lemp_user -p -D lemp_demo
,输入密码后能成功进入,即表示数据库层配置完成。> 实操心得:我习惯在创建数据库时就指定字符集,避免后期
ALTER DATABASE
带来的锁表风险。一次到位,省心省力。
3.4 PHP 及扩展安装:7.4 版本的精准适配
Ubuntu 20.04 默认源中的 PHP 版本是 7.4,虽然 PHP 官方已停止支持,但 Canonical 仍在提供安全更新,且 7.4 与 Nginx、MySQL 8.0 的兼容性经过千锤百炼,是当前最稳妥的选择。安装命令为:
sudo apt install php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip -y
。这里
php-fpm
是核心,它提供了 FastCGI 接口;
php-mysql
是 MySQL 数据库驱动;其余扩展则是现代 PHP 应用的标配:
curl
(HTTP 请求)、
gd
(图像处理)、
mbstring
(多字节字符串支持,处理中文必装)、
xml
(XML 解析)、
zip
(压缩包操作)。安装完成后,必须验证 PHP-FPM 服务状态:
sudo systemctl status php7.4-fpm
,确保其
active (running)
。然后检查其监听方式:
sudo cat /etc/php/7.4/fpm/pool.d/www.conf | grep -E "listen|listen.owner"
,应看到
listen = /var/run/php/php7.4-fpm.sock
和
listen.owner = www-data
。这与 Nginx 配置中的
fastcgi_pass
路径完全匹配。接着,创建一个测试文件:
sudo vim /var/www/html/info.php
,写入
<?php phpinfo(); ?>
。保存后,访问
http://localhost/info.php
,如果页面正常显示 PHP 信息,则说明 Nginx -> PHP-FPM -> PHP 解释器的链路已打通。此时,滚动页面查找
Loaded Configuration File
,确认路径为
/etc/php/7.4/fpm/php.ini
;查找
extension_dir
,确认值为
/usr/lib/php/20190902
(这是 PHP 7.4 的扩展目录)。> 关键细节:
php.ini
有两个关键参数必须调整。用
sudo vim /etc/php/7.4/fpm/php.ini
打开,搜索
upload_max_filesize
,改为
upload_max_filesize = 64M
;搜索
post_max_size
,改为
post_max_size = 128M
。这是为了支持大文件上传,如 WordPress 插件安装。改完后,
sudo systemctl restart php7.4-fpm
生效。
3.5 全链路联调与首个 PHP 应用:从 Hello World 到数据库连接
现在,所有组件都已安装并配置完毕,是时候进行终极验证了。第一步,创建一个简单的数据库连接测试脚本:
sudo vim /var/www/html/db_test.php
,内容如下:
<?php
$host = 'localhost';
$dbname = 'lemp_demo';
$username = 'lemp_user';
$password = 'StrongPass123!';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]);
echo "✅ 数据库连接成功!<br>";
// 创建一个测试表
$pdo->exec("CREATE TABLE IF NOT EXISTS test_table (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
echo "✅ 测试表创建成功!<br>";
// 插入一条记录
$stmt = $pdo->prepare("INSERT INTO test_table (name) VALUES (?)");
$stmt->execute(['LEMP Stack']);
echo "✅ 数据插入成功!<br>";
// 查询并显示
$result = $pdo->query("SELECT * FROM test_table ORDER BY id DESC LIMIT 1");
$row = $result->fetch();
echo "📝 最新记录 ID: {$row['id']}, 名称: {$row['name']}, 时间: {$row['created_at']}";
} catch (PDOException $e) {
echo "❌ 连接失败: " . $e->getMessage();
}
?>
这段代码涵盖了 PHP 连接 MySQL 的全部关键要素:DSN 字符串(含
charset=utf8mb4
)、PDO 异常模式、预处理语句防注入、UTF-8 字符集初始化。保存后,访问
http://localhost/db_test.php
。如果页面显示绿色勾号和最新记录信息,恭喜你,LEMP 栈已 100% 就绪!如果报错,最常见的原因是:1)密码错误(检查
lemp_user
密码是否输错);2)socket 路径不匹配(确认 Nginx 和 PHP-FPM 的 socket 文件路径完全一致);3)MySQL 用户权限不足(确认
GRANT
语句已执行且
FLUSH PRIVILEGES
)。此时,查看 Nginx 错误日志
sudo tail -f /var/log/nginx/error.log
和 PHP-FPM 日志
sudo tail -f /var/log/php7.4-fpm.log
,错误信息会直接指向问题根源。> 我的经验:第一次联调失败时,不要急于重装,而是养成“看日志”的肌肉记忆。95% 的问题,日志里第一行就写明了原因。
4. 进阶配置与性能调优:让 LEMP 真正扛住流量
4.1 Nginx 性能参数调优:worker 进程与连接数的科学计算
默认的 Nginx 配置(
worker_processes auto;
)在多核服务器上未必最优。
worker_processes
决定了 Nginx 启动多少个工作进程,每个进程可处理大量并发连接。一个科学的设置公式是:
worker_processes = CPU 核心数
。假设你的服务器是 4 核,应在
/etc/nginx/nginx.conf
的
main
块中显式设置:
worker_processes 4;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
}
worker_rlimit_nofile 65535
提升单个 worker 进程可打开的最大文件描述符数(Linux 中 socket 也属于文件描述符),
worker_connections 4096
表示每个 worker 进程最多处理 4096 个并发连接。那么,理论最大并发连接数 =
worker_processes × worker_connections = 4 × 4096 = 16384
。但这只是理论值,还需结合系统级限制:
sudo sysctl -w fs.file-max=100000
(临时提升系统总文件数限制),并写入
/etc/sysctl.conf
永久生效。
use epoll
是 Linux 下最高效的 I/O 多路复用机制,比
select
或
poll
性能高出数倍。此外,在
http
块中,添加缓存相关配置:
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
sendfile on
启用内核级零拷贝传输,大幅提升静态文件(CSS/JS/图片)发送效率;
tcp_nopush
和
tcp_nodelay
避免 Nagle 算法造成的微小延迟;
keepalive_timeout 65
设置长连接超时时间为 65 秒,减少 TCP 握手开销。这些参数无需死记硬背,只需理解其作用:它们共同目标是让 Nginx 在高并发下,用最少的 CPU 和内存,处理最多的请求。我曾在 8 核 16G 服务器上,将
worker_processes
从
auto
改为
8
,
worker_connections
从
1024
提升到
8192
,配合
sendfile
,静态文件 QPS 从 12000 提升至 28000,提升幅度超过 130%。
4.2 PHP-FPM 进程管理策略:动态 vs 静态模式的实战抉择
PHP-FPM 的进程管理策略(
pm
)直接影响内存占用和响应速度。Ubuntu 20.04 默认使用
pm = dynamic
(动态模式),其配置位于
/etc/php/7.4/fpm/pool.d/www.conf
。关键参数有:
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
pm.max_children
是最大子进程数,它决定了 PHP 能同时处理多少个请求。计算公式:
max_children ≈ (总内存 - 系统预留) / 每个 PHP 进程平均内存
。例如,4G 内存服务器,系统预留 1G,每个 PHP 进程平均占用 30MB,则
max_children ≈ (3072) / 30 ≈ 102
。但保守起见,设为 50 更安全。
start_servers
是启动时创建的子进程数,
min_spare_servers
和
max_spare_servers
定义了空闲进程的上下限,FPM 会自动增减进程以维持此区间。
pm.max_requests = 500
表示每个子进程处理 500 个请求后自动重启,这是防止内存泄漏的黄金参数。另一种模式是
pm = static
(静态模式),所有参数都固定:
pm = static
pm.max_children = 20
它适用于内存充足、请求量稳定且可预测的场景,避免了进程创建销毁的开销。但若
max_children
设得过高,内存可能被 PHP 进程吃光,导致系统 OOM(Out of Memory)被杀。我的建议是:中小流量网站(日 PV < 10 万)用
dynamic
,大流量或内存充足的服务器可尝试
static
,但必须配合监控。无论哪种模式,都必须设置
pm.max_requests
,否则长时间运行的 PHP 进程会因内存碎片化导致响应变慢。> 实测对比:在 WordPress 站点上,
dynamic
模式下,高峰时段内存占用在 1.2G~1.8G 波动;
static
模式下,固定占用 1.5G,但首字节响应时间(TTFB)平均快 15ms。选择取决于你的优先级:是内存稳定性,还是极致响应速度?
4.3 MySQL 查询缓存与 InnoDB 优化:告别“慢查询”魔咒
MySQL 8.0 已彻底移除查询缓存(Query Cache),但这不意味着可以忽视缓存。我们必须转向更高效的 InnoDB 缓冲池(Buffer Pool)优化。InnoDB 缓冲池是 MySQL 最重要的内存区域,它缓存数据页和索引页。其大小应占服务器总内存的 50%~75%。在
/etc/mysql/mysql.conf.d/mysqld.cnf
中,找到
[mysqld]
块,添加:
innodb_buffer_pool_size = 1G
innodb_buffer_pool_instances = 8
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 1
假设服务器有 2G 内存,
innodb_buffer_pool_size = 1G
是合理起点。
innodb_buffer_pool_instances = 8
将缓冲池划分为 8 个实例,减少并发访问时的锁争用,提升多核 CPU 利用率。
innodb_log_file_size
是事务日志(redo log)大小,增大它可减少日志写入频率,提升写入性能,但恢复时间会略长。
innodb_flush_log_at_trx_commit = 1
是最安全的设置,每次事务提交都强制刷盘,保证数据不丢失(ACID 中的 Durability)。如果你的应用对写入性能要求极高且能接受极小概率的数据丢失(如日志系统),可设为
2
(每秒刷盘一次)。另一个关键优化是连接数:
max_connections = 200
(默认 151),避免高并发时连接被拒绝。修改后,必须重启 MySQL:
sudo systemctl restart mysql
。重启时,MySQL 会自动重建 redo log 文件,因此
innodb_log_file_size
修改后首次启动会稍慢。> 注意事项:修改
innodb_buffer_pool_size
后,务必监控
Innodb_buffer_pool_read_requests
和
Innodb_buffer_pool_reads
两个状态变量。前者是逻辑读请求数,后者是物理读(磁盘 IO)次数。计算缓存命中率:
(1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100%
。理想值应 > 99%。如果低于 95%,说明缓冲池太小,需要增大。
4.4 HTTPS 强制跳转与 SSL 证书部署:Let's Encrypt 的零成本实践
现代 Web 站点必须启用 HTTPS,这不仅是安全要求,更是 SEO 和浏览器信任的硬性指标。Ubuntu 20.04 下,使用 Certbot(Let's Encrypt 客户端)部署免费证书是最优解。首先安装 Certbot:
sudo apt install certbot python3-certbot-nginx -y
。然后,确保你的域名已正确解析到服务器 IP,并在 Nginx 配置中为该域名设置了
server_name
。例如,在
/etc/nginx/sites-available/your-site
中:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/html;
index index.php;
# ... 其他配置
}
接着,执行命令申请并自动配置证书:
sudo certbot --nginx -d example.com -d www.example.com
。Certbot 会自动:1)验证域名所有权(通过在
.well-known/acme-challenge/
下放置验证文件);2)向 Let's Encrypt 申请证书;3)修改 Nginx 配置,添加 443 端口监听和 SSL 相关指令;4)设置 HTTP 到 HTTPS 的 301 永久重定向。整个过程全自动,无需手动编辑任何配置文件。证书有效期为 90 天,Certbot 会自动添加一个 systemd 定时任务(
/etc/systemd/system/timers.target.wants/certbot.timer
),每周日凌晨 12:00 自动续期。你可以手动测试续期:
sudo certbot renew --dry-run
。如果一切顺利,访问
https://example.com
就能看到绿色锁标志。> 关键技巧:Certbot 的
--nginx
插件非常智能,但它依赖 Nginx 配置的规范性。如果你的
server
块中有复杂的
if
判断或
rewrite
规则,Certbot 可能无法正确插入重定向。此时,可先用
--standalone
模式(临时停用 Nginx)申请证书,再手动配置 Nginx 的 SSL 块。但绝大多数标准配置,
--nginx
插件都能完美胜任。
5. 常见问题排查与独家避坑指南:那些文档里不会写的真相
5.1 “502 Bad Gateway” 故障的七层穿透式诊断法
502 Bad Gateway
是 LEMP 环境中最令人抓狂的错误,它意味着 Nginx 作为网关,无法从上游(PHP-FPM)获得有效响应。但它的成因千差万别,必须建立一套系统化的排查流程。第一层:确认 PHP-FPM 服务状态。
sudo systemctl status php7.4-fpm
,如果显示
inactive (dead)
,直接
sudo systemctl start php7.4-fpm
。第二层:检查 PHP-FPM 是否在监听。
sudo ss -tlnp | grep php
,应看到
php-fpm: pool www
进程监听
unix:/var/run/php/php7.4-fpm.sock
。如果看不到,检查
/etc/php/7.4/fpm/pool.d/www.conf
中
listen
路径是否与 Nginx 配置一致。第三层:验证 socket 文件权限。
ls -l /var/run/php/php7.4-fpm.sock
,输出应类似
srw-rw---- 1 www-data www-data
。如果属主不是
www-data
,执行
sudo chown www-data:www-data /var/run/php/php7.4-fpm.sock
。第四层:检查 Nginx 错误日志。
sudo tail -50 /var/log/nginx/error.log
,常见错误如
connect() to unix:/var/run/php/php7.4-fpm.sock failed (111: Connection refused)
,说明 PHP-FPM 没启动;
connect() to unix:/var/run/php/php7.4-fpm.sock failed (13: Permission denied)
,说明权限不对。第五层:检查 PHP-FPM 错误日志。
sudo tail -50 /var/log/php7.4-fpm.log
,可能看到
WARNING: [pool www] child 12345 exited on signal 11 (SIGSEGV)
,这是 PHP 进程段错误,通常由扩展冲突或内存不足引起。第六层:检查系统资源。
free -h
看内存是否耗尽,
df -h
看磁盘是否写满(尤其是
/var/log
),
ulimit -n
看文件描述符是否达到上限。第七层:终极手段——手动触发。
sudo -u www-data /usr/bin/php-cgi -v
,如果报错
Segmentation fault
,基本可断定是 PHP 本身或某个扩展损坏,需重装
php7.4-fpm
。> 我的独家心得:90% 的 502 问题,都在前三层解决。养成
status -> ss -> ls -l
三连查的习惯,比盲目重启服务高效十倍。
5.2 “PHP 页面显示源码,不解析” 的元凶与根治方案
当访问
.php
文件时,浏览器直接下载或显示 PHP 代码,而不是执行,这说明 Nginx 根本没有将请求转发给 PHP-FPM。根本原因只有一个:Nginx 的
location ~ \.php$
块未生效或配置错误。排查步骤:1)确认该
location
块是否存在于你正在使用的
server
块

957

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



