专题 1 个子章节

小服务器生存法则:2C4G VPS 的性能优化记录

梦随乡兮 梦随乡兮 #VPS#性能优化#PHP-FPM#MySQL#OpenResty

记录 2C4G VPS 承载多个 WordPress 站点时,对 PHP-FPM、MySQL 与 OpenResty 进行的分阶段优化。

这台 2 核 4G 的云服务器承载着 3 个 WordPress 网站和 2 个静态页面。准备增加第 4 个 WordPress 站点时,我开始整理 PHP-FPM、MySQL 与 OpenResty 的资源占用。

先处理 PHP-FPM 的闲时进程,再根据实际诊断结果调整数据库和 Web 服务。

优化前的服务器状况

在开始任何操作前,先盘点一下服务器的初始状态,这有助于我们明确优化的目标。

  • 服务器配置:2 核 CPU、4 GB 内存。
  • 承载应用:3 个 WordPress 网站和 2 个静态 HTML 页面。
  • 软件环境:PHP 8.x、MySQL 8.x。
  • 已有优化
    • WordPress 后台已启用缓存插件(如 WP Super Cache)。
    • 启用了 Redis 进行对象缓存。
    • 静态资源(图片、CSS、JS)通过 CDN 分发。

尽管已经做过应用层优化,服务器的闲时内存占用依然不低。我想降低空闲时的资源占用,为第 4 个 WordPress 站点预留空间。

重要前提:在进行任何修改前,我通过服务商的控制台为服务器创建了快照。这是最重要的一步,它保证了在任何操作失误的情况下,我都能快速恢复到原始状态。

调整 PHP-FPM 进程管理器

经过分析,服务器性能瓶颈的首要怀疑对象指向了 PHP-FPM(FastCGI Process Manager)。它是 PHP 与 Web 服务器(如 Nginx)之间的桥梁,负责管理 PHP 的子进程池。它的运行模式,直接决定了服务器内存的消耗方式。

我查看了各个 WordPress 站点对应的 PHP-FPM 池配置文件(通常位于/etc/php/[版本号]/fpm/pool.d/目录下),发现了关键配置:

pm = dynamic

pm代表 Process Manager(进程管理器)。dynamic(动态模式)是很多系统的默认设置。在这种模式下,即使网站没有任何访问,FPM 也会始终保持一定数量的“待命”子进程(由pm.min_spare_servers参数决定)。这些待命进程会持续占用内存,对于我这种访问量不大的个人站点来说,这是一种显著的资源浪费。

切换到 Ondemand 模式

ondemand模式是一种更为智能的进程管理方式。它的工作逻辑是:平时不创建任何子进程,完全不占用内存;只有当第一个 Web 请求到达时,才会“按需”启动一个子进程来处理。处理完成后,如果该进程闲置超过一定时间,就会被自动销毁,从而释放内存。

操作步骤

  1. 备份原有的 FPM 池配置文件。

  2. 编辑文件,修改pm相关的配置。

    修改前:

    pm = dynamic
    pm.max_children = 10
    pm.start_servers = 2
    pm.min_spare_servers = 1
    pm.max_spare_servers = 3

    修改后:

    pm = ondemand
    pm.max_children = 10 ; 这个值暂时保留,代表最多能同时处理 10 个请求
    pm.process_idle_timeout = 10s ; 启用此项,表示进程闲置 10 秒后自动关闭
    ; 以下三行在 ondemand 模式下无效,用分号注释掉
    ; pm.start_servers = 2
    ; pm.min_spare_servers = 1
    ; pm.max_spare_servers = 3
  3. 对服务器上所有WordPress 站点的 FPM 池配置文件重复以上修改。

  4. 执行systemctl restart php-fpm命令重启 PHP-FPM 服务,使新配置生效。

效果立竿见影:重启服务后,通过free -h命令查看,服务器在无访问状态下的可用内存(available)有了非常明显的增加。这第一刀,成功地为我“挤”出了宝贵的内存资源。

为容器化应用对齐环境

在完成已有站点的优化后,我使用 1Panel 的一键部署功能,安装了第四个 WordPress 站点。这引出了新的问题:如何为这个容器化的新站点进行同样的 PHP 优化?

经过探索发现,容器化应用的配置管理遵循不同的哲学。我们无法直接修改其内部的 FPM 配置文件。但 1Panel 的应用模板已经做好了预设,它通过挂载一个外部配置文件的方式来让我们调整 PHP 的核心参数。

操作步骤

  1. 在 1Panel 的文件管理器中,找到新应用目录下的conf/uploads.ini文件。
  2. 编辑此文件,将 PHP 的核心参数(如memory_limit, upload_max_filesize等)与老站点进行对齐。这确保了所有 PHP 应用的负载模型基本一致。
  3. 对于 FPM 的进程管理模式,我们相信 1Panel 使用的官方 WordPress 镜像已经默认采用了ondemand或一个为容器环境高度优化的模式。我们无需、也无法直接干预。
  4. 修改完uploads.ini后,在 1Panel 的应用管理界面对该应用执行“重建 (Rebuild)”操作,使新配置生效。

