专题 6 个子章节

WordPress 部署与运维实战:从部署认知到故障排查

梦随乡兮 梦随乡兮 #WordPress#1Panel#部署#运维#性能优化

从部署结构、迁移和缓存讲到多站资源控制与故障排查,串起一套 WordPress 部署与运维实践。

这组文章来自几次不同阶段的 WordPress 实践:从宝塔迁到 1Panel,重新认识 Docker 部署,给多站环境接入缓存,再到小规格 VPS 的资源收敛和后台 524 排查。现在把它们整理为一个专题,章节按这条运维链路排列。

一次请求经过哪些组件

在 1Panel 环境里,浏览器发出的请求通常先到 OpenResty。传统部署由 OpenResty 把 PHP 请求交给 PHP-FPM,PHP 再读取 WordPress 文件、查询数据库并生成页面;一键部署的容器化 WordPress 则多了一层反向代理,OpenResty 把请求转给 WordPress 容器,由容器内的 Web 与 PHP 环境继续处理。

Redis 不负责接收网页请求。启用对象缓存后,WordPress 在生成页面的过程中会先从 Redis 读取部分数据库查询结果,未命中时再访问 MySQL 或 MariaDB。页面缓存处在更靠外的位置,它保存已经生成的 HTML,让部分匿名访问不用再次运行 PHP。

浏览器
OpenResty
PHP-FPM 或 WordPress 容器
├─ WordPress 文件、主题与插件
├─ MySQL / MariaDB
└─ Redis 对象缓存

实际部署未必与这张简图完全一致,但排查问题时可以沿请求方向逐层确认:域名和证书是否正常,OpenResty 是否把请求交给了正确的后端,PHP 是否可用,数据库和 Redis 是否能从当前运行环境访问。

传统部署和容器部署

传统部署的网站文件直接放在主机目录中,OpenResty、PHP 和数据库之间的关系比较直观。容器部署把应用及其运行环境隔离起来,网站数据通过挂载目录持久保存,组件之间通过端口映射或 Docker 网络通信。

这一区别会影响文件位置、配置生效方式、组件使用的主机名和故障排查路径。主机上的 localhost 与容器里的 localhost 指向不同环境,容器访问宿主机或其他容器时,也不能直接照搬传统部署里的连接地址。

这六章如何衔接

前两章从部署结构和一次真实迁移开始,适合刚换到 1Panel 时阅读。第三、四章分别解释页面缓存与对象缓存的分工,并记录 Redis 在多站环境里的配置和隔离方式。第五章把范围扩展到整台 2C4G VPS,处理 PHP-FPM、MariaDB、OPcache、页面缓存和 swap 的资源配置。第六章是这套环境上线后的故障复盘,问题从 WordPress 后台 524 一直追到容器环回和 Docker 出网。

只遇到某个具体问题时,可以直接打开相应章节;如果正在搭建或迁移环境,按顺序阅读更容易理解后面命令所在的网络和部署背景。

延伸阅读

这六章围绕目前的 1Panel 部署与运维链路整理。另有几篇文章保留为独立记录:

从 1Panel 初探 Docker:WordPress 部署逻辑与实践

梦随乡兮 梦随乡兮 #容器#文件#应用#配置#部署

从 1Panel 初探 Docker,把 WordPress 的部署模式、Nginx 角色、镜像、容器与持久化这些关键概念一次讲清楚。

把服务器管理工具迁到 1Panel 后,我第一次接触了基于 Docker 的应用部署。WordPress 仍然由网站文件、PHP 和数据库组成,但文件位置、OpenResty 的职责和配置生效方式都与传统部署不同。

文件路径与 OpenResty 的职责

在同一台服务器上,我同时管理着两种不同模式部署的 WordPress 站点,它们的差异是理解后续所有逻辑的基础。

  • 传统部署(非容器化):网站文件位于集中的 Web 根目录,例如 /opt/.../www/sites/。主机上的 Nginx 在 1Panel 中对应 OpenResty,它直接处理 Web 请求,配置文件中的 root 指向网站文件所在的物理路径。
  • 容器化部署(1Panel 一键部署):网站文件位于独立路径,如 /opt/1panel/apps/wordpress/你的网站名/data。主机上的 OpenResty 不再直接读取网站文件,而是通过 proxy_pass 把 Web 请求转发给 WordPress 容器。

所以容器化站点的 OpenResty 配置中,即使 root 指向的目录几乎为空也不影响访问,因为请求实际由后面的容器处理。

