symfony 6.4 健康检查端点需验证数据库、缓存、文件系统等关键依赖,返回200/503/500状态码;路由必须为/public /healthz且置于防火墙后;k8s探针须设x-forwarded-proto头并匹配容器端口。

在 Symfony 6.4 应用中为 Kubernetes 或 Prometheus 等运维监控系统提供可靠健康检查端点,不能只返回 HTTP 200,必须验证关键依赖(数据库连接、缓存、文件系统写入权限)是否真实可用,否则 Pod 会误判为“就绪”却实际无法处理请求。
添加健康检查路由和控制器
在 config/routes.yaml 中新增一条不带任何安全拦截的公开路由:
health_check: path: /healthz controller: App\Controller\HealthCheckController::index methods: [GET]
这一步必须放在所有 security.firewalls 路由之后,否则会被防火墙拦截导致返回 401;【/healthz 必须是根路径且无前缀】,K8s liveness/readiness probe 默认不带 Host 头或重写路径,加前缀会导致探测失败。
编写 HealthCheckController
在 src/Controller/HealthCheckController.php 中创建控制器:
使用 Symfony\Component\HttpFoundation\Response 直接返回 JSON,禁止调用 Twig 或任何可能触发缓存/日志的组件;状态码必须严格区分:200 表示完全就绪,503 表示未就绪(K8s 会停止转发流量),500 表示故障(触发重启)。
构造函数中注入 PdoConnection(或 Doctrine\DBAL\Connection)、CacheInterface(如 cache.app)、Filesystem(来自 symfony/filesystem),全部设为可选(?前缀),避免因某项不可用导致构造失败而无法进入 execute 流程。
在 index() 方法中逐项执行轻量级探测:尝试 $this->connection?->executeStatement('SELECT 1'),用 $this->cache?->get('health:probe', fn() => 'ok') 触发一次读写,用 $this->filesystem?->write('/tmp/health-test', 'x') + unlink() 验证 var/log 所在卷的写权限。任意一项失败,立即返回 503 并带上失败项名称,不要继续执行后续检查。
配置 K8s Probe(非 Symfony 代码,但必须同步完成)
在 Deployment 的 spec.containers.livenessProbe 和 readinessProbe 中统一使用:
httpGet: path: /healthz port: 80 httpHeaders: - name: X-Forwarded-Proto value: https
必须显式设置 X-Forwarded-Proto 头,否则 Symfony 的 Request::isSecure() 在反向代理后会误判为非 HTTPS,导致部分依赖安全上下文的逻辑(如 session cookie 标记)提前报错退出;【probe 必须与容器内监听端口一致,不能写 service port 名称】,K8s 不解析 service 层,直接访问容器 IP:PORT。
livenessProbe 初始延迟设为 60 秒,避免应用尚未完成 OPcache 预热或缓存冷启动时被误杀;readinessProbe 延迟设为 30 秒,失败阈值设为 1,确保流量只打到真正就绪的实例。
验证健康检查行为
第一步:本地运行 php bin/console server:run,用 curl 访问 http://127.0.0.1:8000/healthz,确认返回 {"status":"ok"} 且状态码为 200。
第二步:临时禁用数据库服务,再次请求,应返回类似 {"status":"error","failed":"database"} 且状态码为 503。
第三步:将应用部署到 Kubernetes 集群,执行 kubectl get pods -w,观察 Pod 状态从 ContainerCreating → Running → Ready 的完整流转;若卡在 Running 但未变 Ready,说明 readinessProbe 返回非 200,需立刻查看容器日志定位哪项探测失败。











