《PHP 怎么就绪探针?从原理到实战,一次讲透 K8s 健康检查的“隐形守护者”》**

目录导读
- 什么是就绪探针?为什么 PHP 应用需要它?
- PHP 就绪探针 vs 存活探针:傻傻分不清?
- 手写 PHP 就绪探针:三行代码的“生死判决”
- 进阶:探针性能陷阱与容器编排的“优雅停服”
- 常见问题 FAQ:PHP-FPM 僵死、数据库延迟、Nginx 缓冲
- 没有探针的 PHP,就像没有安全带的赛车
什么是就绪探针?为什么 PHP 应用需要它?
在 Kubernetes(K8s)或 Docker Compose 的编排世界里,就绪探针(Readiness Probe) 是容器对外宣告“我准备好了,可以接收流量”的机制,对于 PHP 问题很现实:PHP 是“请求-响应”模型,没有常驻内存进程(除常驻 Worker 或 Swoole 外),当你的 Deployment 滚动更新时,旧的 Pod 被终止,新的 Pod 启动——但新 Pod 的 PHP 代码可能需要加载配置、连接数据库、预热 OpCache,如果此时负载均衡直接转发流量,用户就会看到 502 或超时。
就绪探针会定期检查一个 HTTP 端点(/healthz),只有返回 200 且响应体正确,K8s 才把 Pod 标记为 “Ready”,流量才会流入,这就是 PHP 在容器世界里的“隐形守护者”。
PHP 就绪探针 vs 存活探针:傻傻分不清?
这是最常见的混淆点,用一句话区分:
- 存活探针(Liveness):问“进程还活着吗?死了就重启。”
- 就绪探针(Readiness):问“进程能干活吗?不能就先别给它流量。”
一个反直觉的例子:假设你的 PHP 应用依赖 MySQL,MySQL 挂了,但 PHP 进程本身没死——此时存活探针会认为“正常”,而就绪探针应该返回 503。这样 K8s 不会重启 Pod(避免雪崩),但会将流量摘除,让运维有时间修复数据库。
手写 PHP 就绪探针:三行代码的“生死判决”
最标准的做法是创建一个 public/healthz.php 文件,内容如下:
<?php
// 检查核心依赖(示例:Redis、数据库)
$ok = @fsockopen('mysql-host', 3306, $errno, $errstr, 2);
if (!$ok) {
http_response_code(503); // 未就绪
exit('db unavailable');
}
http_response_code(200);
echo 'ready';
在 K8s 配置中,就绪探针可以这样写:
readinessProbe:
httpGet:
path: /healthz.php
port: 80
initialDelaySeconds: 5 # 启动后等5秒再检查
periodSeconds: 10 # 每10秒检查一次
failureThreshold: 3 # 连续失败3次才标记为未就绪
注意:PHP 的 fsockopen 超时设为 2 秒,如果数据连接超时时间过长,探针本身会拖慢整个 Pod 的健康评估。
进阶:探针性能陷阱与容器编排的“优雅停服”
探针开销
如果探针每次都要查数据库,高并发下会打爆连接池,建议:探针只检查“轻量级”依赖(如 file_exists 检查会话目录),而把重依赖检查交给更长的间隔(如每 30 秒)。
优雅停服(Graceful Shutdown)
当 K8s 决定删除一个 Pod 时,会先调用 SIGTERM 信号,但 PHP-FPM 默认不一定响应这个信号,你需要确保在 php-fpm.conf 中配置 process_control_timeout,并在代码中注册 pcntl_signal 处理函数,让当前正在处理的请求完成后再退出,否则正在下载文件的用户会突然断连。
常见问题 FAQ
Q1:PHP-FPM 僵死(zombie)了,探针能检测到吗?
可以,但要看探针类型,HTTP 探针会请求 Nginx,Nginx 发现 FPM 无响应会返回 502,此时探针返回 503,Pod 会被摘除流量,但更好的做法是用 TCP 探针直接检测 9000 端口。
Q2:就绪探针和 Nginx 缓冲冲突怎么办?
如果你使用 Nginx 作为反向代理,注意设置 proxy_read_timeout 小于探针的 timeoutSeconds,否则探针可能一直挂起。
Q3:为什么 MySQL 慢查询导致探针 503?
探针里的连接超时设置太短(2秒),但 MySQL 被慢查询拖到 CPU 100%,连接建立失败,这不是“未就绪”,而是“性能劣化”,建议在探针中不检查数据库实时状态,而是检查一个本地缓存文件(如 apcu 存的时间戳)。
PHP 就绪探针不是“锦上添花”,而是容器化 PHP 应用的刚需,它解决了两个致命问题:滚动更新时的新旧 Pod 切换,以及依赖服务故障时的流量优雅摘除,记住两个核心公式:
- 就绪探针 = 流量开关(不重启进程)
- 存活探针 = 进程急救(重启进程)
写一个只检查 php-fpm.pid 是否存在的探针,是最容易但防御力最低的做法,真正的生产级探针,必须根据你的业务依赖(Redis、MySQL、外部 API)分层设计,在微服务大行其道的今天,让 PHP 在 K8s 里“稳稳地连接”而非“狂暴地重启”,这才是探针存在的终极意义。
(全文完,约 1280 字)