镜像、容器与数据持久化

  1. 镜像(Image):只读的应用模板。例如,wordpress:6.9.0 镜像包含运行对应 WordPress 版本所需的环境和程序文件。
  2. 容器(Container):镜像的运行实例,可以启动、停止和销毁。没有持久化的内部修改会随容器销毁而丢失。
  3. 数据卷(Volume)或目录挂载(Bind Mount):把主机目录映射到容器内部,用来保存主题、插件、上传文件和配置等数据。

在 1Panel 的 WordPress 部署中,docker-compose.yml 指定使用哪个镜像创建容器,并将主机上的 /data 目录挂载到容器内部。主题、插件、上传文件和 wp-config.php 因此不会随着容器重建而丢失。

升级与配置

WordPress 核心升级

  • 直接在后台更新:这会修改数据卷中的核心文件,使运行代码与镜像版本不一致,之后排查和回滚会更麻烦。
  • 正确做法
    1. 编辑应用的 docker-compose.yml 文件。
    2. 修改 image: 标签,例如从 wordpress:6.8.2 改为 wordpress:6.9.0
    3. 在 1Panel 中对该应用执行“重建(Rebuild)”。

“重建”会销毁基于旧镜像的容器,并基于新镜像创建一个全新的容器,然后将包含用户数据的旧数据卷重新挂载到新容器上。这个过程实现了核心程序的原子化、无污染更新,并保留了“一键回滚”(将版本号改回去再重建)的能力。

插件和主题属于用户数据,仍可在 WordPress 后台更新。

配置修改

PHP 参数也通过挂载的配置文件修改。1Panel 的 WordPress 应用模板会在 docker-compose.yml 中设置配置文件挂载,例如把主机上的 ./conf/uploads.ini 挂载到容器内部。

因此,正确的修改流程是:

  1. 在主机上编辑 ./conf/uploads.ini
  2. 重建应用,让新容器加载配置。

上传与超时问题

上传大文件失败

即使在 uploads.ini 中放宽了 PHP 的上传限制,大文件仍可能上传失败。上传请求至少经过主机上的 OpenResty 和容器内的 PHP,两处限制都要检查。在 1Panel 的“网站 > 配置文件”中,可以为对应站点添加 client_max_body_size 128m;

请求超时

上传进度走了一段时间后连接中断,可能是 OpenResty 等待后端容器响应超时。可以在站点配置中检查并按需设置 proxy_connect_timeout 300;proxy_send_timeout 300;proxy_read_timeout 300;

修改主机上的站点配置后,需要重启 openresty 应用使配置生效,无须重建 WordPress 应用。

原笔记由 Gemini 2.5 Pro 整理、构建。

从宝塔迁移 WordPress 到 1Panel 的完整流程

梦随乡兮 梦随乡兮 #网站#数据#安装#配置#宝塔

记录一次把 WordPress 从宝塔迁移到 1Panel 的完整流程,包括网站数据、数据库、伪静态与 SSL 检查。

宝塔越更新广告越多,能理解它要恰饭,但越来越大的广告模块还是让我有点无语。我平时也用不到付费服务,就把博客迁到了当时更清爽的 1Panel。下面是这次迁移的完整过程。

打包网站数据

使用宝塔提供的网站备份和数据库备份即可,备份完成后下载到本地。

安装 1Panel 面板

网站数据备份后,在云服务商处重装系统。我当时用的是 Debian 12,通过 SSH 登录后执行:

Terminal window
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && bash quick_start.sh

其他系统的安装命令可以去 1Panel 官网查看。

面板安装完成后去服务器防火墙放通面板端口,不然打不开面板。

配置 1Panel 面板

进入 1Panel 后台,点击侧栏的“网站”,面板会提示安装 OpenResty。

安装 OpenResty 后,再到“运行环境”安装 WordPress 所需的 PHP:

PHP 版本最好与原宝塔环境保持一致,扩展可以在此时设置,也可以稍后按需修改。

创建网站

等待 PHP 安装完成后,回到网站栏创建网站。这里选择运行环境,不要选择一键部署。

选择刚才安装的 PHP 版本,输入网站域名并确认。

创建网站完成后,点开对应的目录。

1Panel 的网站文件位于 index 目录中,进入该目录后上传网站备份。

然后直接解压到当前目录,解压后务必把压缩文件删掉,避免被他人下载泄露网站密码。

导入成功后,回到网站设置,在网站目录中重新保存权限。缺少这一步时,WordPress 可能因权限错误而无法访问插件、主题或上传图片。

创建数据库

