Go服务在Kubernetes中健康检查端口必须与探针配置、应用监听端口、NetworkPolicy三者严格对齐;需用独立轻量health server(如:8081)暴露/livez和/readyz,禁用中间件,且YAML中httpGet.port、containerPort、ListenAndServe端口须完全一致,否则导致CrashLoopBackOff或NotReady。

Go 服务在 Kubernetes 中的健康检查端口不是“随便配一个能通就行”,而是必须和探针配置、应用监听、网络策略三者对齐,否则 livenessProbe 会持续失败,readinessProbe 会永远不就绪,Pod 卡在 CrashLoopBackOff 或 NotReady 状态。
healthz 端口必须独立于业务端口暴露
很多团队图省事,让 Go 服务只监听一个端口(比如 :8080),然后把 /healthz 和业务接口混在同一 HTTP server 里。这会导致三个实际问题:
- 业务中间件(JWT 验证、日志、限流)可能拦截
/healthz,返回401或超时,探针直接判失败 - 业务请求压高时,HTTP server 队列积压,
/healthz响应延迟超过timeoutSeconds,被 kubelet 当作不可用 - 滚动更新时,新 Pod 还没加载完配置,但业务端口已监听,
readinessProbe误判为就绪,流量打入后 503
正确做法是:启动一个**专用的、轻量的 health server**,监听独立端口(如 :8081),不挂任何中间件,不共享连接池或上下文。示例代码:
go func() {
mux := http.NewServeMux()
mux.HandleFunc("/livez", livezHandler)
mux.HandleFunc("/readyz", readyzHandler)
log.Println("Health server listening on :8081")
http.ListenAndServe(":8081", mux) // 注意:不加 err check,避免阻塞主流程
}()
这个端口要和 YAML 中的 port 字段严格一致,比如 httpGet.port: 8081。
YAML 中的 port 是容器端口,不是宿主机端口
Kubernetes 探针里的 port 指的是容器内部监听的端口号,和 containerPort 是同一概念,和 NodePort、hostPort 完全无关。常见错误包括:
- 写成宿主机映射端口(如
31234),导致探针连不上 - 写成 Service 的
targetPort,但该值未在容器内监听 - 用了命名端口(如
name: health),但容器ports列表里没定义同名端口
必须确保以下三点一致:
- Go 代码中
ListenAndServe(":8081", ...) - Deployment 的
containers.ports包含{name: health, containerPort: 8081} - 探针配置中
httpGet.port: 8081或httpGet.port: health
startupProbe + readinessProbe 组合时,端口复用要格外小心
慢启动 Go 服务(如加载大配置、预热缓存)常配 startupProbe,但它和 readinessProbe 如果指向同一个端口+路径,容易引发竞争:
-
startupProbe成功后退出,但readinessProbe立即开始探测,此时 DB 连接可能还没建好 - 两个探针共用一个 handler,而该 handler 在初始化完成前返回 503,
readinessProbe就一直失败
建议拆开:
-
startupProbe用/livez(只检查进程存活,不查依赖) -
readinessProbe用/readyz(检查 DB、Redis、下游 gRPC 等) - 两个路径都走
:8081端口,无需额外开监听器
同时注意 initialDelaySeconds 要大于服务真实就绪时间,否则 failureThreshold 再高也救不回来。
非 HTTP 服务(如 Kafka 消费者)怎么配端口
纯后台 Go 服务(无 HTTP server)不能靠 httpGet,但也不该退回到 exec 执行 ps aux | grep 这种不可靠方式。正确路径是:
- 仍然启动一个最小化
net/httpserver(哪怕只 listen:8081),暴露/livez和/readyz -
/livez检查主 goroutine 是否 panic、关键 channel 是否 closed -
/readyz检查 consumer 是否 connected、是否收到过消息、internal queue length - 探针全部走
httpGet,端口仍设为8081,和 HTTP 服务配置完全一致
这样既保持运维一致性,又避免 exec 探针因 shell 解析、PATH、权限等引入不确定性。Kubernetes 不关心你业务逻辑,只认稳定、可测、低开销的响应。
最常被忽略的一点:健康端口的防火墙和 NetworkPolicy 必须放行 kubelet 流量——它从节点网络空间发起探测,不是从 Pod 内部。如果用了 strict NetworkPolicy,记得加一条 from: {namespaceSelector: {}, podSelector: {matchLabels: {component: "kubelet"}}},否则探针永远超时。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











