资讯动态

手动搭建LNMP架构:Nginx、MySQL与PHP-FPM全流程详解(含调优与排错)

发布时间:2026/10/15 9:36:09 来源:尧图企业网站定制
我最早接触LNMP架构用的是网上那种“一键脚本”。当时觉得这个组合真神奇一条命令下去PHP网站就能跑起来了。直到有一天线上站点突然502我连下一步该敲什么命令都不知道才决定把Nginx、MySQL、PHP-FPM每个组件都手动装一遍。这篇文章不教你怎么下载一键脚本而是把这三层之间如何通信、如何配置、如何调优彻底讲清楚整个搭建过程全手写。如果你想自己掌控服务器、想搞明白部署原理或者已经在用集成环境但遇到问题不会排查这篇流程应该能帮到你。1. 为什么生产环境普遍选LNMP而不是LAMP1.1 一次请求在LNMP栈里的完整流转先把最基本的东西说清楚一次用户访问在LNMP架构里到底发生了什么。这里我按实际请求路径拆一下用户在浏览器输入域名请求到达服务器80或443端口监听这个端口的是Nginx它先不看业务逻辑只根据请求的URI和后缀判断该交给谁处理如果请求的是/static/css/app.css这种静态文件Nginx直接读磁盘返回完全不经过PHP如果请求的是/index.php这样的PHP脚本Nginx就把请求通过FastCGI协议转发给PHP-FPM进程PHP-FPM执行PHP代码执行过程中脚本可能需要查询数据库于是通过MySQL驱动连接本机或远程的MySQL服务数据库返回结果给PHPPHP再把整段HTML渲染出来经PHP-FPM交回NginxNginx最终把响应数据返回给浏览器连接关闭或保持复用。这个流程看起来不复杂但实际配置时每一步都可能出问题。Nginx不知道把PHP请求交给谁、PHP-FPM配置文件里的运行用户不对、PHP连不上MySQL等等任何一个环节断了表现出来的症状五花八门但根本原因都得顺着这条链路去查。1.2 LNMP在工程上的取舍与适用边界先聊聊LNMP和LAMP的区别。同样是LinuxMySQLPHP差别主要在Web服务器和PHP的运行方式对比项LNMPLAMPWeb服务器NginxApachePHP运行方式PHP-FPM独立进程池mod_php模块嵌入Apache进程或PHP-FPM静态文件处理事件驱动高并发下优势明显进程/线程模型并发上限相对低动态请求隔离性PHP进程独立可以单独重启、单独调优与Apache耦合配置方式不同配置复杂度稍高需要理解FastCGI转发相对简单模块化加载常见场景高并发Web、反向代理、前后端分离传统虚拟主机、.htaccess依赖强的老项目Nginx的流行不是因为“快”这么简单。它的请求处理是事件驱动的一个worker进程能同时维护成千上万个连接配合PHP-FPM这种独立的FastCGI进程池PHP出问题最多影响PHP进程Nginx还能继续扛静态请求。这一点在LNMP里非常重要至少不会出现PHP一崩整个网站全挂的情况。当然LAMP也有自己的优势Apache对.htaccess的支持是最成熟的兼容性也最好。很多老项目直接搬到LNMP会遇到伪静态规则无法使用的问题需要把.htaccess规则翻译成Nginx的location语法。但以今天新项目的部署取向来说LNMP确实更符合现代Web服务的需求。2. 部署前的系统准备基础环境决定了后面顺不顺2.1 操作系统选型与软件源差异我以Ubuntu 22.04 LTS为例来讲因为它的软件源里直接就有Nginx、MySQL 8.0和PHP 8.1三条apt命令就能把环境装齐非常适合作演示和中小型项目。Red Hat系发行版如Rocky Linux、AlmaLinux其实也能完全复现这套流程只是包管理器和部分路径不同关键差异我会在后面标注出来。执行系统更新sudo apt update sudo apt upgrade -y然后安装基础工具sudo apt install -y curl wget vim git unzip这里有一个新手很容易忽略的教训不要在环境没准备好的时候就开始装东西。build-essential、vim、curl这类基础工具看似无所谓但后面编译扩展、测试接口、修改配置文件全都要用。缺一个都会卡住你几分钟。Red Hat系对应的是sudo dnf install -y curl wget vim git unzip同时要注意Red Hat系默认仓库里通常没有Nginx和MySQL需要先添加第三方源。这一点和Debian系差别很大。2.2 目录规划与运行用户设计目录规划看似是小事实际上直接影响后续排障效率。我的建议是单独分出一块数据盘或系统盘的独立路径不要塞在系统根目录的零散位置。这里用一套实际项目里比较成熟的目录结构sudo mkdir -p /data/www sudo mkdir -p /data/wwwlogs/data/www放所有站点代码/data/wwwlogs放日志。以后要备份、要清理日志一眼就能找到地方。然后创建一个专门跑Web服务的用户sudo useradd -r -s /sbin/nologin www这个www用户非常关键。Nginx的worker进程、PHP-FPM的进程池都应该以这个用户运行。为什么如果你直接用root运行Nginx一旦站点代码被上传了恶意脚本攻击者拿到WebShell就相当于拿到了整台服务器的root权限。用普通用户跑至少能把影响范围限制在这个用户自己的目录权限内。同时Nginx、PHP-FPM、站点文件三者的用户如果不统一最常见的后果就是PHP写不了文件、上传失败、日志目录没权限。后面我会专门讲这个坑。现在先执行sudo mkdir -p /data/www/site1 sudo chown -R www:www /data/www sudo chmod -R 755 /data/www这一步为后面所有配置打好了基础。3. Nginx安装与配置先让请求有地方落地3.1 安装方式选型apt安装还是源码编译Nginx的安装方式主要有两种很多人一上来就选源码编译理由是“网上教程都这么装”“可以定制模块”。但在实际生产里如果没有特殊需求我建议优先用发行版自带包对比项包管理器安装源码编译安装安装速度秒级需要编译通常5-15分钟模块完整性内置常用模块够用需要自己选定模块升级维护apt/dnf一键升级手动下载替换容易出问题适用场景绝大多数标准部署需要第三方模块、特殊优化参数Nginx官方和发行版仓库都会持续推送安全更新用包管理器装一条apt upgrade就能打补丁。源码编译装出来的版本如果不定时关注更新公告很容易长期带着已知漏洞跑。我的原则是遇到需要特殊模块的情况再编译平时直接用包管理器。执行安装sudo apt install -y nginx安装完成后检查服务状态和版本nginx -v sudo systemctl status nginx如果看到active (running)说明Nginx已经起来了。先访问服务器IP能看到Nginx默认欢迎页就说明安装成功。3.2 主配置与站点配置的最小可运行形态Ubuntu的Nginx包会把配置拆得很散/etc/nginx/nginx.conf是主配置/etc/nginx/sites-available/和/etc/nginx/sites-enabled/负责站点配置。我建议在/etc/nginx/conf.d/下按站点建独立配置文件这样更清晰。先看主配置nginx.conf里需要调整的几个关键项user www; worker_processes auto; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; }重点解释一下user www让Nginx的worker进程以www用户运行上一步创建的目录正好匹配worker_processes autoNginx会按CPU核心数自动启动worker进程不需要手动填数字worker_connections 1024单个worker进程能同时保持的连接数后面压测调优时再动它。然后创建站点配置文件sudo vim /etc/nginx/conf.d/site1.conf配置内容如下server { listen 80; server_name example.test; root /data/www/site1; index index.php index.html; access_log /data/wwwlogs/site1_access.log; error_log /data/wwwlogs/site1_error.log; location / { try_files $uri $uri/ 404; } location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php8.1-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }这里有两个地方值得展开。第一是try_files $uri $uri/ 404;它的意思是如果直接能找到这个文件就直接返回找不到就尝试当成目录都不行就返回404。没有这一行站点伪静态规则和路由很容易出问题很多后端框架的URL美化全靠它配合。第二是location ~ \.php$这个正则匹配块。之所以要单独配一个以.php结尾的location是为了把PHP请求精确匹配出来交给fastcgi_pass。注意fastcgi_pass这里指向的是unix:/run/php/php8.1-fpm.sock这是一个Unix域套接字文件不是网络地址。现在这个文件还不存在因为PHP-FPM还没装。测试静态页面时可以先注释掉这个location块也可以装完PHP再启用我都会在联调时说明。先检测配置语法并重载Nginxsudo nginx -t sudo systemctl reload nginxnginx -t这个命令非常重要。每次改完配置、在reload之前务必跑一遍它会告诉你配置文件里有没有语法错误。我以前跳过这步直接reload结果把整个Nginx服务搞挂过教训深刻。3.3 静态站点验证在/data/www/site1下新建一个静态欢迎页echo htmlheadtitleLNMP Test/title/headbodyh1It works!/h1/body/html /data/www/site1/index.html然后用curl验证curl -I http://127.0.0.1/如果能看到HTTP/1.1 200 OK说明Nginx、目录权限、站点配置这条链路已经跑通了。很多人在这一步卡住最常见的情况是403 Forbidden。出现403优先检查root目录下是否真的有文件、目录权限是否是755、Nginx的运行用户是否对路径有可读权限。排查顺序一定是从进程用户和文件权限开始而不是急着改配置。4. MySQL 8.0安装与初始化数据库的安全底线4.1 安装与服务启动这里说的MySQL指的是MySQL 8.0系列。Ubuntu 22.04的默认软件源里自带mysql-server安装命令非常直接sudo apt install -y mysql-server安装完成后服务一般会自动启动。检查一下sudo systemctl status mysql如果没启动手动启动并设置开机自启sudo systemctl enable --now mysqlCentOS/Rocky Linux上对应的包名是mysql-server但源里往往没有需要先通过MySQL官方源或替代方案安装这里就不展开赘述了。安装完MySQL后先确认能否用root身份进入本地控制台sudo mysql注意这台机器上的MySQL root账号默认使用的是auth_socket插件也就是只要你是系统root用户就可以直接登录MySQL root不需要密码。这是Ubuntu包特意设计的安全策略意味着本机只有sudo权限用户才能操作MySQL root账号这对于本地管理员是合理的。但如果你要用远程工具连MySQL root那是绝对不行的后面会说怎么创建业务账号。4.2 安全初始化脚本到底做了什么MySQL装完仓库里的数据还是“出厂状态”有一堆隐患匿名用户可以连接、test库默认存在、root账号允许空密码等。所以官方提供了一个安全初始化脚本sudo mysql_secure_installation这个脚本交互式地引导你完成几件事设置root密码虽然Ubuntu下root走socket认证但有些场景还是会用到密码删除匿名账号禁止root远程登录删除test测试数据库重载权限表。我见过不少教程在装完MySQL后跳过这一步理由很简单“反正我本地开发没风险。”但等你哪天把端口暴露到公网就等着被扫描爆破吧。安全初始化必须做没有例外。4.3 业务库与最小权限账号的创建登录MySQL控制台sudo mysql然后创建业务数据库。注意字符集必须明确指定CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;为什么强调utf8mb4因为MySQL 8.0默认字符集虽然是utf8mb4但显式声明能避免历史项目迁移时沿用旧的utf8其实是utf8mb3。utf8mb3连emoji都存不了写入时直接报Incorrect string value错误这个坑在后端开发里非常经典。接着创建业务账号。MySQL 8.0里必须先CREATE USER再GRANT不能像老版本那样用一条GRANT顺带建用户CREATE USER app_userlocalhost IDENTIFIED BY 这里写一个强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO app_userlocalhost; FLUSH PRIVILEGES;这里有两个设计细节值得讲。第一账号的host我写的是localhost也就是说这个账号只允许从本机连接数据库。LNMP场景下PHP和MySQL跑在同一台机器完全没有必要开放远程连接。第二权限我按最小化原则只给了SELECT, INSERT, UPDATE, DELETE没有给DROP、ALTER这类高危权限。后台管理页面或定时任务里一般不需要改表结构给足了普通CRUD权限就够了。如果哪天项目需要执行数据库迁移再用root账号或者单独授权而不是图省事把所有权限一次性给满。验证账号是否可用退出后用业务账号登录mysql -u app_user -p app_db -h 127.0.0.1这里有个容易混淆的点PHP连接数据库时用的host到底是localhost还是127.0.0.1在MySQL的授权表里localhost和127.0.0.1可能是两条不同的记录取决于MySQL怎么解析。如果PHP连接时报Access denied第一步就是检查mysql.user表里账号匹配的host。我上面创建的账号是app_userlocalhostPHP里连接串用127.0.0.1却可能匹配不上稍后在联调部分会专门讲这个问题。4.4 远程访问能不开就不开很多生产环境出问题根因不是代码而是数据库端口直接暴露到了公网。MySQL默认监听3306端口如果配置没有改动它默认只绑定127.0.0.1外部连接不进来。但有些人为了让本地Navicat之类的工具能连就把bind-address改成了0.0.0.0同时防火墙又没拦住结果数据库被全网扫描爆破。我的建议是数据库不要直接开公网端口。如果确实需要在本地调试远程数据库优先用SSH隧道或者只对可信内网IP放行并且在云厂商的安全组层面就限制来源IP。配置层面/etc/mysql/mysql.conf.d/mysqld.cnf里有一个关键项bind-address 127.0.0.1保持这个默认值最安全。修改之前先想清楚你暴露的到底是什么风险。5. PHP-FPM安装及与Nginx的通信机制5.1 为什么Nginx要借助PHP-FPM处理PHP很多人最开始会疑惑Nginx处理PHP到底怎么处理的答案是它处理不了。Nginx本身只懂HTTP协议和静态文件服务它不理解PHP代码。PHP代码需要一个解释器来执行而执行PHP的进程必须和Nginx协作。这条路就是FastCGI协议。Nginx收到*.php请求后按照配置把请求信息URI、请求头、请求体、一些环境变量通过FastCGI协议转发给后台的PHP-FPM进程PHP-FPM执行完脚本把结果再通过同样的协议传回Nginx。你可以这样类比Nginx是餐厅的前台负责接待所有顾客HTTP请求PHP-FPM是后厨负责制作菜品执行PHP代码FastCGI就是前台下给后厨的传菜单窗口。顾客不会直接去后厨点菜前台也不会自己炒菜二者通过传菜单窗口协作。这个类比能解释大多数502问题传菜单窗口socket没打通或者后厨没人PHP-FPM没启动前台就只能告诉顾客“我这边没法处理”。5.2 安装、版本对应与常用扩展安装PHP-FPM和常用扩展一条命令搞定sudo apt install -y php8.1-fpm php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-zip这里要特别强调版本对应PHP解释器、PHP-FPM、扩展三者必须同一个大版本。你装了php8.1-fpm配置socket文件路径里如果写了php8.0-fpm.sock那Nginx永远连不上FPM因为对应版本的服务根本不存在。安装完成后确认服务状态sudo systemctl status php8.1-fpmPHP扩展列表可以通过php -m查看确认mysqli、pdo_mysql是否在其中php -m | grep mysql输出里至少有mysqli和pdo_mysql才说明PHP能操作MySQL。5.3 关键配置项与socket/TCP选型PHP-FPM的主配置在/etc/php/8.1/fpm/pool.d/www.conf这个文件决定了FPM进程池的所有行为。几个关键项user www group www listen /run/php/php8.1-fpm.sock listen.owner www listen.group www listen.mode 0660 pm dynamic pm.max_children 10 pm.start_servers 3 pm.min_spare_servers 2 pm.max_spare_servers 5 pm.max_requests 500user和group设成www确保PHP进程和Nginx进程权限一致。listen指向socket文件路径这一步决定了Nginx里fastcgi_pass要写的目标。关于socket和TCP两种连接方式很多初学者分不清我用表格整理一下对比项Unix SocketTCP/IP本质本机进程间通信走socket文件走回环网络地址性能略优于TCP减少网络栈开销略低但差距不大适用场景Nginx和PHP-FPM同机Nginx和PHP-FPM跨机器配置写法unix:/run/php/php8.1-fpm.sock127.0.0.1:9000我的选择非常简单Nginx和PHP-FPM在同一台机器上就坚持用Unix Socket。虽然性能差异在低负载下几乎感知不到但少一层网络栈调用总归是好的而且socket文件的权限管控更直观配合listen.owner能精确控制访问权限。跨机器部署时才改用TCP方式。PHP本身的配置在/etc/php/8.1/fpm/php.ini几个常见的需要按项目调整date.timezone Asia/Shanghai upload_max_filesize 20M post_max_size 20M memory_limit 128Mpost_max_size必须不小于upload_max_filesize否则上传大文件时PHP会直接拒收这是一个新手经常踩的坑。5.4 让Nginx把PHP请求转发给PHP-FPM这时候再回到Nginx的站点配置把之前那段location ~ \.php$启用。注意fastcgi_pass后面写的路径必须和www.conf里的listen路径完全一致。我这里是location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php8.1-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }fastcgi_params是Nginx自带的一个文件里面定义了FastCGI协议需要传递的标准请求信息。SCRIPT_FILENAME是PHP-FPM执行文件必须知道的参数它指明了要执行的PHP脚本的完整绝对路径。如果这个参数配错PHP-FPM会无法定位脚本返回File not found但请求本身已经正常转发过去了日志里通常能看到“Primary script unknown”这类提示。改完配置一定要校验并重载sudo nginx -t sudo systemctl reload nginx到这里Nginx和PHP-FPM之间的“传菜单窗口”就打通了。但PHP-FPM到底能不能执行PHP、能不能连数据库还得用实际请求去验证。6. 三件套联调从静态页到动态查询全链路验证6.1 静态页面验证联调的第一步永远是验证最外层Nginx本身。用上一步创建的index.html再测一次curl -I http://127.0.0.1/如果这一步过不了后面所有环节都没有意义。拿到200 OK后再往下一步走。排错时一定要按链路逐层定位千万别在Nginx还没通的时候就去投诉PHP-FPM。6.2 PHP解析验证在/data/www/site1下新建index.php?php phpinfo();然后请求curl -s http://127.0.0.1/index.php | head -50如果页面返回HTML且能看到PHP Version相关字样说明Nginx→PHP-FPM链路已经打通。这里我要特别提醒phpinfo()页面会暴露PHP版本、扩展列表、服务器环境变量等大量敏感信息任何人只要知道地址就能看到你的服务器状况生产环境绝不允许长期暴露。它的价值仅限于本地验证测试完成后立即删除rm /data/www/site1/index.php6.3 PHP连接MySQL验证最后验证全链路中最容易出问题的一环PHP连MySQL。在站点目录下建一个测试脚本?php $dsn mysql:host127.0.0.1;dbnameapp_db;charsetutf8mb4; $user app_user; $pass 你设置的密码; try { $pdo new PDO($dsn, $user, $pass); $stmt $pdo-query(SELECT VERSION()); $row $stmt-fetch(PDO::FETCH_NUM); echo MySQL version: . $row[0] . PHP_EOL; } catch (PDOException $e) { echo Connection failed: . $e-getMessage() . PHP_EOL; } ?保存为test_db.php请求它curl -s http://127.0.0.1/test_db.php如果输出MySQL version: 8.0.x恭喜LNMP架构已经完整跑通。如果报错把错误信息拆分来看常见的无非这么几类报错关键字含义处理方向Connection refused连不上MySQL确认MySQL服务启动、监听地址正确Access denied for user账号密码或host不匹配检查mysql.user中的host和密码Unknown database库名不存在检查数据库拼写和大小写could not find driver缺少PDO/MySQL扩展确认php-mysql扩展安装正确测试通过后记得删除这个文件rm /data/www/site1/test_db.php到这里手动搭建LNMP的基本目标已经完成。但离“上线能扛住流量”还差最后一步压测和调优。7. 上线前压测与三项关键调优7.1 用ab拿到第一份基准数据压测工具我用最普及的ab它虽然老但够用。安装方式在Ubuntu上是sudo apt install -y apache2-utils装好后对PHP页面发起一波1000个请求、50并发的压测ab -n 1000 -c 50 http://127.0.0.1/index.php看这几个指标Requests per second每秒能处理多少请求数值越高越好Time per request平均每个请求耗时Failed requests有没有失败请求这一步直接反映配置是否合理。压测不是为了追求极致数字而是为了拿到“调优前的基准”。没有基准你后面改配置有没有效果全靠感觉。先记录当前数据再做下面的调整改完再压一次对比提升了多少。7.2 Nginx的进程模型与连接参数Nginx调优的核心是理解它的进程模型。worker_processes auto会让每个CPU核心跑一个worker进程这通常就是最优解。需要调整的是每个worker能同时保持的连接数worker_connections。如果机器是2核4G并发量不大默认1024足够。但如果静态资源请求量大或者要扛的并发上来可以调到2048或更高events { worker_connections 2048; }计算总连接上限的公式是worker_processes * worker_connections。同时Linux系统层还有一个文件描述符的限制ulimit -n如果系统限制不够Nginx连接数再高也白搭。可以用ulimit -n查看当前值必要时在systemd单元或limits配置里提高。此外keepalive_timeout 65这个值建议保持在一个合理范围。太短会导致频繁建立TCP连接增加开销太长会占着连接不释放。动态页面场景一般配60到75秒就够了。Nginx开启gzip也是性价比很高的优化gzip on; gzip_types text/plain text/css application/json application/javascript image/svgxml;尤其对于API接口返回的JSONgzip压缩率通常能到70%以上响应体积立刻小一大截。7.3 PHP-FPM的进程池参数计算PHP-FPM的调优核心是pm相关参数。默认pm dynamic会在空闲时保持一部分进程、忙碌时动态扩容这种做法比较通用。关键是要算清楚pm.max_children。计算方法很简单一个PHP-FPM进程在正常业务下大约占30到50MB内存。假设服务器总内存4GB预留1GB给MySQL和操作系统那么PHP可用的内存大约是3GB。按每个进程平均40MB算pm.max_children设置为3GB / 40MB ≈ 75但这里要留出安全余量。如果业务本身只用其中一部分实际设置20到40比较合理。设置太高会怎样高并发来临时FPM拼命创建进程把内存瞬间吃光触发OOMMySQL甚至整个服务器被杀掉。我见过最典型的线上事故就是max_children设置过大系统直接卡死。pm.max_requests 500同样重要。PHP-FPM进程长期运行会产生内存碎片或局部变量残留设置每个进程处理完500个请求后自动回收重建能有效避免内存缓慢增长。7.4 MySQL的innodb缓冲池与慢查询MySQL调优提一个参数就够了innodb_buffer_pool_size。InnoDB引擎会把磁盘上的数据和索引页缓存在这个内存区域它的大小直接影响数据库查询速度。一般建议设置为物理内存的50%到70%但前提是这台机器主要跑MySQL。如果和PHP、Nginx共用就需要给它们留出空间整体控制在总内存的40%到50%左右。修改位置在/etc/mysql/mysql.conf.d/mysqld.cnfinnodb_buffer_pool_size 1G slow_query_log 1 slow_query_log_file /data/wwwlogs/mysql_slow.log long_query_time 1long_query_time设置为1秒超过1秒的查询都会被记入慢查询日志。上线后隔几天看一眼这个日志基本能定位哪些SQL没走索引、哪些表数据量该拆了。这是排查数据库性能问题最高效的入口。8. 上线后最容易踩的五个坑8.1 502 Bad Gateway排查链路502是LNMP环境里最常见也最容易让人崩溃的问题。静态页面正常访问PHP页面却报502其实说明Nginx本身没问题问题出在Nginx和PHP-FPM的沟通环节。按以下顺序排查90%的问题能快速定位第一步确认PHP-FPM服务还活着sudo systemctl status php8.1-fpm第二步查看socket文件是否存在ls -l /run/php/php8.1-fpm.sock第三步对比Nginx配置里的fastcgi_pass和FPM的listen确认路径完全一致。很多时候是改了FPM的socket路径但Nginx没同步或者装PHP时版本号写错。第四步同时看两端日志。Nginx错误日志在/data/wwwlogs/site1_error.log或/var/log/nginx/error.logPHP-FPM错误日志通常也在/var/log/php8.1-fpm.log。日志里的线索比猜测可靠得多。8.2 socket权限问题看起来像502又不是502有几次我遇到502按上面的流程排查服务在跑、socket文件也存在路径也没错但Nginx就是连不上。最后发现是socket文件权限问题。PHP-FPM创建的socket文件临时位于/run/php/php8.1-fpm.sock默认属主是FPM池里配置的user和group。如果Nginx worker用户的属主和FPM不一致或者socket文件的权限没有放开Nginx就没有权限向这个文件写入FastCGI请求表现同样是502。解决办法是把www.conf里的listen.owner、listen.group设置为Nginx的运行用户一般统一为wwwlisten.owner www listen.group www listen.mode 0660改完重启PHP-FPM。这个细节很容易被忽略因为默认配置下如果PHP-FPM和Nginx都用www-data或同名用户运行就没事一旦你像我一样自定义了www用户而没有同步socket所有者就踩坑了。8.3 MySQL 8认证插件导致PHP连接失败MySQL 8.0默认使用caching_sha2_password认证插件而某些老版本的PHP数据库扩展尤其是较旧的mysqlnd只认识mysql_native_password。表现就是命令行用账号密码能连MySQL但PHP连接时报Access denied甚至错误信息都不明显。排查方法SELECT user, host, plugin FROM mysql.user;如果看到app_user对应的plugin是caching_sha2_password而你的PHP扩展版本太旧可以临时改用老插件ALTER USER app_userlocalhost IDENTIFIED WITH mysql_native_password BY 密码;但我要说明这是兼容手段而不是最佳方案。新项目优先升级PHP和扩展版本让mysqlnd支持新的认证方式能从本机socket连接就别走TCP保持MySQL默认的强认证插件才是长远选择。现代PHP 7.4以上版本基本都支持caching_sha2_password不需要这个妥协。8.4 防火墙、SELinux与“本地连不上”的错觉有一次我在一台新服务器上部署PHP脚本里连MySQL始终Connection refused但MySQL命令行却能连。查了半天发现是防火墙默认策略丢包。Ubuntu上ufw开启时会限制外部访问3306而云服务器控制台的安全组还会另外挡一道。如果你的业务场景确实需要远程数据库连接必须同时放行系统防火墙和云平台安全组对应端口少一个都连不上。CentOS/Rocky Linux这类系统还要注意SELinux。它有一组布尔值控制HTTP服务是否允许连接网络数据库默认可能是禁止日志里还不明显。如果是在Red Hat系系统上部署排查远程连接问题时一定要查一下SELinux状态和对应的布尔值设置。8.5 文件权限不一致引发的上传失败最后一个坑非常经典PHP代码明明没问题上传功能却报“目录不可写”。根本原因通常是三个身份不一致Nginx worker进程是www-dataPHP-FPM进程是www目标目录属主是root。PHP能执行但往目录里写文件时权限检查过不去。解决思路非常简单统一用户。Nginx和PHP-FPM都改成www用户运行整个站点目录也由www用户管辖sudo chown -R www:www /data/www/site1日志目录同样处理。保存文件的目录给到755或750都行但别为了省事直接chmod 777。777意味着任何用户都能改这个目录下的文件一旦站点出现漏洞后果会严重很多。这套LNMP架构我后来在好几个项目里重复部署过每次把上面的链路和坑位过一遍基本不会再出幺蛾子。手动搭一次是为了理解理解了之后再用自动化工具或脚本去部署心里才有底。说句实话一键脚本最大的问题不是不好用而是当你不知道它配置了什么时出问题连排查方向都没有。希望你跟着这套流程走完能拥有那种“看到配置就知道问题在哪”的感觉。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