接下来处理数据库。在应用商店中安装原网站使用的数据库,我当时使用的是 MySQL:

安装完成后,到数据库创建。如果你记不住之前网站的数据库名称用户名密码了,不要紧,现在重新设置一个就可以了,等会儿要去修改。

创建完成后导入备份,操作逻辑与导入网站文件相同。如果解压报错,就把数据库压缩文件里的 SQL 文件解压出来上传。

上传完成后点击恢复。

此时网站还没有连接刚创建的数据库。回到网站目录,打开 wp-config.php

把刚才我们设置的数据库名称、用户名、密码分别填写进去。

我当时的 1Panel 环境通过容器连接各组件,因此数据库主机要从 localhost 改为 mysql。如果面板显示了其他连接地址,应以当前环境的实际服务名为准。

改好后保存。

测试网站

此时网站已经可以访问。

我的图片此时仍无法显示,还需要配置伪静态和 SSL 证书。

配置伪静态

打开网站配置,找到伪静态,选择 WordPress,保存并重载。

配置 SSL 证书

打开 HTTPS 设置。我当时使用的 1Panel 版本无法直接在这里申请证书,需要先上传已有证书。

腾讯云或阿里云的域名可以在对应平台申请证书。我当时使用腾讯云,在控制台的 SSL 证书页面自动签发了一张。

签发后,下载 Nginx 服务器版本的证书。

然后打开 1Panel 的“网站 > 证书”,选择上传证书。

填写后确认。

回到网站的 HTTPS 设置,为网站选用刚上传的证书。

迁移完成

再次打开网站,文件、数据库、伪静态和 HTTPS 均已恢复,迁移完成。

希望 1Panel 以后仍能保持界面干净,不要走向过度商业化。

WordPress 缓存:页面缓存、对象缓存与 Redis

梦随乡兮 梦随乡兮 #缓存#页面#数据#对象#页面缓存

解释 WordPress 页面缓存和对象缓存的职责差异,并记录 1Panel 多站环境中的 Redis 配置与排错方法。

页面缓存和对象缓存都带有“缓存”二字,但处在 WordPress 请求链路的不同位置。一个保存最终生成的 HTML,另一个保存数据库查询结果。

动态渲染与预先构建

WordPress:按需实时渲染的动态架构

WordPress 是一个典型的动态内容管理系统(Dynamic CMS)。它的核心工作流是“按需实时生成”:

  1. 访客请求:当浏览器请求一个页面时,请求被发送至服务器。
  2. 服务器处理:服务器上的 PHP 脚本开始执行,向 MySQL 数据库发出多个查询,以获取内容、配置、评论等所有必需的数据。
  3. 实时渲染:PHP 将从数据库取回的数据与主题模板相结合,动态地、实时地生成一个完整的 HTML 页面。
  4. 发送响应:将这个刚刚生成的 HTML 页面发送给访客的浏览器。

这个过程的特点是,页面的构建发生在请求时,每一次(无缓存的)访问都需要消耗 PHP 和数据库的计算资源。

Hugo/Astro:一次性预先构建的静态架构

像 Hugo、Astro 这类工具被称为静态站点生成器(Static Site Generator,SSG)。它们的核心工作流是“预先构建一切”:

  1. 开发构建:在开发阶段,开发者在本地计算机上执行一个构建命令(如hugonpm run build)。
  2. 预先渲染:此时,生成器会读取所有内容文件和模板,一次性地将网站的每一个页面都生成为纯粹的、静态的.html文件。
  3. 部署:开发者只需将这个包含了所有最终成品的文件夹,部署到任何静态文件托管平台。

当访客请求页面时,服务器的唯一工作就是直接返回那个已经存在的.html文件

核心区别:WordPress 的页面是在访客请求时才被创建,而 Hugo/Astro 的页面是在网站部署前就已全部创建完毕。

静态站点生成器在部署前已经生成 HTML 页面,因此不需要用 WordPress 的页面缓存来绕过实时渲染。

页面缓存(Page Cache)

代表插件WP Super Cache, W3 Total Cache, WP Rocket等。

技术要点

  • 缓存内容完整的、最终渲染好的 HTML 页面
  • 工作原理:当访客首次访问页面时,WordPress 正常执行动态渲染。页面缓存插件会捕获这个最终的 HTML 成品,并将其保存为静态.html文件。
  • 加速逻辑:后续访客访问同一页面时,Web 服务器直接返回这个预先生成的.html文件,完全绕过了 PHP 执行和数据库查询
  • 核心目标:将动态请求的昂贵开销(PHP 执行、数据库查询)最小化,极大改善 TTFB(首字节响应时间),使 WordPress 在服务未登录访客时,其响应模式无限接近于静态网站。

