Ubuntu 18.04 LEMP环境搭建:Nginx+PHP-FPM+MySQL 5.7生产部署指南
1. 项目概述:在 Ubuntu 18.04 上构建稳定可用的 LEMP 生产级环境
LEMP 这个词,第一次听到时我也愣了一下——它不是 Linux、Apache、MySQL、PHP 的缩写(那是 LAMP),而是把 Apache 换成了 Nginx,取其首字母 N 的发音 /ɛn/,拼成 LEMP。这个细节背后其实藏着一个很实际的工程判断:当你的服务器要扛住每秒几百甚至上千并发请求,尤其是静态资源分发、反向代理、负载均衡这些重活,Nginx 的事件驱动异步非阻塞模型,比 Apache 的多进程/多线程模型更省内存、响应更快、连接数上限更高。我最早在给一家本地教育机构部署在线题库系统时踩过坑:用 Apache 跑 PHP-FPM,300 人同时刷题,服务器内存直接飙到 95%,Nginx 同配置下只到 62%。所以今天这篇,不是照着官网文档念一遍“sudo apt install nginx”,而是带你从零开始,在 Ubuntu 18.04 这个已被长期验证、企业级部署中仍大量存在的 LTS 版本上,亲手搭一套真正能上线、能运维、能排查问题的 LEMP 环境。它面向的是刚接触 Linux 服务部署的开发者、运维新人,或是需要快速交付一个 Web 后端基础环境的全栈工程师。你不需要会写 Shell 脚本,但得知道 sudo 是干什么的;你不用精通 TCP/IP 协议栈,但得明白 80 端口和 3306 端口分别对应什么服务。整套流程我已在三台不同配置的物理机和虚拟机上实测过:一台是 2 核 4G 内存的阿里云轻量应用服务器,一台是 VMware Workstation 里配了 4G 内存的 Ubuntu 18.04 虚拟机,还有一台是公司内网老旧的 Dell T3600 工作站(i7-3770 + 16G 内存)——全部一次通过,没有依赖任何第三方 PPA 源或编译安装,全程使用 Ubuntu 官方仓库的稳定包,确保可复现、可审计、可交接。
Ubuntu 18.04 的生命周期虽然已于 2023 年 4 月结束标准支持,但它仍是很多存量生产环境、嵌入式设备管理后台、内部工具平台的基石。它的软件包版本非常“克制”:Nginx 是 1.14.0,MySQL 是 5.7.33,PHP 是 7.2.24。这些版本不是最新,但胜在极度稳定,漏洞修复及时,且与大量遗留 PHP 应用(比如老版本的 WordPress、Drupal、自研 CMS)兼容性极佳。很多人一上来就想装 PHP 8.x 或 MySQL 8.0,结果发现某个用了十几年的财务报表插件直接报错退出——这不是技术落后,而是工程现实。所以这篇教程的核心价值,不在于教你“怎么装最新版”,而在于教会你“怎么装一个真正能用、出了问题有迹可循、团队成员都能接手维护的版本”。接下来的所有步骤,我都将明确告诉你:为什么选这个命令而不是那个命令?为什么这个配置项不能删?为什么这个防火墙规则必须加?每一个选择背后,都是我在过去十年里,在上百个项目现场反复验证过的经验。
2. 整体设计思路与方案选型逻辑
2.1 为什么坚持用 Ubuntu 18.04 官方源,而不是升级到 20.04/22.04 或手动编译?
这个问题我被问过太多次。答案很直白: 可控性优先于先进性 。Ubuntu 18.04 的 APT 包管理器(Advanced Package Tool)经过了长达五年的高强度生产环境锤炼,它的依赖解析、冲突处理、回滚机制都极其成熟。当你执行 sudo apt install nginx mysql-server php-fpm 时,APT 不仅会下载软件包,还会自动解决所有底层依赖(比如 libpcre3 、 libssl1.1 、 php-common ),并确保它们的版本号彼此兼容。我见过太多人为了追求“新”,用 wget 下载 Nginx 源码, ./configure --prefix=/usr/local/nginx --with-http_ssl_module 编译安装,结果因为 OpenSSL 版本不匹配,导致 HTTPS 证书加载失败,查了三天日志才发现是 libssl.so.1.0.0 和 libssl.so.1.1 的符号冲突。官方源的包,哪怕版本旧一点,但所有 .so 动态库、配置文件路径、systemd 服务单元文件,都是经过 Canonical 公司 QA 团队统一测试的。更重要的是,Ubuntu 18.04 的 /etc/apt/sources.list 文件结构清晰,主仓库(main)、受限仓库(restricted)、社区维护仓库(universe)、非自由软件仓库(multiverse)分工明确,你一眼就能看出 nginx 在 universe 里, mysql-server 在 main 里,这种确定性对运维来说就是安全感。
再看升级这条路。Ubuntu 18.04 升级到 20.04 是 LTS 到 LTS 的“就地升级”,理论上可行,但实操中风险极高。我亲身经历的一个案例:某政务服务平台,运维同事按官方指南执行 do-release-upgrade -d ,升级过程中网络抖动导致 apt 下载中断,系统卡在一半, /var/lib/dpkg/status 文件损坏,最终不得不重装系统,业务停摆 6 小时。而手动编译,看似“掌控一切”,实则把所有底层细节都甩给了你自己。Nginx 的 --with-http_v2_module 是否开启?PHP 的 --enable-opcache 是否启用?MySQL 的 innodb_buffer_pool_size 初始值设多少?这些参数没有标准答案,全靠经验调优。对于一个刚入门的工程师,与其花三天时间研究 configure 参数,不如用官方包跑起来,先让业务跑通,再根据监控数据(比如 htop 看内存、 mysqladmin processlist 看慢查询)去针对性优化。所以,本方案的设计哲学是: 用最保守的安装方式,换取最高的启动成功率和最低的初期维护成本 。
2.2 为什么选择 Nginx + PHP-FPM 组合,而不是 Apache + mod_php?
这涉及到 Web 服务器处理 PHP 请求的根本机制差异。Apache 的 mod_php 是把 PHP 解释器直接编译进 Apache 进程里,每个 Apache 子进程都自带一个 PHP 运行时。好处是简单, a2enmod php7.2 一行命令搞定;坏处是内存开销巨大,且 PHP 进程生命周期完全绑定 Apache,无法独立重启、无法单独做性能分析。Nginx 本身是一个纯粹的 HTTP 服务器和反向代理,它不内置 PHP 解释器,而是通过 FastCGI 协议,把 PHP 请求转发给一个独立的、专门负责执行 PHP 的服务——这就是 PHP-FPM(PHP FastCGI Process Manager)。你可以把 PHP-FPM 想象成一个“PHP 工厂”,Nginx 是“订单接收员”,用户发来一个 index.php 请求,Nginx 把这个“订单”转给 PHP-FPM,PHP-FPM 里的工人(worker 进程)处理完,再把结果(HTML 页面)交还给 Nginx,由 Nginx 返回给用户。
这个分离架构带来了三个关键优势。第一是 资源隔离 :Nginx 进程只负责网络 I/O 和静态文件服务,内存占用极低(通常 < 10MB);PHP-FPM 进程只负责 CPU 密集型的脚本执行,你可以单独为它设置最大子进程数( pm.max_children )、空闲超时时间( pm.process_idle_timeout ),避免 PHP 内存泄漏拖垮整个 Web 服务。第二是 弹性伸缩 :当 PHP 处理变慢时,你只需调整 PHP-FPM 的 pm.start_servers 和 pm.max_spare_servers ,无需重启 Nginx;反之,如果 Nginx 遇到大量静态文件请求,你也可以单独优化它的 sendfile 和 tcp_nopush 参数。第三是 安全加固 :PHP-FPM 支持为每个网站(vhost)配置独立的 Unix Socket 文件(如 /run/php/php7.2-fpm-siteA.sock )和运行用户( user = www-data-siteA ),实现严格的权限隔离。一个网站的 PHP 代码被黑,攻击者最多只能拿到 www-data-siteA 用户的权限,无法影响其他网站。而 mod_php 下,所有网站共享同一个 www-data 用户,风险是全局性的。因此,本方案采用 nginx + php7.2-fpm 的组合,是经过权衡后的最优解。
2.3 MySQL 5.7 的选型依据与安全基线设定
Ubuntu 18.04 仓库中的 MySQL 是 5.7.33,这是一个非常成熟的版本。它引入了 JSON 数据类型、生成列(Generated Columns)、原生的 GROUP_REPLICATION 插件,但又避开了 MySQL 8.0 中一些颠覆性的变更,比如默认认证插件从 mysql_native_password 改为 caching_sha2_password ,这个改动曾让无数 PHP 的 mysqli 扩展连接失败,报错 Client does not support authentication protocol requested by server 。5.7.33 的另一个关键优势是它的 InnoDB 引擎稳定性。我们线上一个日均百万 PV 的电商后台,数据库表超过 200 张,其中 order_detail 表数据量达 1.2 亿行,连续运行三年未出现一次 InnoDB 崩溃或页损坏。这得益于 5.7 对 innodb_log_file_size 、 innodb_buffer_pool_instances 等核心参数的精细调优空间。
在安全方面,MySQL 5.7 的 mysql_secure_installation 脚本是必跑的。它会强制你为 root 用户设置强密码、删除匿名用户、禁止 root 远程登录、删除 test 数据库。很多人跳过这一步,觉得“反正内网用”,结果被扫描器扫到, root@'%' 账户被爆破,整个数据库被加密勒索。本方案将严格遵循 CIS(Center for Internet Security)发布的 MySQL 5.7 基线检查清单,重点加固以下几点:一是将 bind-address 从 0.0.0.0 改为 127.0.0.1 ,确保 MySQL 只监听本地回环地址,外部网络根本访问不到;二是创建专用的应用数据库用户(如 app_user ),并授予最小权限( GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app_user'@'localhost' ),绝不使用 root;三是启用 log_error_verbosity = 3 ,记录详细的错误日志,便于事后审计。这些不是“可选项”,而是上线前的强制动作。
3. 核心细节解析与实操要点
3.1 系统初始化:更新、防火墙与基础工具安装
在安装任何服务之前,必须先让系统处于一个干净、安全、可追溯的状态。这一步看似简单,却是后续所有操作稳定的基石。首先,执行 sudo apt update && sudo apt upgrade -y 。这里要注意, apt update 是刷新本地的软件包索引缓存,它会从 /etc/apt/sources.list 里列出的镜像源下载最新的 Packages.gz 文件;而 apt upgrade 是根据这个新索引,去升级已安装的软件包。我见过太多人只执行 apt upgrade ,结果因为索引过期,升级了一个有严重安全漏洞的老版本 openssl ,却以为自己已经打上了补丁。所以, update 和 upgrade 必须成对出现,中间不能有间隔。
接着,启用 UFW(Uncomplicated Firewall),这是 Ubuntu 默认的防火墙前端。执行 sudo ufw enable ,然后立刻放行 SSH 端口: sudo ufw allow OpenSSH 。这一步至关重要。如果你是在远程服务器上操作,而忘了放行 SSH, ufw enable 之后,你的 SSH 连接会立即断开,再也连不上——因为 UFW 默认拒绝所有入站连接。 OpenSSH 是一个预定义的应用配置文件,它等价于 sudo ufw allow 22/tcp ,但更语义化。确认状态: sudo ufw status verbose ,你应该看到 Status: active 和 22/tcp (v6) 的条目。然后,为 LEMP 的核心端口放行: sudo ufw allow 'Nginx Full' 。这个 'Nginx Full' 配置文件会同时放行 80(HTTP)和 443(HTTPS)端口。注意,这里不要用 sudo ufw allow 80 这种裸端口写法,因为 UFW 的规则是按顺序匹配的, allow 规则在 deny 规则之后才生效,而 'Nginx Full' 是一个原子化的、经过测试的配置。
最后,安装几个必备的诊断工具: sudo apt install -y curl wget vim net-tools dnsutils 。 curl 和 wget 用于下载文件和测试 HTTP 请求; vim 是强大的文本编辑器,比 nano 更适合编辑复杂的配置文件(如 /etc/nginx/sites-available/default ); net-tools 提供 ifconfig 和 netstat ,用于查看网络接口和端口监听状态; dnsutils 提供 dig 和 nslookup ,用于排查 DNS 解析问题。特别提醒:Ubuntu 18.04 默认不安装 ifconfig ,很多新手会因此误以为网卡没起来,其实应该用 ip a ( ip addr show 的简写)来查看。但 net-tools 包里的 ifconfig 更符合大家的习惯,所以建议装上。这些工具看似琐碎,但在后续排查 Nginx 启动失败、MySQL 连接被拒、PHP-FPM socket 文件找不到等问题时,它们就是你的“听诊器”和“万用表”。
3.2 Nginx 安装与核心配置解析
安装 Nginx 极其简单: sudo apt install -y nginx 。安装完成后, systemd 会自动启动 nginx 服务,并设置为开机自启。验证是否成功: sudo systemctl status nginx ,你应该看到 active (running) 的状态。然后,在浏览器中输入服务器的 IP 地址(如 http://192.168.1.100 ),应该能看到 Nginx 的欢迎页面。如果看不到,第一步不是慌张,而是执行 sudo netstat -tuln | grep :80 ,检查 80 端口是否真的被 nginx 进程监听。如果没监听,说明服务没起来;如果监听了但浏览器打不开,那问题大概率出在防火墙或网络路由上。
Nginx 的核心配置文件位于 /etc/nginx/nginx.conf ,但直接修改它并不推荐。最佳实践是遵循 Nginx 的“模块化”设计思想:主配置文件只做全局设置(如 worker 进程数、日志格式),具体的网站配置放在 /etc/nginx/sites-available/ 目录下,然后通过软链接激活到 /etc/nginx/sites-enabled/ 。Ubuntu 18.04 默认提供了一个 default 配置文件,我们先把它备份: sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak 。然后,用 vim 编辑它: sudo vim /etc/nginx/sites-available/default 。
最关键的修改在 server 块内。找到 location / 这一段,它定义了根路径的处理规则。默认配置是 try_files $uri $uri/ =404; ,意思是“先找 $uri 对应的文件,找不到就找 $uri/ 对应的目录,都找不到就返回 404”。对于 PHP 应用,我们需要让它把 .php 结尾的请求交给 PHP-FPM 处理。所以,我们要添加一个 location ~ \.php$ 的块:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
这里有两个关键点。第一, snippets/fastcgi-php.conf 是一个预定义的片段文件,它包含了所有 FastCGI 的标准参数,比如 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; ,这个参数告诉 PHP-FPM,当前要执行的 PHP 脚本的完整路径是什么。如果你手动写一堆 fastcgi_param ,很容易漏掉关键项,导致 $_SERVER['SCRIPT_NAME'] 获取错误。第二, fastcgi_pass 指定了 PHP-FPM 的通信地址。Ubuntu 18.04 的 PHP-FPM 默认使用 Unix Socket( /run/php/php7.2-fpm.sock ),而不是 TCP 端口(如 127.0.0.1:9000 )。Unix Socket 的性能比 TCP 端口高 20%-30%,因为它绕过了网络协议栈,直接在内核内存中交换数据。而且,Socket 文件的权限可以精确控制( ls -l /run/php/ 会显示 srw-rw---- 1 root www-data ),只有 root 和 www-data 组的用户才能读写,安全性更高。确认 PHP-FPM 的 socket 文件存在,是排查“502 Bad Gateway”错误的第一步。
3.3 MySQL 5.7 的安全初始化与数据库创建
sudo apt install -y mysql-server 会自动运行 mysql_secure_installation 脚本,但很多新手在交互式提示中一路按回车,结果留下了安全隐患。我们必须手动、严谨地执行它。运行 sudo mysql_secure_installation ,它会依次询问:
- 设置密码强度验证插件 :输入
N。Ubuntu 18.04 的validate_password插件默认要求密码包含大小写字母、数字和特殊字符,长度至少 8 位。对于开发环境或内网测试环境,这个要求过于严苛,反而导致大家用Password123!这样的弱密码应付。我们选择禁用它,后续用更强的密码策略(如定期更换、不重复使用)来弥补。 - 为 root 用户设置密码 :务必输入一个强密码,并牢记。这是数据库的最高权限账户,绝不能留空。
- 删除匿名用户 :输入
Y。匿名用户(''@'localhost')没有任何用户名,任何人都可以不输入密码就登录,这是巨大的安全漏洞。 - 禁止 root 远程登录 :输入
Y。root用户只能从localhost登录,即只能在数据库服务器本机上用mysql -u root -p连接。外部应用必须使用专用的、权限受限的应用用户。 - 删除 test 数据库 :输入
Y。test数据库是 MySQL 安装时创建的示例库,没有任何实际用途,反而可能被利用来存放恶意数据。 - 重新加载权限表 :输入
Y。这会让前面的所有更改立即生效。
做完这些,我们就可以创建应用专属的数据库和用户了。执行 sudo mysql -u root -p ,输入刚才设置的 root 密码,进入 MySQL 命令行。然后,逐条执行:
-- 创建名为 'myapp' 的数据库,字符集用 utf8mb4,支持完整的 emoji
CREATE DATABASE myapp CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- 创建名为 'app_user' 的用户,密码为 'StrongPass123!',只允许从 localhost 连接
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongPass123!';
-- 授予 'app_user' 对 'myapp' 数据库的所有权限(生产环境应按需授予,如只给 SELECT,INSERT,UPDATE)
GRANT ALL PRIVILEGES ON myapp.* TO 'app_user'@'localhost';
-- 刷新权限,使授权立即生效
FLUSH PRIVILEGES;
这里的关键细节是 utf8mb4 字符集。MySQL 的 utf8 实际上是 utf8mb3 ,它最多只支持 3 字节的 Unicode 字符,无法存储 emoji(如 😂、👍)和一些生僻汉字(如 “𠮷”)。 utf8mb4 是真正的 UTF-8,支持 4 字节字符。 COLLATE = utf8mb4_unicode_ci 指定了排序规则, _ci 表示 case-insensitive(不区分大小写),这是大多数 Web 应用的默认需求。生产环境中, GRANT ALL PRIVILEGES 是过度授权,应该根据应用的实际 SQL 语句,精确授予 SELECT, INSERT, UPDATE, DELETE 中的必要权限。例如,一个只读的报表后台,只需要 SELECT ;一个用户注册接口,可能只需要 INSERT 到 users 表。最小权限原则,是数据库安全的黄金法则。
3.4 PHP 7.2 的安装、模块启用与 FPM 配置
PHP 的安装命令是 sudo apt install -y php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip 。这个命令里包含了所有现代 PHP Web 应用几乎必需的扩展。 php-fpm 是核心运行时; php-mysql 提供 MySQLi 和 PDO_MySQL 驱动,让 PHP 能连接 MySQL; php-curl 用于发起 HTTP 请求(如调用微信 API); php-gd 用于图像处理(生成验证码、缩略图); php-mbstring 提供多字节字符串处理函数( mb_strlen , mb_substr ),对中文处理至关重要; php-xml 和 php-xmlrpc 是 XML 解析和 RPC 调用的基础; php-soap 用于对接 SOAP 协议的 Web Service; php-intl 提供国际化支持(日期、货币格式化); php-zip 用于压缩和解压 ZIP 文件。
安装完成后,PHP-FPM 服务会自动启动。检查状态: sudo systemctl status php7.2-fpm 。它的主配置文件是 /etc/php/7.2/fpm/php.ini ,但绝大多数情况下,我们不需要修改它。真正需要关注的是池(Pool)配置文件 /etc/php/7.2/fpm/pool.d/www.conf 。这个文件定义了 PHP-FPM 如何管理它的 worker 进程。打开它: sudo vim /etc/php/7.2/fpm/pool.d/www.conf 。
找到 listen = /run/php/php7.2-fpm.sock 这一行,确认它和 Nginx 配置中的 fastcgi_pass 地址一致。然后,找到 user = www-data 和 group = www-data ,这是 PHP-FPM worker 进程以哪个系统用户身份运行。 www-data 是 Ubuntu 上 Nginx 和 PHP-FPM 默认的 Web 服务用户,所有网站文件的属主都应该设为 www-data ,这样才能保证 Nginx 能读取文件,PHP-FPM 能执行文件。再往下,找到 pm = dynamic ,这是进程管理方式。 dynamic 表示动态管理 worker 进程数,根据负载自动增减。下面的 pm.max_children = 5 、 pm.start_servers = 2 、 pm.min_spare_servers = 1 、 pm.max_spare_servers = 3 是关键参数。 max_children 是最大并发请求数,对于一台 2 核 4G 的服务器,5 是一个安全的起点; start_servers 是启动时创建的初始进程数; min_spare_servers 和 max_spare_servers 定义了空闲进程数的上下限。如果 max_spare_servers 设得太高,空闲进程会白白占用内存;设得太低,突发流量来临时,创建新进程会有延迟。我一般的经验法则是: max_children ≈ (总内存 * 0.7) / 每个 PHP 进程平均内存 。在 Ubuntu 18.04 上,一个空闲的 PHP-FPM 进程大约占用 20-30MB 内存,所以 4G 内存的服务器, max_children 设为 5-8 是合理的。
4. 实操过程与核心环节实现
4.1 完整安装流程:从系统到可访问的 PHP 页面
现在,让我们把前面所有的知识点串联起来,走一遍完整的、可复制的安装流程。请严格按照以下顺序执行,每一步都附带了验证方法。
第一步:系统更新与基础工具安装
# 更新软件包索引并升级所有已安装的包
sudo apt update && sudo apt upgrade -y
# 启用并配置 UFW 防火墙
sudo ufw enable
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
# 安装基础诊断工具
sudo apt install -y curl wget vim net-tools dnsutils
验证:执行
sudo ufw status numbered,你应该看到编号为 1 的规则是22/tcp ALLOW IN Anywhere,编号为 2 的规则是80,443/tcp ALLOW IN Anywhere。
第二步:安装 Nginx 并测试
# 安装 Nginx
sudo apt install -y nginx
# 检查 Nginx 状态
sudo systemctl status nginx
# 测试本地访问(在服务器本机上执行)
curl -I http://localhost
# 你应该看到 "HTTP/1.1 200 OK" 的响应头
验证:如果
curl返回200 OK,说明 Nginx 服务正常,且能正确响应 HTTP 请求。如果返回Connection refused,说明nginx服务没启动,执行sudo systemctl start nginx。
第三步:安装 MySQL 并进行安全初始化
# 安装 MySQL 服务器
sudo apt install -y mysql-server
# 运行安全脚本,按前述建议选择 Y/N
sudo mysql_secure_installation
验证:执行
sudo mysql -u root -p -e "SHOW DATABASES;",输入密码后,你应该能看到information_schema、mysql、performance_schema、sys这几个系统库,说明 MySQL 安装成功且 root 用户可用。
第四步:安装 PHP 及相关扩展
# 安装 PHP-FPM 和常用扩展
sudo apt install -y php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip
# 检查 PHP-FPM 状态
sudo systemctl status php7.2-fpm
验证:执行
sudo systemctl is-active php7.2-fpm,返回active即表示服务正在运行。
第五步:创建测试 PHP 页面并配置 Nginx
# 创建一个简单的 PHP 测试文件
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
# 编辑 Nginx 默认站点配置
sudo vim /etc/nginx/sites-available/default
在 server 块内,找到 location / ,在其下方添加:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
}
保存并退出。然后,测试 Nginx 配置语法:
sudo nginx -t
如果输出 syntax is ok 和 test is successful ,说明配置无误。最后,重启 Nginx 使配置生效:
sudo systemctl restart nginx
验证:在浏览器中访问
http://你的服务器IP/info.php。你应该能看到一个巨大的、包含所有 PHP 配置信息的页面。如果看到File not found.或502 Bad Gateway,请立即回到前面的步骤,检查fastcgi_pass地址是否正确、PHP-FPM 服务是否在运行、/var/www/html/info.php文件权限是否为644且属主为www-data。
4.2 关键配置文件详解与参数调优
一个稳定运行的 LEMP 环境,离不开对核心配置文件的深入理解。我们来逐个剖析。
Nginx 主配置 ( /etc/nginx/nginx.conf ) 的关键参数:
worker_processes auto;:设置工作进程数。auto表示自动检测 CPU 核心数并设置为相同数量。对于单核 CPU,设为 1;双核设为 2。过多的 worker 进程会增加上下文切换开销,过少则无法充分利用 CPU。worker_connections 768;:每个 worker 进程允许的最大并发连接数。768 是一个保守值,对于中小流量网站足够。计算公式是:最大并发数 = worker_processes * worker_connections。如果你的服务器有 4 核,那么理论最大并发是4 * 768 = 3072。keepalive_timeout 65;:客户端连接的 keep-alive 超时时间,单位秒。设为 65 秒,意味着如果客户端在 65 秒内没有发送新请求,Nginx 会主动关闭这个 TCP 连接,释放资源。太短(如 10 秒)会导致移动端频繁重建连接;太长(如 300 秒)会占用大量空闲连接。
PHP-FPM 池配置 ( /etc/php/7.2/fpm/pool.d/www.conf ) 的关键参数:
pm.max_requests = 1000:每个 worker 进程在处理完 1000 个请求后,会自动重启。这是一个重要的内存泄漏防护机制。PHP 脚本在执行过程中,如果存在未释放的资源(如大数组、未关闭的文件句柄),会导致内存缓慢增长。max_requests让进程定期“换血”,防止内存耗尽。request_terminate_timeout = 30s:单个 PHP 请求的最长执行时间。如果一个脚本卡死(比如无限循环),30 秒后 PHP-FPM 会强制终止它,避免拖垮整个服务。这个值必须小于 Nginx 的fastcgi_read_timeout(默认 60 秒),否则 Nginx 会先超时返回 504。slowlog = /var/log/php7.2-fpm-slow.log:慢日志文件路径。当一个请求的执行时间超过request_slowlog_timeout(默认 0,即关闭)时,会记录到这个文件。开启它,是定位性能瓶颈的利器。例如,设置request_slowlog_timeout = 5s,然后观察慢日志,就能快速发现哪些 SQL 查询或函数调用拖慢了整个网站。
MySQL 主配置 ( /etc/mysql/mysql.conf.d/mysqld.cnf ) 的关键参数:
bind-address = 127.0.0.1:这是安全底线。它强制 MySQL 只监听本地回环地址,外部网络无法通过 TCP/IP 连接到数据库。所有应用都必须通过localhost(这会触发 Unix Socket 连接)或127.0.0.1(这会触发 TCP 连接)来访问。max_connections = 100:MySQL 允许的最大并发连接数。100 是一个安全的默认值。你可以根据show status like 'Threads_connected';的峰值来调整。如果经常看到Threads_connected接近 100,就需要增大它,但也要考虑服务器内存,因为每个连接都会占用一定内存。innodb_buffer_pool_size = 128M:InnoDB 缓冲池大小,这是 MySQL 最重要的性能参数。它相当于 MySQL 的“内存硬盘”,用来缓存表数据和索引。对于一台 4G 内存的服务器,128M是一个起步值。理想值是物理内存的 50%-75%。所以,如果是 4G 内存,可以设为2G(即2048M)。但切记,不能设得太大,否则会挤占操作系统和其他服务的内存,导致系统频繁 swap,性能反而暴跌。
4.3 权限与文件所有权的终极实践
在 Linux 系统中,“谁拥有这个文件”和“谁可以执行这个文件”,直接决定了 Web 服务能否正常工作。这是一个极易被忽视,却又无比关键的环节。
首先,明确 www-data 用户的角色。它是 Ubuntu 为 Web 服务创建的专用系统用户,UID 通常是 33。Nginx 的 master 进程以 root 身份运行(为了绑定 80 端口),但它的 worker 进程,以及 PHP-FPM 的所有 worker 进程,都以 www-data 用户身份运行。这意味着, www-data 用户必须对网站文件拥有读取( .html , .css , .js )和执行( .php )权限。
标准的文件所有权设置如下:
# 将 /var/www/html 目录及其所有子文件、子目录的属主和属组都设为 www-data
sudo chown -R www-data:www-data /var/www/html
# 设置目录权限为 755(所有者可读写执行,组和其他人可读执行)
sudo find /var/www/html -type d -exec chmod 755 {} \;
# 设置文件权限为 644(所有者可读写,组和其他人只读)
sudo find /var/www/html -type f -exec chmod 644 {} \;
注意:
chmod 644对.php文件是安全的,因为 PHP-FPM 是以www-data身份执行的,它能读取这个文件。chmod 755对.php文件是多余的,甚至可能带来风险(如果文件被意外赋予了执行权限,可能会被当作二进制程序执行)。
一个常见的陷阱是:开发者在本地用 sudo vim 编辑了 /var/www/html/index.php ,结果这个文件的属主变成了 root 。此时, www-data 用户无法读取它,Nginx 就会返回 403 Forbidden 错误。排查方法很简单: ls -l /var/www/html/index.php ,如果看到 root root ,那就立刻 sudo chown www-data:www-data /var/www/html/index.php 。
另一个重要场景是上传目录。如果网站有用户头像上传功能,你需要一个 uploads 目录。这个目录必须对 `www
更多推荐
所有评论(0)