修改后需要观察 24 至 48 小时,再根据实际负载决定是否继续调整 pm.max_children。这次调整主要降低了空闲时的常驻进程数量,没有代替后续的数据库和请求链路检查。

MySQL 与 OpenResty 的调校记录

梦随乡兮 梦随乡兮 #1Panel#Docker#MySQL#OpenResty#性能优化#VPS

根据 MySQLTuner 的诊断结果调整 MySQL,并检查 1Panel 中 OpenResty 的进程与压缩配置。

用 MySQLTuner 检查数据库

盲目地修改 MySQL 配置是危险且低效的。为了做到“有的放矢”,我决定使用业界著名的开源脚本MySQLTuner来为我的数据库做一次全面的“智能体检”。

挑战:如何给容器里的 MySQL“看病”?

我的第一个挑战不期而至。当我像往常一样,在服务器的主机终端里运行perl mysqltuner.pl时,脚本直接报错退出,提示找不到mysqladmin等命令行工具。

MySQLTuner 脚本运行在宿主机,而它需要的命令行工具和 MySQL 服务都在容器里。相关的容器结构记录在从 1Panel 初探 Docker中。

解决方案是:把“医生”请进“屋里”

  1. 找到 MySQL 容器:在主机终端执行docker ps命令,可以列出所有正在运行的容器。我从中找到了我的 MySQL 容器的名字,形如1Panel-mysql-nA60
  2. 进入容器内部:接着,通过docker exec -it 1Panel-mysql-nA60 /bin/bash命令,我成功地进入了 MySQL 容器内部的命令行。
  3. 在容器内安装依赖:我发现这是一个极其精简的容器镜像,连apt-getyum这些常见的包管理器都没有。经过几次尝试,我最终找到了它内置的轻量级包管理器microdnf。通过执行microdnf install -y wget perl,我成功地为这个“纯净”的容器环境安装了运行 MySQLTuner 所必需的两个工具。

MySQLTuner 的诊断报告与“药方”

在容器内部成功运行perl mysqltuner.pl后,我得到了一份详细的“体检报告”。报告的整体评价非常健康,这得益于 MySQL 8.4.5 版本自身已经相当智能。但在报告最末尾的[Recommendations]部分,它依然给出了两条极具价值的建议:

Variables to adjust:

  • innodb_redo_log_capacity should be (=32M) if possible, so InnoDB Redo log Capacity equals 25% of buffer pool size.
  • innodb_log_buffer_size (> 64M)

这两条建议都指向了 InnoDB 存储引擎的日志系统,这是提升数据库写入性能和稳定性的关键。

  • 关于innodb_redo_log_capacity:报告指出我的重做日志容量(100M)相对于缓冲池(128M)的比例过高,建议调整为缓冲池的 25%,即 32M。这有助于在保证数据安全的前提下,平衡恢复时间和写入性能。
  • 关于innodb_log_buffer_size:报告建议增大日志缓冲区。这是一个在日志写入磁盘前的内存缓冲区,增大它可以有效减少磁盘 I/O。考虑到我服务器内存有限,我没有直接采纳>64M的建议,而是选择了一个更为保守但已足够优化的值16M

“对症下药”:修改配置并重建

拿到了清晰的“药方”,我开始“对症下药”。

  1. 在 1Panel 的文件管理器中,我找到了 MySQL 的配置文件:/opt/1panel/apps/mysql/mysql/conf/my.cnf

  2. 编辑该文件,在[mysqld]区块下,添加了以下两行配置:

    [mysqld]
    # ... 原有配置 ...
    # --- 新增的性能优化配置 ---
    innodb_redo_log_capacity = 32M
    innodb_log_buffer_size = 16M
  3. 保存文件后,回到 1Panel 的应用列表,对MySQL 应用执行了一次“重建 (Rebuild)”操作。当 MySQL 重建完成,针对数据库的优化便正式生效了。

检查 OpenResty 配置

优化完数据库后,我将目光投向了 Web 服务的总入口 OpenResty,需要确认它的全局配置是否与这台小服务器的硬件规格相匹配。

在 1Panel 后台,我找到了 OpenResty 的全局配置,其对应的文件路径为/opt/1panel/apps/openresty/openresty/conf/nginx.conf

经过检查,我发现 1Panel 的默认配置已经相当出色:

  • worker_processes auto;:会根据服务器的 CPU 核心数自动启动对应数量的工作进程。
  • worker_connections 1024;:每个工作进程能处理 1024 个连接,总并发处理能力达到 2048,对于我的网站规模来说绰绰有余。

我只调整了 gzip_types,在默认配置上补充 JSON、RSS 和 SVG 等文本格式。

我将gzip_types这一行修改为:

gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss image/svg+xml;

修改并保存后,对OpenResty 应用执行了一次“重启”,使 Gzip 压缩策略覆盖得更为全面。

这次检查后,OpenResty 的进程配置保持原值,只补充了 gzip_types。MySQL 参数来自当时的 MySQLTuner 输出,不适合作为其他服务器可以直接照抄的通用配置。