对象缓存(Object Cache)

代表方案:使用RedisMemcached作为后端,配合Redis Object Cache等插件。

技术要点

  • 缓存内容重复的、频繁的数据库查询结果,即“对象(Object)”。这包括网站配置项、主题/插件设置、导航菜单、瞬态数据(Transients)等。
  • 工作原理:对象缓存将这些高频查询的结果,存放在一个高速的内存数据库(如 Redis)中。
  • 加速逻辑:当 WordPress 需要这些数据时,它会优先从 Redis 中获取。由于内存的读写速度远超磁盘,这极大地减少了对 MySQL 数据库的直接、重复查询。只有当 Redis 中没有所需数据时,才会查询 MySQL,并将结果存入 Redis 以备后用。
  • 核心目标:减轻数据库的压力,优化和加速 WordPress页面生成的过程本身。它对所有请求(包括后台操作、登录用户访问等无法被页面缓存的场景)都有显著的加速效果。

两种缓存如何配合

对比维度 页面缓存(Page Cache) 对象缓存(Object Cache)
缓存内容 完整的 HTML 页面(最终成品) 数据库查询结果(半成品/原料)
缓存位置 服务器硬盘(速度快) Redis 内存(速度极快)
服务对象 Web 服务器,直接服务于访客 WordPress 程序,加快自身运行
核心职责 加速前端响应与分发 加速后端数据处理与生成
  • 如果只用页面缓存:网站对未登录访客的访问速度极快。但一旦页面缓存失效(如管理员登录后台、用户执行动态操作),WordPress 自身的渲染速度依然受限于数据库查询效率,会导致后台卡顿。
  • 如果只用对象缓存:网站后台和动态功能的响应会变得流畅。但对于海量的匿名访问,WordPress 依然需要为每个请求都执行 PHP 来构建页面,这个过程本身就有资源开销,其效率远不如直接分发一个静态 HTML 文件。

页面缓存适合处理匿名访客的重复访问,对象缓存则覆盖后台、登录用户和其他必须动态渲染的请求。是否同时启用,需要根据站点的动态功能和实际查询压力决定。

WordPress Redis 对象缓存:1Panel、多站隔离与报错排查

梦随乡兮 梦随乡兮 #WordPress#Redis#对象缓存#1Panel#性能优化

把旧的多站点 Redis 配置经验和新的 1Panel 实操流程合并到一篇里,覆盖 Redis 对象缓存启用、连接参数、多站隔离与常见报错排查。

下面的配置基于 1Panel 环境,目标是让 WordPress 连接 Redis,并在多个站点共用 Redis 时隔离缓存键。

安装 Redis 服务与 PHP Redis 扩展

如果你现在跑的是 1Panel 环境,流程可以按下面来:

  1. 在 1Panel 应用商店里安装 Redis
  2. 打开当前 WordPress 所使用的 PHP 运行环境
  3. 在 PHP 扩展里启用 redis
  4. 保存后等待运行环境重建完成

这一步的目标很简单:

  • 服务器里真的有 Redis 服务在运行
  • 当前 PHP 环境真的能调用 Redis 扩展

如果少了任意一个,WordPress 插件都会报“Redis 不可访问”或“扩展不存在”。

安装 WordPress 插件

进入 WordPress 后台,安装并启用 Redis Object Cache 插件。

插件装好后先不要急着点各种优化按钮,先把连接参数写清楚,否则你看到的“可访问 / 不可访问”状态很容易反复横跳。

wp-config.php 里写入连接参数

建议直接在 wp-config.php 中写清楚 Redis 连接信息,而不是完全依赖插件自动探测。

define('WP_CACHE_KEY_SALT', 'example.com');
define('WP_REDIS_SELECTIVE_FLUSH', true);
define('WP_REDIS_HOST', 'redis');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_DATABASE', 0);
define('WP_REDIS_PASSWORD', '替换成你的 Redis 密码');

这几个参数里,最值得解释的是下面两个:

WP_CACHE_KEY_SALT

这是给缓存键加前缀。

如果一台服务器上有多个 WordPress 站点,却共用一套 Redis,这个值几乎就是必填项。建议直接使用站点域名或一个稳定、唯一的站点标识。

WP_REDIS_SELECTIVE_FLUSH

把它设为 true 后,刷新缓存时会尽量只清理当前站点对应的缓存键,而不是把整台机器上共用 Redis 的缓存一把清空。

