PHP 应用迁移到 Kubernetes 后,PHP-FPM 单 Pod 的性能调优和传统服务器不一样——容器资源受限、进程数要和容器资源匹配、Opcache 在容器里的行为也有差异。今天把 K8s 环境下 PHP-FPM 单 Pod 的调优经验分享出来。
一、问题现象
迁移到 K8s 后,PHP-FPM Pod 经常 502,监控显示 PHP-FPM 进程数打满,CPU 使用率不高但请求排队。单 Pod QPS 只能到 200 左右,同样的代码在物理机上能到 500。
二、进程数配置
问题
PHP-FPM 默认 pm = dynamic,pm.max_children = 5,进程数太少,并发请求排队。但盲目调大又会导致内存不足 OOM。
解决方案
先算单个 PHP-FPM 进程的内存占用,再根据 Pod 内存限制算 max_children:
| |
假设单进程 60MB,Pod 内存限制 1GB(系统和其他进程留 200MB):
PHP-FPM 配置:
| |
pm = static:固定进程数,避免动态创建销毁的开销pm.max_requests = 1000:每个进程处理 1000 个请求后重启,防止内存泄漏
三、Opcache 优化
问题
容器里 Opcache 命中率只有 60%,比物理机低很多。原因是容器每次重启 Opcache 都清空,而且 validate_timestamps 频繁检查文件更新。
解决方案
| |
关键点:
validate_timestamps=0:生产环境代码不经常变,关闭时间戳检查,命中率提升到 95%+- 代码更新通过重新构建镜像发布,不需要 Opcache 自动刷新
preload预加载框架核心类,性能再提升 20%
四、镜像优化
问题
PHP 镜像 800MB+,启动慢,拉取慢。
解决方案
多阶段构建 + 精简基础镜像:
| |
用 Alpine 基础镜像,最终镜像从 800MB 降到 150MB。
五、资源限制
K8s Pod 资源请求和限制:
| |
- CPU limit 不要设太低,PHP-FPM 是 CPU 密集型,限制太严会导致请求排队
- 内存 limit 要和 PHP-FPM max_children 匹配,防止 OOM
六、效果对比
| 指标 | 调优前 | 调优后 | 提升 |
|---|---|---|---|
| 单 Pod QPS | 200 | 800 | 300% |
| P99 延迟 | 800ms | 200ms | 75%↓ |
| Opcache 命中率 | 60% | 96% | - |
| 镜像大小 | 800MB | 150MB | 81%↓ |
| 502 错误率 | 5% | 0.1% | 98%↓ |
七、总结
K8s 环境下 PHP-FPM 调优核心:
- 进程数匹配资源:根据单进程内存和 Pod 内存限制算 max_children,不是越大越好
- static 模式:固定进程数,避免动态创建开销
- Opcache 关闭时间戳检查:生产环境代码通过镜像发布,不需要自动刷新
- 预加载:PHP 7.4+ 用 preload 预加载核心类
- 镜像精简:Alpine + 多阶段构建,减小镜像体积
- 资源限制合理:CPU 和内存 limit 要和 PHP-FPM 配置匹配
PHP-FPM 在容器里的调优和物理机思路一样,但要更注意资源限制和进程数的匹配。容器资源是隔离的,超了就 OOM,不能像物理机那样"超用一点没关系"。