go服务必须监听0.0.00.0而非127.0.0.1,否则kubernetes探针和外部流量无法连接,导致readiness probe失败、pod反复重启;需用http.listenandserve(":8080", nil)或显式绑定0.0.0.0:8080,并通过netstat验证监听地址为*:8080。

Go服务必须监听 0.0.0.0 而不是 127.0.0.1
Kubernetes Pod 内容器默认只能被同 Pod 或 Service 流量访问,如果 Go 服务只 bind 127.0.0.1:8080,外部探针和流量根本连不上,readinessProbe 会持续失败,Pod 卡在 ContainerCreating 或反复重启。
常见错误现象:kubectl get pods 显示 Ready 0/1,kubectl describe pod 中出现 Readiness probe failed: Get "http://10.244.1.5:8080/ready": dial tcp 10.244.1.5:8080: connect: connection refused。
- 正确写法:
http.ListenAndServe(":8080", nil)(Go 默认绑定所有接口)或显式写http.ListenAndServe("0.0.0.0:8080", nil) - 禁用
localhost、127.0.0.1等本地绑定逻辑,不要在代码里做条件判断 - 验证方式:进容器执行
netstat -tuln | grep :8080,确认监听地址是*:8080或0.0.0.0:8080
livenessProbe 和 readinessProbe 必须配对且路径独立
Kubernetes 不会自动猜你的健康逻辑。/healthz 和 /readyz 是约定俗成路径,但你代码里暴露的 endpoint 名称必须和 YAML 中 path 完全一致,否则探针永远 404,Pod 无法就绪。
关键差异:
-
livenessProbe判断“进程是否还活着”,应只返回 200,不做 DB 查询、不调下游、不等缓存加载 —— 否则容易误杀 -
readinessProbe判断“能否接收流量”,可包含轻量级依赖检查(如 Redis 连通性),但超时必须短(建议 ≤2s),且失败时不终止进程 - 两个 endpoint 必须分开实现,不能共用一个 handler;比如
/health和/ready就比都用/healthz更安全、更易调试
示例片段(Go):
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) })<br>http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {<br> if !dbPing() { w.WriteHeader(503); return }<br> w.WriteHeader(200)<br>})
优雅停机必须响应 SIGTERM 并完成 HTTP Server 关闭
Kubernetes 删除 Pod 前会发 SIGTERM,默认等待 30 秒(由 terminationGracePeriodSeconds 控制),超时则发 SIGKILL 强杀。Go 默认不处理信号,直接退出会导致正在处理的请求被中断。
实操要点:
- 启动 HTTP server 时用
http.Server实例,而非http.ListenAndServe简写 - 注册
os.Signal监听syscall.SIGTERM,收到后调用srv.Shutdown() -
Shutdown()会等待活跃连接关闭(最多给context.WithTimeout设定的时间),期间新连接会被拒绝 - 务必在
Shutdown()后再清理 DB 连接、关闭 goroutine 等资源,顺序不能反
典型结构:
srv := &http.Server{Addr: ":8080", Handler: mux}<br>go func() { log.Fatal(srv.ListenAndServe()) }()<br><br>sigChan := make(chan os.Signal, 1)<br>signal.Notify(sigChan, syscall.SIGTERM)<br><br>ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)<br>defer cancel()<br>srv.Shutdown(ctx)
Deployment YAML 中必须设置 terminationGracePeriodSeconds 和 preStop hook
仅靠 Go 代码处理 SIGTERM 还不够。Kubernetes 默认 terminationGracePeriodSeconds: 30,但如果你的 Go 服务需要 15 秒关连接,而探针又没及时失效,Service 可能还在转发流量过去,造成 5xx。
解决办法是配合 preStop hook 让 Kubernetes 主动把 Pod 从 Endpoints 中摘除,再等 shutdown 完成:
- 在 container spec 中添加:
lifecycle:<br> preStop:<br> exec:<br> command: ["/bin/sh", "-c", "sleep 2"]
—— 给 readinessProbe 一点时间反馈失败,触发 Service 摘流 - 同时设置
terminationGracePeriodSeconds: 25(略大于 Go shutdown timeout),避免被强杀 - 确保
readinessProbe.failureThreshold不设太高(默认 3),否则摘流延迟明显 - 滚动更新时,Kubernetes 默认先启新 Pod、等就绪后再删旧 Pod;但若新 Pod 启动慢(比如要加载大配置),旧 Pod 又没及时下线,就会短暂双写或负载不均 —— 所以
initialDelaySeconds要结合实际初始化耗时设准
最容易被忽略的一点:无论 Go 代码 shutdown 多完善,只要 preStop 缺失或 terminationGracePeriodSeconds 小于 shutdown 超时,就一定会丢请求。这不是“理论上优雅”,而是上线前必须验证的实际行为边界。