对于多站点环境,这个参数很有用。

1Panel 与 Docker 环境下的连接问题

1. 为什么 WP_REDIS_HOST 常常写成 redis

在 1Panel 的 Docker 网络里,很多应用是通过 容器名或服务名 互相访问的。

因此只要你的 WordPress 运行环境和 Redis 在同一个可互通网络里,redis 这种主机名通常就能直接使用。

如果你在 1Panel 的“Redis 连接信息”里看到的是别的容器连接地址,也可以按面板显示的值来写,以实际网络环境为准。

2. 插件仍提示 “Redis 不可访问” 时看什么

如果你已经在 wp-config.php 里写了参数,插件仍然报错,优先检查下面几项:

  1. Redis 容器是否真的在运行
  2. PHP Redis 扩展是否已经启用
  3. WP_REDIS_PASSWORD 是否与面板展示的密码一致
  4. WP_REDIS_HOST 是否与容器网络中的实际连接地址一致

如果这些都对,但插件还是无法连通,再去检查 wp-content/object-cache.php 里最终生成的连接参数是否与 wp-config.php 一致。

重点看:

  • 主机地址是不是还停留在错误值
  • 密码有没有被正确带进去

如果插件生成的参数不对,修正后再刷新后台状态页确认。

多站点环境的缓存隔离

早期我在多站点环境里踩过的一个坑,就是多个站点都在用 Redis,但没有做清晰的缓存键隔离。

这会带来两个问题:

  • 某个站点刷新缓存时,可能把别的站点的缓存也一起清掉
  • 站点之间的缓存命中和污染会变得不可控

因此,无论你是旧宝塔环境还是现在的 1Panel 环境,只要是 多站共用 Redis,都建议至少做好这两件事:

  1. WP_CACHE_KEY_SALT 使用唯一前缀
  2. WP_REDIS_SELECTIVE_FLUSH 设为 true

确认对象缓存已经生效

你可以从三个角度确认:

1. 看插件状态页

WordPress 后台的 Redis Object Cache 页面里,会直接显示是否已连接、是否已启用对象缓存。

2. 看 Redis 监控或面板命中情况

如果你刷新几次首页、文章页和后台页面后,Redis 侧出现了命中与读写活动,说明对象缓存已经在工作。

3. 看站点的实际体感

对象缓存不是银弹,但在数据库查询较多的 WordPress 场景里,后台和首页通常会更稳一些,尤其是在小规格 VPS 上。

2C4G VPS 上运行多个 WordPress 站点

梦随乡兮 梦随乡兮 #WordPress#1Panel#OpenResty#LEMP#PHP-FPM#MariaDB#Redis#OPcache#WP Super Cache#Swap#性能优化

记录在 2 核 4G VPS 上使用 1Panel 传统部署(OpenResty + PHP8.4 + MariaDB + Redis)承载 4 个 WordPress 站点的“最小成本优化”过程:PHP-FPM 并发控制、缓存体系(页面缓存/对象缓存/OPcache)、MariaDB 内存与临时表参数收敛、端口暴露检查,以及最后通过 swap 兜底避免突发 OOM。

场景与目标

  • 机器:2C4G VPS(Debian)
  • 面板:1Panel
  • Web:OpenResty
  • PHP:PHP 8.4.x(容器)
  • DB:MariaDB(容器)
  • 缓存:Redis(容器)
  • 站点:3 个在跑 + 准备上第 4 个
  • 特点:无前台会员/购物车;动态主要是搜索、评论;只有站长自己登录后台
  • 目标:不升级 VPS,在现有资源内提升稳定性、降低峰值风险(尤其是 502 / OOM)

先定原则(少折腾也能稳)

  1. 别为了“先进”上 K8s:现状是 LEMP/LEMP+容器混合,先把并发与缓存做对。
  2. 不要为了省资源去降 PHP 版本:省不出多少,风险更大;真正省资源靠缓存与并发控制。
  3. 避免面板不可见的“野改”:能在面板里做的优先在面板做,减少升级覆盖风险。
  4. 先保命再加速:没有 swap 的 4G 机器,突发内存峰值容易直接 OOM。

一、网络暴露与容器通信(避免“看着安全、实际有坑”)

1) 用 docker ps 判断端口是否暴露到宿主机/公网

Terminal window
docker ps --format 'table {{.Names}}\t{{.Ports}}'

当时关键结果(示例):

  • MariaDB:127.0.0.1:3306->3306/tcp
  • PHP:127.0.0.1:9000->9000/tcp
  • Redis:127.0.0.1:6379->6379/tcp
  • OpenResty:Ports 为空(由面板/宿主机层管理 80/443)

