LNMP这套组合,我这几年少说从头编译过十几次,踩过的坑比装过的系统都多。前阵子帮朋友把一套PHP项目从旧服务器迁到全新CentOS 7上,又完整走了一遍Nginx + MySQL + PHP的部署流程,趁热把笔记整理出来。这篇文章会从方案选型讲起,一直聊到编译参数、联动配置、故障排查和基础加固,全部基于我实际执行过的步骤,不是抄文档那种。无论你是正在学习Linux运维的入门者,还是需要自己搭环境跑项目的开发者,照着这套思路走一遍,基本能把LNMP这块硬骨头啃下来。
1. 环境部署思路:为什么我坚持LNMP组合和源码编译
1.1 LNMP与LAMP、面板工具之间的选择逻辑
很多人第一次搭Web环境时会纠结:到底选LNMP还是LAMP,还是干脆用宝塔面板一键装完?我个人的看法是,如果你只想快点把网站跑起来,用面板没有问题;但如果你想搞清楚每个组件在做什么、将来出问题能自己定位,LNMP手动部署是绕不开的一课。
LNMP代表Linux + Nginx + MySQL + PHP,而LAMP则是Linux + Apache + MySQL + PHP。两者最大的差异在Web服务这一层。Apache处理高并发时每个连接都会占用不少内存,而Nginx基于事件驱动模型,用少量进程就能撑起大量并发连接,静态文件处理能力尤其强。实际部署中,Nginx内存占用比Apache低很多,在2GB内存的云服务器上跑LNMP明显比LAMP更从容。加上现在绝大多数PHP框架,比如ThinkPHP、Laravel,都依赖Nginx的rewrite规则来实现优雅路由,Nginx事实上已经成为PHP项目的首选搭档。
至于为什么选CentOS 7而不是更新的系统,理由很直接:大量企业生产环境还在用CentOS 7,yum源里的软件包版本稳定,网上能查到的踩坑经验也最多。虽然它已经进入维护周期尾声,但对学习和小规模生产来说,资料丰富本身就是最大的优势。如果以后需要迁移到其他系统,LNMP的部署逻辑完全一样,只是包管理命令略有差异。
1.2 源码编译相比yum安装、面板安装的核心优势
同样是安装Nginx,yum install nginx一条命令就结束,为什么我还要下载源码去configure、make、make install折腾一个小时?
核心原因有三点。第一,编译安装的目录可控。yum安装会把文件散落在/etc、/usr/share、/var/log等各个目录,而编译安装时我可以指定统一的安装前缀,比如/usr/local/nginx,所有东西都在一个目录里,备份、迁移、卸载都非常清爽。第二,扩展模块可选。yum安装的Nginx默认只带固定模块,想启用第三方模块还得额外编译;PHP也一样,编译时想加上什么扩展、去掉什么扩展,主动权全在自己手里。第三,性能参数可以定制。比如PHP-FPM用什么用户运行、Nginx的pid文件放哪里、启不启用调试符号,这些在configure阶段就能定下来。
当然,编译安装不是没有代价。最大的问题就是耗时,一台双核服务器编译PHP至少要等十几分钟,而且遇到系统库版本不对时会报一堆错。所以我平时有个折中策略:Nginx和PHP用源码编译,MySQL直接使用官方编译好的二进制版本。MySQL源码编译时间太长、失败率太高,而官方二进制版本已经针对主流CPU做过优化,性能损耗微乎其微。这不是偷懒,而是把时间花在刀刃上的工程思维。
提醒一句:如果你在生产环境用编译安装,务必在configure阶段保留好当时的编译参数,最好写成脚本存档。否则半年后你想加一个扩展,却想不起来当时是怎么configure的,就只能对着源码目录发愣。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前准备:服务器规划、依赖库安装与目录约定
2.1 服务器配置建议与系统初始化操作
我推荐的最小配置是2核CPU + 2GB内存 + 40GB硬盘,这足以支撑一个日均PV几万的中小站点。如果内存只有1GB,编译PHP时极可能因为内存不足而进程被杀,这种情况我遇到过不止一次。解决办法是临时增加swap分区,或者编译时用make -j1限制并发,但体验都很痛苦。建议在部署前先检查内存和磁盘:
bash复制free -h
df -h
cat /etc/redhat-release
系统初始化阶段有几件小事必须做:关闭SELinux、配置好hostname、同步系统时间、创建部署用户。SELinux在CentOS 7上默认是 enforcing 状态,如果你不了解它的规则,Nginx访问PHP文件时经常出现莫名其妙的权限拒绝,日志里又看不出明显原因。直接用setenforce 0临时关闭,并修改/etc/selinux/config将SELINUX设为disabled,至少在部署排查阶段能省去大量干扰因素。
时间同步很多人忽略,但PHP会话、MySQL日志的时间戳都依赖系统时间。安装ntpdate后执行ntpdate ntp.aliyun.com把时间对准,防止后续看日志时被时区问题误导。
2.2 安装编译工具链与基础依赖库
源码编译需要一套完整的工具链,CentOS 7上通常这样装:
bash复制yum install -y gcc gcc-c++ make automake autoconf libtool
yum install -y pcre-devel zlib-devel openssl-devel libxml2-devel libcurl-devel libjpeg-devel libpng-devel freetype-devel libmcrypt-devel
这些依赖库各有用途:pcre-devel是Nginx rewrite模块的正则表达式底层依赖,缺了它configure直接报错;openssl-devel用于HTTPS证书支持,编译Nginx时必须指定;libxml2、libcurl、libjpeg、libpng、freetype这些是PHP编译GD库扩展、curl扩展时的依赖。很多人栽在最后一步,PHP编译时加了一堆扩展参数,结果系统里缺对应开发库,configure中途报错,还得回头补装,往返折腾。
如果你的yum源里找不到某些包,比如libmcrypt-devel,可以使用EPEL源:
bash复制yum install -y epel-release
yum install -y libmcrypt-devel
这一步在CentOS 7上很常见,装完EPEL源再装依赖就顺畅很多。
2.3 目录规划与日志拆分约定
我习惯把所有服务统一安装到/usr/local下,然后按目录做规划:
bash复制mkdir -p /data/wwwroot/default # Web站点根目录
mkdir -p /data/wwwlogs # 站点日志统一目录
mkdir -p /usr/local/nginx/conf/vhost # 虚拟主机配置文件目录
mkdir -p /server/backup # 备份目录
日志集中放在/data/wwwlogs而不是每个站点各放一处,排查问题时一条命令就能快速浏览所有站点的访问情况和错误日志。这个习惯最早是被一次故障逼出来的——当时两个项目日志散落在不同路径,出问题后找日志比修问题用的时间还长。从那以后,所有日志放同一目录成了我的硬性规定。
关于端口规划,也提前想好:Nginx监听80/443,PHP-FPM监听127.0.0.1:9000,MySQL监听127.0.0.1:3306。PHP-FPM和MySQL都只监听内网地址,不对外暴露,这是基本的安全意识。
3. 三大组件安装实操:Nginx、MySQL、PHP的具体步骤
3.1 Nginx 1.24源码编译过程
下载源码前先确认要安装的版本。生产环境建议选择主线稳定版,比如Nginx 1.24.x,不要追求最新,也不要停留在太老的版本。下载与解压:
bash复制cd /usr/local/src
wget https://nginx.org/download/nginx-1.24.0.tar.gz
tar -zxvf nginx-1.24.0.tar.gz
cd nginx-1.24.0
configure参数我长期使用这样一套:
bash复制./configure \
--prefix=/usr/local/nginx \
--user=www \
--group=www \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre
这些参数里,--user和--group指定Nginx worker进程的运行用户,这个用户需要提前创建好;--with-http_ssl_module开启HTTPS支持,缺失的话后面配置443端口会直接报错;--with-http_v2_module是HTTP/2支持,对性能优化有帮助;--with-http_stub_status_module提供Nginx运行状态监控页面,排查和监控时非常实用。
配置完成后执行:
bash复制make && make install
编译过程大概5到10分钟。完成之后做两件事:把nginx命令软链到/usr/bin下,方便全局执行;然后设置开机自启动。CentOS 7使用systemd来管理服务,我选择写一个nginx.service文件:
bash复制ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx
cat > /usr/lib/systemd/system/nginx.service <<'EOF'
[Unit]
Description=nginx service
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable nginx
这样以后就能用systemctl start nginx、systemctl restart nginx来管理服务,和原生服务没有区别。
3.2 MySQL 5.7二进制方式安装与初始化
MySQL我选择5.7官方二进制包。先创建mysql用户和目录:
bash复制groupadd mysql
useradd -r -g mysql -s /sbin/nologin mysql
mkdir -p /usr/local/mysql/data
chown -R mysql:mysql /usr/local/mysql
把下载好的mysql-5.7.42-linux-glibc2.12-x86_64.tar.gz解压到/usr/local/mysql目录下。然后修改/etc/my.cnf,这是我的常用配置:
ini复制[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/tmp/mysql.sock
port=3306
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
log-error=/data/wwwlogs/mysql_error.log
slow_query_log=1
slow_query_log_file=/data/wwwlogs/mysql_slow.log
long_query_time=2
max_connections=500
utf8mb4字符集是目前的主流选择,它可以完整支持emoji和生僻字。如果只用utf8,用户昵称里出现一个特殊符号就会导致写入失败。慢查询日志更是优化SQL的有力工具,打开后定期浏览,能发现大量隐藏的性能问题。
初始化数据目录:
bash复制/usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
注意5.7版本的初始化密码机制。用--initialize-insecure是生成空密码的root账户,初始化后直接登录再设置密码;用--initialize则会在错误日志里生成一个临时密码,第一次登录时必须使用那个密码。我平时用--initialize-insecure,省去翻日志找临时密码的环节。
启动MySQL后设置root密码并创建普通业务账号:
bash复制systemctl start mysqld
mysql -u root
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的强密码';
CREATE USER 'appuser'@'localhost' IDENTIFIED BY '应用连接密码';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
这里有个细节值得注意:业务账号不应该直接使用root连接数据库,这是最基本的安全原则。即使PHP应用和MySQL在同一台机器上,也应该为每个应用单独创建账号,只授予这个应用需要的库和权限。
3.3 PHP 8.2编译安装与FPM配置
PHP选择8.2版本,性能相比PHP 5.6/7.x有明显提升,主流框架兼容性也很好。下载源码后进入解压目录,我的configure参数是:
bash复制./configure \
--prefix=/usr/local/php \
--with-config-file-path=/usr/local/php/etc \
--with-fpm-user=www \
--with-fpm-group=www \
--enable-fpm \
--enable-mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-mysql-sock=/tmp/mysql.sock \
--with-gd \
--with-curl \
--with-openssl \
--with-zlib \
--with-freetype \
--with-jpeg \
--with-png \
--enable-mbstring \
--enable-zip \
--enable-opcache \
--enable-sockets \
--enable-gd \
--enable-bcmath
把这些参数拆开理解一下。--enable-fpm是PHP-FPM的开关,PHP本身是一个命令行解释器,要配合Nginx处理Web请求就必须开启FPM模式;--with-mysqli和--with-pdo-mysql都使用mysqlnd驱动,这是官方推荐的MySQL连接驱动,性能好且不需要额外装libmysqlclient;--with-gd、--with-freetype、--with-jpeg这些是图片处理扩展,使用GD库生成验证码、裁剪图片等功能都依赖它们;--enable-opcache是PHP字节码缓存,强烈建议开启,能显著减少PHP响应时间。
编译PHP比Nginx慢很多,建议执行:
bash复制make -j2 && make install
-j2表示用两个并行任务编译,能缩短一些编译时间。但如果内存只有1GB,建议老老实实make单线程跑,并行编译导致的OOM我已经经历过无数回,每次都是编译到一半进程被kill,前功尽弃。
编译完成后复制配置文件:
bash复制cp php.ini-production /usr/local/php/etc/php.ini
cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf
php.ini里最值得修改的两个参数是upload_max_filesize和post_max_size。默认值只有2MB,实测上传大文件时会被匿名卡住,建议直接改成50M:
ini复制upload_max_filesize = 50M
post_max_size = 50M
PHP-FPM的www.conf里需要确认listen、listen.allowed_clients和user参数正确,我的配置如下:
bash复制user = www
group = www
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm参数是PHP-FPM的进程管理模式。dynamic模式根据请求量动态调整进程数,适合大多数场景。pm.max_children是最大进程数,取值要结合服务器内存来算——常规PHP进程占用约30MB内存,2GB服务器最多跑50个左右。如果设置过大,高峰期内存会直接被打爆;设置过小,并发一高就开始排队。
启动PHP-FPM并设置开机自启:
bash复制/usr/local/php/sbin/php-fpm
cat > /usr/lib/systemd/system/php-fpm.service <<'EOF'
[Unit]
Description=php-fpm service
After=network.target
[Service]
Type=forking
PIDFile=/usr/local/php/var/run/php-fpm.pid
ExecStart=/usr/local/php/sbin/php-fpm
ExecReload=/bin/kill -USR2 $MAINPID
ExecStop=/bin/kill -QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable php-fpm
4. 联动配置:Nginx与PHP-FPM、MySQL的协同工作
4.1 配置Nginx解析PHP请求的完整server块
组件都安装好后,最关键的一步是把它们串联起来。Nginx的默认配置文件在/usr/local/nginx/conf/nginx.conf,主配置文件里需要include虚拟主机目录:
nginx复制http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /data/wwwlogs/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
include vhost/*.conf;
}
在/usr/local/nginx/conf/vhost目录下创建默认站点的配置文件default.conf:
nginx复制server {
listen 80;
server_name localhost;
root /data/wwwroot/default;
index index.php index.html;
access_log /data/wwwlogs/default_access.log main;
error_log /data/wwwlogs/default_error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
root /data/wwwroot/default;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
这里最关键的是location ~ .php$这个块。它的作用是拦截所有以.php结尾的请求,交给FastCGI接口处理。fastcgi_pass指定PHP-FPM的监听地址,fastcgi_param SCRIPT_FILENAME告诉PHP去执行哪个文件。很多新手遇到的"PHP文件被浏览器下载"或者"直接显示源码",基本都是这个配置缺失或参数写错导致的。
4.2 静态文件与动态请求分离,以及框架路由配置
实际项目中,图片、CSS、JS这些静态文件不应该进入PHP-FPM,否则白白消耗PHP进程资源。我的习惯是在server块中显式声明静态文件的过期时间:
nginx复制location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
expires 7d;
access_log off;
}
location ~ .*\.(woff|woff2|svg|eot|ttf)$ {
expires 30d;
access_log off;
}
expires指令浏览器可以缓存这些文件7天或30天,再次访问时直接从本地缓存读取,能明显减轻服务器压力。access_log off则关掉了静态文件的访问日志,减少大量无效的日志写入。
如果你是部署ThinkPHP或Laravel这类使用前后端分离路由的框架,核心在于try_files这条指令:
nginx复制location / {
try_files $uri $uri/ /index.php?$query_string;
}
这条指令的含义是:如果请求的文件存在就直接返回,如果目录存在也直接返回,否则把请求重写到index.php并携带原始查询参数。这样用户访问任意路径,比如/order/detail/100,框架的URL路由都能正确解析,而不是返回404。部署ThinkPHP时我经常看到有人忘记配这条,结果除首页外所有页面全部404,原因其实就是rewrite规则缺失。
4.3 验证PHP与MySQL连接
写完配置后先检测Nginx配置语法,然后平滑重载:
bash复制/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload
接下来在站点根目录创建一个测试文件info.php:
php复制<?php
phpinfo();
浏览器访问http://服务器IP/info.php,如果能看到完整的PHP信息页面,说明Nginx到PHP-FPM的链路已经打通。这一步顺利的话,再用PDO测试MySQL连接:
php复制<?php
$pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=appdb', 'appuser', '密码');
echo $pdo->query('select version()')->fetchColumn();
能够输出MySQL版本号,说明三件套已经完整接通。我测试时习惯从以下几个方面确认:页面返回时间是否正常、PHP加载的扩展列表是否符合预期、MySQL字符集是否生效。测试完毕务必删除info.php,生产环境保留phpinfo页面等于向所有人敞开服务器信息。
上线前检查清单:PHP扩展是否齐全、站点目录权限是否为www用户、防火墙是否放行80和443端口、MySQL慢查询日志是否已开启。
5. 常见部署故障排查实录与速查清单
5.1 PHP页面显示白屏或直接被下载
遭遇过太多次这个情况,第一次遇到时花了一个小时才找到原因。页面直接显示源代码,典型症状是Nginx收到php请求后没有交给PHP-FPM,而是把它当成普通文本返回了。排查顺序按这三步走:
第一步,确认server块里有没有location ~ .php$这个配置,没有的话补上。第二步,确认fastcgi_pass的地址和PHP-FPM实际的listen地址一致。最常见的不一致是Nginx配置127.0.0.1:9000,而PHP-FPM的www.conf里listen写成了unix socket路径,两边各说各话,自然不通。第三步,执行php-fpm -t检查FPM配置是否有语法错误,然后重启PHP-FPM。
白屏情况则不同,大概率是PHP代码报错但错误展示被关闭了。临时在php.ini里打开display_errors = On,再刷新页面就能看到具体报错信息;定位问题后记得改回Off。如果连PHP-FPM都启动失败,直接看日志/usr/local/php/var/log/php-fpm.log,里面会写清楚是缺少扩展还是配置语法错误。
5.2 Nginx返回502 Bad Gateway的常见原因
502错误在LNMP环境里太有代表性了,含义是Nginx发出请求给FastCGI后端,但后端无响应或直接断连。常用的排查手段是短命令组合:
bash复制netstat -tlnp | grep 9000
ps aux | grep php-fpm
tail -n 30 /data/wwwlogs/php-fpm.log
如果9000端口根本没人监听,说明PHP-FPM没起来,直接启动即可。如果PHP-FPM正常但依然502,就要看日志有没有live connections或max_children相关的报错。这类报错的本质是PHP-FPM的进程数被打满,新请求排不上队。解决方法要么提高pm.max_children,要么检查是否有代码死循环导致请求不释放。我曾经在排查一个502问题时发现,有台服务器的PHP函数curl超时时间被设置成了0秒,接口一旦阻塞就永远占用进程,直接把进程池拖死。这类问题光扩进程数治标不治本,必须定位到底是什么请求在长时间占用。
5.3 MySQL连接失败与Access denied处理
连接MySQL报Access denied,原因基本锁定在两方面:一是账号密码错误,二是账号允许登录的主机范围不对。开发机上用的习惯不同,测试数据库时这边用root、那边用root,到了线上就容易出现权限没授权的低级问题。
最有效的做法是在MySQL里明确查看账号权限:
sql复制SELECT user, host, authentication_string FROM mysql.user;
SHOW GRANTS FOR 'appuser'@'localhost';
如果业务程序通过127.0.0.1连接数据库,但授权时写的是'localhost',在某些场景下会因socket连接和TCP连接解析差异导致认证失败。我的建议是授权时把localhost和127.0.0.1都配上,避免这类歧义。另外,PHP配置里的数据库密码如果包含特殊字符,要注意框架配置文件的写法是否把特殊字符转义了,否则密码会多出一截导致认证失败。
5.4 编译阶段的常见报错速查表
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
| configure: error: the HTTP rewrite module requires the PCRE library | 缺少pcre-devel | yum install pcre-devel |
| configure: error: SSL modules require the OpenSSL library | 缺少openssl-devel | yum install openssl-devel |
make: *** No rule to make target libphp7.so |
PHP编译中断不完整 | 清空源码目录重新编译 |
| /usr/bin/ld: cannot find -lcurl | 缺少libcurl-devel | yum install libcurl-devel |
| virtual memory exhausted: Cannot allocate memory | 内存不够 | 增加swap或改用make -j1 |
编译报错看着五花八门,底层逻辑其实就两类:依赖库缺失和资源不足。每次报错,优先看看错误信息里有没有相关包的名字,然后直接搜centos 7 + 这个包名,基本都能快速定位。
6. 部署后的性能优化与基础安全加固
6.1 PHP OpCache与Nginx Gzip的效果对比
LNMP环境装好只算完成了一半,优化才是决定站点体验的关键环节。OpCache在php.ini中的常用配置:
ini复制opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
OpCache的原理是把PHP字节码缓存到共享内存里,避免每次请求都重新解析和编译PHP文件。我的实测经验是开启之后,PHP响应时间普遍能下降30%到50%。这里有个注意事项:revalidate_freq=60意味着PHP文件修改后最多需要60秒才生效。如果在开发环境频繁改代码,建议设为0或直接关闭OpCache,否则每次改完代码都要等一分钟才看到效果,效率极低。
Nginx的gzip压缩开启后效果直观:
nginx复制gzip on;
gzip_min_length 1k;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
gzip_min_length设置为1k,太小文件压缩收益不高反而浪费CPU;comp_level的取值5是平衡点,设到9时压缩率提升有限但CPU开销翻倍。对于纯文本类资源,开启gzip后传输体积能减少60%以上,页面加载速度的提升体感明显。
6.2 目录权限、防火墙与MySQL远程访问安全
安全加固上我形成了一套固定动作。部署完成后立即执行:
bash复制chown -R www:www /data/wwwroot/default
find /data/wwwroot/default -type d -exec chmod 755 {} \;
find /data/wwwroot/default -type f -exec chmod 644 {} \;
chmod -R 755 /usr/local/nginx /usr/local/php
这套权限设计的核心思路是:PHP-FPM以www用户运行,站点目录必须归www所有,否则程序无法读写;所有文件和目录使用常规权限,不随意给777。我见过不少人图省事直接chmod -R 777整个网站目录,结果被上传木马后WebShell一执行,整个站被掏空。
MySQL层面,确认端口不对外暴露:
bash复制netstat -tlnp | grep 3306
如果监听地址是0.0.0.0,立刻修改my.cnf里的bind-address=127.0.0.1。同时删除MySQL安装自带的匿名账号和空密码账号,这两条习惯救过我太多次。
6.3 部署验证清单与并发测试方法
全部收尾后,用ab工具做一次简单的压测确认整体可用性:
bash复制yum install -y httpd-tools
ab -n 1000 -c 100 http://127.0.0.1/index.php
重点关注两个指标:Failed requests是否为0,Requests per second的数值是否合理。如果出现大量失败或耗时异常,回头检查PHP-FPM进程数和Nginx配置。
最后是部署检查清单,我每搭完一套环境都会逐项过一遍:
- 服务器时间是否准确,时区是否为Asia/Shanghai
- SSH是否禁用了root密码登录,是否改用密钥登录
- Nginx是否开启了Gzip和静态文件缓存
- PHP是否开启了OpCache,错误展示是否已关闭
- MySQL是否设置了强密码,远程端口是否已封禁
- 站点目录权限是否为www用户所有
- 系统防火墙是否只放行了必要端口
- 是否安装并配置了fail2ban防暴力破解
7. 写在最后:部署LNMP我养成的一些习惯
经过这么多次部署,我形成了几个固定习惯,对稳定性和排障效率都有很大帮助。第一,重大配置修改前先备份原文件,备份名带日期,比如nginx.conf.bak-20240520,改动出问题随时回退。第二,所有操作按顺序记录到备忘录,尤其是configure参数和授权的SQL命令,半年后要加模块或者迁移服务器时,翻自己笔记比重新摸索快得多。第三,每次部署都顺手写一个部署脚本,把依赖安装、目录创建、编译参数固化下来,下次部署新服务器时直接跑一遍再手动微调,省掉大量重复劳动。
LNMP这套东西说难不难,说简单也不简单。难的是每个组件都有自己的一套逻辑,任何一个环节对不上就全盘卡住;简单的是只要搞懂每个配置项背后到底在做什么,所有问题都能用同一种排查思路解决。如果你准备自己动手搭一套,这个过程中多花一点时间读报错日志、理解参数含义,比任何一步快进都值得。
