
看到这个标题我第一反应是“又是LNMP教程”但把Discuz部署和动静分离串在一起其实远比想象中能挖出东西来。LNMP/LNAMP是Web服务端最经典的组合方案Discuz又是PHP社区产品里最典型的代表动静分离则几乎每个生产环境都绕不开。这篇就把这三件事一次讲透环境怎么搭、Discuz怎么上、动静分离到底怎么落地、踩坑点在哪里。适合刚接触服务器运维、想完整走一遍Web架构流程的新人也适合已经会装Nginx但始终没搞明白动静分离原理的朋友直接抄作业。1. 架构选型LNMP 和 LNAMP 到底差在哪1.1 LNMP 的结构与优势LNMP 的全称是 Linux Nginx MySQL PHP-FPM。Nginx 负责接收请求、处理静态文件、反向代理请求PHP-FPM 负责解析 PHP 动态请求MySQL 负责数据存取。三者之间通过 FastCGI 协议和 TCP/Socket 通信。这套结构之所以能成为主流关键点在于 Nginx 的事件驱动模型——它能以极低的内存开销扛住大量的并发连接尤其擅长处理静态文件和转发请求。而 PHP-FPM 则独立于 Web 服务器运行动态请求被转发到它那里再由 PHP 解释器执行代码并返回结果。我实际部署下来的感受是LNMP 这种“前后端分离”的结构最大的好处是各司其职。静态资源完全由 Nginx 直接返回不消耗 PHP 进程资源动态请求虽然要走一层转发但 FastCGI 的转发开销远小于重新起一个 Apache 进程去加载 mod_php。对于 Discuz 这种以读为主、帖子和静态资源量大的社区系统LNMP 是最稳妥的选择。1.2 LNAMP 的结构与适用场景LNAMP 则是 Linux Nginx Apache MySQL PHPNginx 作为前置入口监听 80/443 端口接收所有外部请求Apache 作为后端服务器通常监听内网端口或 Unix Socket只处理和解析 PHP。数据流向是用户请求先到 NginxNginx 判断如果是静态资源就直接返回如果是 PHP 动态请求就转发给 ApacheApache 再通过 mod_php 或 PHP-FPM 执行 PHP 代码。我在什么情况下会放弃 LNMP 而选 LNAMP大概有几种一是项目依赖 Apache 的 .htaccess 实现目录级配置这对某些老代码来说改造成本太高二是有些 PHP CMS 插件代码用了 Apache 环境特有的环境变量或模块行为例如 mod_rewrite 的 RewriteRule 在目录级生效的逻辑Nginx 的 rewrite 没法一一对应。三是团队对 Apache 运维更熟悉出问题排查起来更快。但 LNAMP 的劣势也明显——Nginx 和 Apache 之间多了一层转发并发量增长时 Apache 进程数会膨胀内存占用比纯 LNMP 高所以如果是全新项目我基本不会选 LNAMP。1.3 动静分离与架构选型的关系动静分离这个概念本身其实与架构选型是紧密绑定的。它的本质思想是把不会被 PHP 处理的静态资源图片、CSS、JS、字体、附件直接交给 Nginx 这类性能更强的 Web 服务器把需要 PHP 动态生成的页面才转发给后端解释器。这样做的好处有两块第一是静态请求变快了因为 Nginx 直接读磁盘文件、加缓存头返回性能远高于让 PHP 再走一次框架第二是动态资源被保护了PHP 不处理静态文件就意味着无法通过请求路径穿越等方式让 PHP 进程去解析不属于它的内容。所以不管是 LNMP 还是 LNAMP动静分离都是必然要做的事。LNMP 里的 Nginx 天然承担静态服务器角色而 PHP-FPM 只接收 .php 结尾的请求LNAMP 里则是 Nginx 承担静态Apache 承担动态。理解了这一点后面配置起来就会很顺手。2. 环境搭建LNMP 基础服务配置与关键参数2.1 操作系统与初始准备这里我以 CentOS 7 为例因为它在服务器市场占有率仍然很高虽然已经进入维护期但很多存量环境依然在用。系统安装时建议最小化安装装好之后第一件事不是装软件而是确认几个基础项关闭默认防火墙或放行 80/443 端口确保 SELinux 不是强制阻断状态要么关闭要么写对应策略新手建议先关闭设置正确的时区时间不同步会导致 PHP 会话和数据库连接出现莫名其妙的问题。然后是安装编译工具和依赖包。Nginx 我建议直接编译安装理由是可以精确控制编译参数和模块集合轻量干净后续排障时心里有底。需要装的东西包括 gcc、make、pcre-devel、zlib-devel、openssl-devel 等。用 yum 安装的话要额外加 EPEL 仓库yum install -y epel-release gcc gcc-c make pcre-devel zlib-devel openssl-devel这些包的作用分别对应 Nginx 的正则模块、压缩模块和 HTTPS 支持缺了哪样编译都会报错别省。2.2 Nginx 编译安装与配置要点Nginx 版本我选 1.24 或 1.20 稳定版没必要追最新。一个在生产环境用了三四年的平稳版本远比刚发布的新功能版本可靠。编译命令里的几个重点参数如下./configure \ --prefix/usr/local/nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-pcre make -j4 make install装完之后有几个细节要立刻处理修改 nginx.conf把 worker_processes 设置为 CPU 核心数worker_connections 提高到 10240 以上把运行用户从默认的 nobody 改成 nginx 或 www需要先用 useradd 创建该用户在 server 块里配置 server_name 和 root 目录。还有一个非常容易被忽略的点Nginx 默认的 server 块会响应所有未匹配的 IP 请求生产环境建议配置一个默认的空 server 返回 444防止被 IP 绕过域名直接访问。2.3 MySQL 安装与基础优化MySQL 版本建议 5.7 或 8.0如果服务器内存不大也可以用 MariaDB 10.x 替代毕竟 API 兼容对 Discuz 完全没有兼容性问题。我经常用 MariaDB 是因为它的官方源里持续有维护版本升小版本方便而且在低配服务器上内存控制得比 MySQL 更好一些。安装完成后立刻要调整 my.cnf不要用默认配置。最关键的一个参数是 innodb_buffer_pool_size它决定了 InnoDB 表的数据和索引缓存在内存中的规模。经验值是总内存的 60%-70%比如 4G 内存的机器设 2G-2.5G。如果这台机器同时还跑 Nginx 和 PHP-FPM建议不要超过 50% 避免内存不足触发 OOM。另外把 innodb_flush_log_at_trx_commit 从默认的 1 调整为 0 或 2 可以提升写入性能但会降低崩溃时数据安全的级别社区论坛场景可以接受金融类业务不要这么调。初始化数据库后记得创建 Discuz 需要的库和账号这里有个建议用户名和密码不要用 root数据库名也不要直接用默认的 discuz这样做即便将来连接串泄露影响面也可控。CREATE DATABASE discuz DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER dz_userlocalhost IDENTIFIED BY StrongPss2024; GRANT ALL PRIVILEGES ON discuz.* TO dz_userlocalhost; FLUSH PRIVILEGES;2.4 PHP-FPM 的安装与关键配置PHP 版本我建议 7.4 或 8.0 以上因为 Discuz X3.4/3.5 对 PHP 7 的支持已经很完善而 PHP 5.6 已经非常古老性能和安全都跟不上。注意 PHP 不仅是装一个解释器还需要装基础扩展因为这些扩展直接影响 Discuz 能否正常运行。常用的扩展有yum install -y php-fpm php-mysqlnd php-gd php-xml php-mbstring php-json php-curl php-zip装完之后要改两个层面的配置。第一是 php.ini重点调整 upload_max_filesize默认 2M论坛要传附件肯定不够建议调成 20M 以上、post_max_size比 upload_max_filesize 大一点比如 50M、date.timezone设置成 Asia/Shanghai 或 UTC不设的话 PHP 日志和函数会报警告。第二是 php-fpm.conf 里的 pool 配置通常是 /etc/php-fpm.d/www.conf 文件关键项是 listen 和 pmlisten 127.0.0.1:9000 user nginx group nginx pm dynamic pm.max_children 50 pm.start_servers 5 pm.min_spare_servers 5 pm.max_spare_servers 35pm 参数怎么理解pm.max_children 是 PHP-FPM 最多同时处理多少个 PHP 请求子进程每个子进程默认占用 20M-30M 内存如果机器内存 4G50 个是比较安全的上限。pm 设置为 dynamic 时PHP-FPM 会按需求动态增减进程数适合多数场景。设置完毕重启 php-fpm再用 ss -lnt 查看 9000 端口是否监听即可。3. Discuz 部署实操从下载到安装完成3.1 下载解压与目录权限Discuz 官方提供两个版本线X3.4稳定保守和 X3.5新版支持 PHP 8、UTF8MB4我个人建议直接用 X3.5因为它在安全机制、编码支持和功能迭代上都明显比 X3.4 好。下载后解压到 Nginx 的网站根目录比如 /data/wwwroot/discuz然后把config目录和data目录的权限调整一下。Discuz 安装阶段会向 config/config_global.php、config/config_ucenter.php 写入配置向 data 目录写入缓存和附件所以这两个目录需要给 Web 运行用户写权限。但注意不要图省事直接 chmod 777正确做法是把目录属主改为 nginx 用户chown -R nginx:nginx /data/wwwroot/discuz chmod -R 755 /data/wwwroot/discuz chmod -R 777 /data/wwwroot/discuz/config chmod -R 777 /data/wwwroot/discuz/data安装完成后我会把 config 里的文件改成 644只保留 data 目录可写。因为 config 文件一旦可写任何通过 Web 层面的漏洞都可能修改论坛配置这是非常危险的事情经验教训太多了。3.2 数据库准备与浏览器安装用前面创建的 dz_user 账号Discuz 安装向导里填入数据库信息即可。有一点要注意Discuz 安装时要求填写数据库端口默认 3306 不用改但如果 MySQL 跑在另一台机器上需要保证防火墙放行且有网络连通权限。安装过程非常傻瓜唯一需要认真看的地方是“管理员账号”和“创始人”信息务必设置高强度密码这是 Discuz 后台的第一道防线。浏览器安装完成后立即删除 install 目录这一步是 Discuz 官方手册反复强调的。我不止一次见过服务器上 install 目录没删结果被扫描器重装然后接管后台的案例。删除后顺手验证一下站点能否正常打开首页、帖子列表、用户登录页各点一遍。3.3 Nginx 伪静态配置Discuz 的静态化也就是伪静态依赖 Nginx rewrite 规则。伪静态的作用是把 topic-1.html 这类 URL 重写为真实请求的 PHP 参数形式既美观又有利于搜索引擎收录。Discuz 官方有标准规则直接贴到 Nginx server 块里即可location / { rewrite ^([^\.]*)/topic-(.?)\.html$ $1/portal.php?modtopictopic$2 last; rewrite ^([^\.]*)/article-(.?)\.html$ $1/portal.php?modarticlearticle$2 last; rewrite ^([^\.]*)/forum-(\w)-(.?)\.html$ $1/forum.php?modforumdisplayfid$2page$3 last; rewrite ^([^\.]*)/thread-(\w)-(.?)-(.?)\.html$ $1/forum.php?modviewthreadtid$2extrapage%3D$4page$3 last; rewrite ^([^\.]*)/group-(.?)\.html$ $1/forum.php?modgroupfid$2 last; rewrite ^([^\.]*)/space-(.?)\.html$ $1/home.php?modspaceuid$2 last; rewrite ^([^\.]*)/blog-(.?)\.html$ $1/home.php?modspaceuid$2doblogid$3 last; }注意把这段规则放在 location / 块内并且保证 server 块中 root 和 try_files 的配合正确。如果配置了伪静态后访问 URL 出现 404优先检查 rewrite 规则与 Discuz 版本是否对应再看 Nginx 是否有权限解析该目录。3.4 上传限制与 PHP 会话细节Discuz 论坛少不了用户上传头像和附件如果前面只调了 php.ini 里的大小限制还不够。Nginx 层有个 client_max_body_size 参数默认是 1M意味着即使 PHP 允许上传 50MNginx 会在客户端请求时直接拦截并返回 413。所以必须在 server 或 location 块里加上client_max_body_size 50m;另外 PHP-FPM 处理上传时使用临时目录要确保该目录可写且空间充足。如果上传文件较大且并发高建议把临时目录从 /tmp 移到独立的磁盘分区避免 /tmp 写满导致系统异常。4. 动静分离真正落地Nginx 配置与验证4.1 动静分离解决的是什么问题动静分离不是一个玄乎的概念。在没有动静分离的架构里用户请求一张图片Web 服务器也会把请求交给 PHP 进程去处理PHP 脚本再去磁盘上读取文件返回。这会带来两个后果一是 PHP 进程被无意义的静态请求占满导致真正需要计算的动态页面出现高延迟二是静态请求绕了一大圈性能差还容易受 PHP 配置影响。在 LNMP 架构下Nginx 本身就是一个高性能静态文件服务器。动静态分流后CSS、JS、图片、附件直接在 Nginx 层面返回处理一个静态请求的耗时通常在毫秒级而且不占用 PHP 进程。动态请求仍然通过 FastCGI 转给 PHP-FPM整个链路的负载被拆分到不同进程里不会被互相拖垮。这就是动静分离最直接的价值。4.2 Nginx 静态资源 location 配置实践在 Nginx 配置里做动静分离核心是 location 的匹配规则。以 Discuz 为例静态资源通常集中在 static 目录、data 目录下的附件区以及 uc_server 的静态文件。我常用的配置写法是这样的location ^~ /static/ { expires 7d; access_log off; add_header Cache-Control public, max-age604800; } location ^~ /data/attachment/ { expires 30d; access_log off; add_header Cache-Control public, max-age2592000; }这里有两个要点。第一^~ 修饰符表示命中这个前缀后不再做正则匹配对于纯静态目录非常合适避免静态请求落到后面的 PHP 正则 location 里。第二expires 和 Cache-Control 的作用是让浏览器强缓存用户第二次访问时直接读本地缓存连请求都不发。我见过一些站点安装后从来没有配过 expires导致每次刷新页面都重新加载图片和脚本首屏渲染速度差一截。JS 和 CSS 我通常会额外启用 gzip因为文本类的压缩率能达到 70% 以上。在 http 块里开启 gzip on加上 gzip_types text/css application/javascript image/svgxml响应体积会立竿见影地缩小。4.3 PHP 动态请求转发的正确写法动态请求的 location 规则要放在静态规则之后用正则去匹配 .php 结尾的文件。注意要有短路处理防止用户请求一个不存在的 PHP 文件时Nginx 把它转给 PHP-FPM然后 PHP 再抛 404这样既慢又暴露路径信息。我在生产上常用的配置如下location ~ \.php$ { root /data/wwwroot/discuz; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }如果把这个 location 放在 ^~ /static/ 里静态请求则根本不会进入 PHP 正则匹配所以“动静分离”就是靠这两类 location 的优先级和顺序落实的。测试时用 curl -I 请求一个 PHP 文件正常情况下能看到返回 200 和 PHP-FPM 生成的响应头请求一个静态图片响应来自 Nginx头信息里会带上我们刚加的 Cache-Control 字段。4.4 用 curl 和日志验证动静分离是否生效配置写完之后别急着自我感觉良好用命令验证一遍最有说服力。我先请求静态资源curl -I https://yourdomain.com/static/image/logo.png返回结果里应该有 expires 和 Cache-Control 相关字段而且响应时间通常在几毫秒到十几毫秒。然后请求首页和论坛列表curl -I https://yourdomain.com/forum.php这个请求会进入 PHP 处理返回头里会带 X-Powered-By 或 Set-Cookie 等动态特征。再看 Nginx access.log静态资源请求的响应字节数和 PHP 请求有明显差异——静态请求的耗时记录很短PHP 请求则往往是几十毫秒甚至几百毫秒这就说明动静分离已经生效。如果静态请求日志里出现响应时间飙高通常是要么磁盘读取慢要么 PHP 规则拦截了静态请求没有命中静态 location。4.5 LNAMP 场景下的动静分离实现LNAMP 架构下的动静分离思路完全一致只是动态后端从 PHP-FPM 变成了 Apache。Nginx 监听 80 端口Apache 在本机监听 8080 端口静态请求 Nginx 自己处理动态请求反向代理给 Apacheserver { listen 80; server_name yourdomain.com; root /data/wwwroot/discuz; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ^~ /static/ { expires 7d; access_log off; } }这个配置下Nginx 对未匹配到静态规则的请求全部代理给 Apache而 Apache 负责解析 PHP、处理 Discuz 的伪静态。这种模式确实能做动静分离但我也要在实站里提醒一句LNAMP 的性能瓶颈通常出现在 Apache 进程数量上并发一旦上来Apache 会为每个连接保持进程内存吃紧。所以在 LNAMP 架构中Apache 的 prefork/worker 模式参数需要和本机内存严格匹配这就没有 LNMP 来得轻省。5. Discuz 安全加固与常见故障排查5.1 安全加固目录权限、危险函数与命令执行防护部署完 Discuz并不代表工作结束了安全加固才是更值得花时间的地方。Discuz 作为老牌社区系统历史上公开过多个命令执行类型的漏洞公告这类漏洞通常需要配合上传功能或后台功能利用攻击者拿到执行权限后往往直接接管服务器。所以我的态度很明确不研究怎么利用但要清楚怎么防。第一件事是确认版本。官方最新版 X3.5 对历史漏洞的修补覆盖远好于老版本而且官方会持续发布安全补丁新装站点没有任何理由停留在低版本上。第二件事是排除非官方修改版网上能搜到很多“破解版”“去限制版”的 Discuz这类包的代码被第三方改过极有可能被预留后门生产环境千万别碰。第三件事是 PHP 危险函数禁用。在 php.ini 里通过 disable_functions 关掉那些被命令执行类漏洞经常借用的函数比如 system、shell_exec、proc_open、popen、passthru、exec。Discuz 正常运营通常用不到这些关闭后即使出现文件写入漏洞攻击者利用 PHP 直接执行系统命令的路也少了一半。第四件事是目录权限的收敛。data 和 config 目录在安装后尽量收紧论坛定时任务和后台如果不需要写文件就把写权限摘掉。第五件事是 MySQL 账号权限只给 Discuz 库的所有权和基本的增删改查权限不要让应用账号拥有 GRANT 权限防止注入拖库后被进一步提权。5.2 常见故障排查速查表我在部署过程中踩过的坑基本都能归类整理成一张表遇到问题可以直接按表操作。现象可能原因处理方案访问 PHP 页面返回 502PHP-FPM 未启动或进程崩溃检查 php-fpm 进程/var/log/php-fpm/error.log 看哭日志确认 9000 端口监听访问首页直接下载文件Nginx 没有配置 PHP location 匹配确认 location ~ .php$ 的 fastcgi_pass 已配置伪静态 URL 返回 404rewrite 规则不对或与版本不匹配核对 Discuz 版本对应规则清除 Discuz 缓存后重试上传附件提示超时或无法上传client_max_body_size 和 php.ini 大小未协调同时检查 Nginx 层和 PHP 层的上传容量安装时提示数据库无法连接数据库账号权限不足或网络不通先用命令行测试账号能否连接再检查防火墙与 bind-address论坛页面显示空白但无报错PHP 错误日志被关闭或 opcache 缓存了旧代码临时打开 display_errors清空 opcache 缓存排查时我习惯先看日志Nginx error.log、PHP-FPM error.log、MySQL ssslow.log 三个日志文件是定位问题的三条主线比到处试配置效率高得多。记得把 log_level 调成 notice 或 warn默认的 error 级别往往漏掉很多细节信息。5.3 生产环境运维的几个小习惯部署完成不等于可以撒手不管。我每次做完这类项目都会顺手做几件事配置 Nginx 和 PHP-FPM 的日志切割避免日志文件无限膨胀撑爆磁盘写一个简单的数据库备份脚本每天凌晨用 mysqldump 备份 Discuz 库保留最近 7 天用 cron 定时清理 data/cache 目录下的临时文件。这些动作不起眼但在事故现场能救命的恰恰是这些基本功。我个人在实际操作中最大的体会是LNMP 这套架构本身很成熟真正出问题的往往是权限和状态管理。比如运行用户不统一、SELinux 没放行、php-fpm 和 Nginx 的用户不一致导致读不了目录等等。配完一套环境后用 ps 看看进程隶属于哪个用户用 ls -l 检查目录属主是否一致往往隐患都在这些细节里。另外压测工具不要迷信 ab 的单并发数字至少用并发 100 跑 30 分钟再观察内存和错误日志这才能看出动静分离的优化效果到底稳不稳。把这些理顺了LNMP 部署 Discuz 这件事就算真正落地了。