结论:

  • DB/PHP/Redis 仅绑定宿主机 127.0.0.1,不对公网开放
  • 因此即使 PHP-FPM 容器内 listen = 0.0.0.0:9000,也不会被公网直接访问(关键在“宿主机端口映射”)。

2) 为什么把 PHP-FPM 改 listen=127.0.0.1 会 502?

因为容器内的 127.0.0.1 只代表容器自身,而 OpenResty 与 PHP 通过宿主机端口映射/容器网络通信;当 PHP 只监听容器内回环地址时,外部自然连不上 → 502。

正确思路:保持容器内 listen=0.0.0.0:9000,并确保宿主机侧只对本地开放(127.0.0.1 映射)即可。


二、PHP-FPM:用“限并发 + 防卡死 + 防膨胀”换稳定

目标

  • 多站共用 PHP 时,最怕某个站/爬虫/慢请求把 worker 占满 → 其他站一起 502。
  • 2 核机器不适合把并发开得很大:宁可排队,也不要 CPU+内存雪崩。

1) 采用 pm = ondemand

适合低中流量、多站点、希望省内存的场景:没有请求就不常驻 worker。

2) 关键参数(最终采用)

  • pm = ondemand
  • pm.max_children = 6(总并发上限)
  • pm.process_idle_timeout = 10s(空闲回收,省内存)
  • pm.max_requests = 500(防进程越跑越胖)
  • request_terminate_timeout = 120s(防卡死拖全站)
  • request_slowlog_timeout = 3s(慢日志阈值,用于抓插件/慢请求)

注意:ondemandpm.start_servers / min_spare / max_spare 不再有意义,建议移除避免误判。

3) 用 FPM status 验收是否需要继续调参

看这些就够:

  • listen queue:长期为 0 = 没排队
  • max children reached:长期为 0 = 并发没撞墙
  • slow requests:接近 0 = 没明显慢请求堆积

三、缓存体系:把 PHP 从请求链路里“尽量拿掉”才省资源

1) 页面缓存(Page Cache):WP Super Cache

所有站点统一使用 WP Super Cache,并采用“简单模式(推荐)”。

建议设置要点:

  • 启用缓存
  • 登录访客不缓存
  • 评论后刷新对应文章缓存
  • 发布或更新内容时清理相关缓存
  • 不缓存带 querystring 的页面(例如 ?x=y
  • 搜索页不缓存(?s=... 关键词无穷多,缓存碎片大且收益小)
  • 垃圾回收/过期清理频率:按站点更新频率设置,不必太激进(避免无意义 IO)

纯内容站 + 匿名访问为主:Page Cache 的收益最大(直接减少 PHP 调用次数)。

2) 对象缓存(Object Cache):Redis

已确认所有站点 Redis 插件连接正常,并且各站点的缓存键前缀不冲突。

对象缓存的价值:

  • 减少重复的 DB 查询与 options 读取
  • 后台编辑、插件较多时更明显

3) OPcache:确认已开启

在 PHP 容器内确认:

Terminal window
docker exec -it PHP8 sh -lc 'php -i | egrep -i "Zend OPcache =>|opcache.enable =>|opcache.memory_consumption =>|opcache.jit" | head -n 30'

关键输出(示例):

  • opcache.enable => On
  • opcache.memory_consumption => 128
  • opcache.jit => disable(WordPress 场景关闭也很正常)

结论:OPcache 已开,128MB 对 4 个小站一般够用;若后续插件/主题变多再考虑增到 192/256。


四、MariaDB:小库别“浪费内存”,把风险上限收紧

背景

  • 数据库体量:几十 MB
  • 站点有 Page Cache + Redis 后,DB 压力本就不大
  • 优化目标不是“跑分”,而是:减少无效占用、降低峰值风险、减少临时表落盘

采取的调整(面板内完成)

  1. 降低 key_buffer_size
  • 原因:MyISAM 用得少,128MB 往往是白占(WordPress 主要 InnoDB)。
  1. 提高 innodb_buffer_pool_size
  • 从过小值提升到更合理区间(例如 256 至 512 MB),避免 InnoDB 频繁折腾磁盘。
  1. 提高 tmp_table_size(并确保 max_heap_table_size 不成为短板)
  • 目的:降低“临时表落盘比例”,减少 IO 抖动。
  1. 降低 max_connections
  • 目的:降低最坏情况下的内存上限,防止异常连接/扫描导致资源被拉爆。
  • 对小站而言,80 至 100 通常足够。

经验判断

  • 小站优化 DB 的关键不是“越大越好”,而是“够用且上限可控”。
  • 真遇到慢,先看慢查询日志/插件行为,而不是盲目扩大各种 buffer。

五、系统层兜底:开启 swap 防止突发 OOM(强烈推荐)

为什么必须要 swap?

4G 机器一旦出现:

  • 突发爬虫、插件内存膨胀
  • 容器瞬间涨内存

在无 swap 情况下容易直接 OOM → 容器被杀 → 站点短暂挂。

最终实施:2GB swap(/swapfile)

关键步骤(已验证可用):

Terminal window
sudo fallocate -l 2G /swapfile || sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
swapon --show
free -h
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

并设置:

Terminal window
printf '\nvm.swappiness=10\nvm.vfs_cache_pressure=50\n' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

目标:

  • swap 用于“兜底”,不要长期依赖;
  • swappiness=10 让系统尽量不碰 swap,仅在内存压力大时使用。

六、上线第 4 个站的“最小风险”步骤

  1. 复制现有栈(Redis + WP Super Cache;OPcache 默认已开)
  2. 评论/搜索做回归:
    • 发表评论后文章页能看到新评论(缓存清理正确)
    • 搜索正常且不出现异常缓存结果(搜索页排除正确)
  3. 观察 1 至 2 天:
    • PHP-FPM status:queue=0、max_children_reached 不增长
    • docker stats:PHP/MariaDB 内存不持续爬升
    • swap 使用量长期接近 0

七、快速巡检命令(只读、随时可跑)

1) 容器资源占用

Terminal window
docker stats --no-stream

2) 系统内存与 swap

Terminal window
free -h
swapon --show

3) OPcache 状态

Terminal window
docker exec -it PHP8 sh -lc 'php -i | egrep -i "Zend OPcache =>|opcache.enable =>|opcache.memory_consumption =>|opcache.jit" | head -n 30'

4) 端口映射(确认不对公网暴露)

Terminal window
docker ps --format 'table {{.Names}}\t{{.Ports}}'

WordPress 后台 524 与 Docker 出网故障复盘

梦随乡兮 梦随乡兮 #WordPress#EdgeOne#1Panel#Docker#iptables#运维#故障排查

一台 2C4G VPS 上用 1Panel(LEMP + Docker)跑多个 WordPress,优化后后台变慢并出现 EdgeOne 524。本文按时间线复盘:如何从“缓存/超时”误区切换到“容器环回 + Docker 出网被 iptables FORWARD 掐死”的根因定位,最终用 host-gateway + iptables 持久化完成修复,并给出可复制的检查清单。

前面的多站优化上线后,WordPress 前台仍然很快,但后台开始出现延迟和 EdgeOne 524。插件与主题安装页也无法正常连接 WordPress.org。

现象与影响

现象 1:后台“转圈几秒”,严重时 524

  • 前台秒开(静态/页面缓存命中)
  • 后台打开任意页面都要等,偶发直接超时(EdgeOne 524)

现象 2:插件/主题安装页报错

  • “发生了预料之外的错误…”
  • 站点健康提示:无法与 WordPress.org 通信、REST API/环回请求失败(超时)

排查过程

1)先从 EdgeOne 缓存模板入手:发现“模板不等于可用”

EdgeOne 的 WordPress 缓存模板存在两个典型坑:

  • 规则里只提供 URL path = 等于,没有“前缀/包含/正则”时,/wp-admin/ 并不能覆盖 /wp-admin/*.php
  • 兜底写法(例如把“全站”写成 URL path = /.*)如果仍是“等于”,就等于没写。

采取的缓存策略(最终稳定版)

EdgeOne 只缓存静态资源,其他一律不缓存:

  • IF:静态后缀(js/css/jpg/png/webp/svg/woff2…)→ TTL 7 天
  • ELSE:不缓存

这能避免 HTML/登录态/REST 被边缘缓存误伤,也让“问题归因”更干净。

注:这一步解决的是“缓存误伤/不可控缓存”,但不是最终根因


2)关键证据:PHP 容器里访问自己站点 REST(wp-json)超时

在宿主机执行:

Terminal window
docker exec -it PHP8 sh -lc 'curl -I -m 10 https://bubaigei.com/wp-json/ || true'

结果:curl: (28) Connection timed out after 10000 milliseconds

这条证据直接说明:后台慢不是浏览器/边缘的问题,是源站(容器)自己发 HTTP 请求就卡住了

为什么会影响文章编辑页?

  • WordPress 后台(特别是 Gutenberg 编辑器)大量依赖 REST API。
  • REST/环回慢 → 任何后台页面都可能“转几秒”。

3)第一次误导:把域名映射到“公网 IP”在容器里仍超时

尝试在 compose 里加:

extra_hosts:
- "bubaigei.com:139.155.110.8"

现象:容器 getent hosts 已指向公网 IP,但 curl https://bubaigei.com/wp-json/ 仍超时。

原因:容器访问宿主机公网 IP 常会遇到 NAT 回环/发夹(hairpin NAT)问题,宿主机自己能通但容器不一定能回环成功。


4)改用 host-gateway(172.17.0.1)

把域名映射改为 host-gateway 并重建容器后,再次验证:

Terminal window
docker exec -it PHP8 sh -lc 'getent hosts bubaigei.com; curl -I -m 10 https://bubaigei.com/wp-json/ || true'

输出解析到 172.17.0.1,并返回 HTTP/2 200

这一步基本解决了:

  • WordPress 后台的 REST/环回超时
  • 后台页面普遍“转圈几秒”的核心卡点
  • EdgeOne 524 的主要诱因(源站迟迟不响应)

5)第二个根因:插件/主题目录仍报错,宿主机能上网,容器不能上网

宿主机:

Terminal window
curl -I -m 10 https://api.wordpress.org/plugins/info/1.2/ || true
# 200 OK

容器(PHP8):

Terminal window
docker exec -it PHP8 sh -lc 'curl -sS -m 10 -o /dev/null -w "HTTP=%{http_code}\n" https://api.wordpress.org/plugins/info/1.2/ || echo FAIL'
# 超时 FAIL

结论:不是 WordPress.org 被墙(宿主机能通),而是 Docker bridge 网络出网被拦


6)快速定位:iptables FORWARD 默认 DROP

查看:

Terminal window
iptables -S FORWARD | head -n 20

结果:-P FORWARD DROP

这会导致:宿主机自己出网没问题,但容器转发/NAT 出网挂掉。


最终修复

A. 让容器内的“站点自请求”走 host-gateway(解决 REST/环回卡顿)

在 PHP8 的 compose 里:

extra_hosts:
- "bubaigei.com:host-gateway"
- "d4.bubaigei.com:host-gateway"

重建:

Terminal window
docker compose up -d --force-recreate

验证:

Terminal window
docker exec -it PHP8 sh -lc 'curl -I -m 10 https://bubaigei.com/wp-json/ || true'

B. 修复 Docker 出网(解决插件/主题目录、更新检查)

1)开启内核转发

Terminal window
sysctl -w net.ipv4.ip_forward=1

2)在 FORWARD 链插入三条放行规则(更稳,比全局 ACCEPT 更克制)

Terminal window
iptables -I FORWARD 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -I FORWARD 2 -i docker0 -o eth0 -j ACCEPT
iptables -I FORWARD 3 -i eth0 -o docker0 -j ACCEPT

如果默认网卡不是 eth0,用 ip route | grep defaultdev 是什么再替换。

3)验证容器出网

Terminal window
docker exec -it PHP8 sh -lc 'curl -I -m 10 https://api.wordpress.org/plugins/info/1.2/ || true'

C. 持久化规则

1)保存 IPv4 规则

Terminal window
mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4

2)安装并启用持久化服务

Terminal window
apt-get update
apt-get install -y iptables-persistent
netfilter-persistent save

3)(可选)生成空的 IPv6 规则文件,避免启动报错

Terminal window
ip6tables-save > /etc/iptables/rules.v6
systemctl restart netfilter-persistent

验收标准

1)容器内 REST 自请求正常

Terminal window
docker exec -it PHP8 sh -lc 'curl -I -m 10 https://bubaigei.com/wp-json/ || true'
# 200

2)容器内能访问 WordPress.org API

Terminal window
docker exec -it PHP8 sh -lc 'curl -I -m 10 https://api.wordpress.org/plugins/info/1.2/ || true'
# 200

3)后台体验恢复

  • 插件 / 主题安装页可正常加载
  • 站点健康不再报 REST/环回/WordPress.org 通信超时
  • EdgeOne 524 显著减少或消失

可继续检查的方向

  • WP-Cron 改系统 cron,减少后台随机卡顿
  • 定期检查 slowlog,定位慢插件/慢查询
  • EdgeOne 单独给后台域名直连源站(admin 子域名不走边缘),体验更稳但配置略多