Golang应用在K8s中的优雅关闭与启动探针配置

2次阅读

pod启动即被杀因livenessprobe初始延迟过短,应设initialdelayseconds为10或30秒;go需显式监听sigterm并用shutdown()优雅关闭;readinessprobe须与业务端口一致;terminationgraceperiodseconds需与shutdown超时对齐。

Golang应用在K8s中的优雅关闭与启动探针配置

Pod 启动后立即被 kill – 检查 livenessProbe 初始延迟是否过短

很多 Go 应用在 K8s 里刚启动几秒就被重启,不是程序崩了,而是 livenessProbe 太急——它默认从容器启动就开始计时,而 Go 的 http server 可能还没 bind 成功,更别说加载配置、连 DB、预热缓存了。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • initialDelaySeconds 至少设为 10(简单服务)或 30(带初始化逻辑的),别信“我代码很快”——K8s 节点 IO、网络、调度延迟都不可控
  • 如果用 httpGet 探针,确保 handler 不做重操作:比如不要在 /healthz 里查 mysql 主从延迟,只检查本地 listener 是否就绪
  • 避免用 exec 执行 curlnetstat:容器里可能没装,且 shell 启动有开销,httpGet 更轻量也更稳定

Go 程序收不到 SIGTERM 或关不干净 —— 必须显式监听并阻塞主 goroutine

K8s 发送 SIGTERM 后,若 Go 进程 main 函数已退出(比如启了个 goroutine 就 return),容器会立刻终止,正在处理的 HTTP 请求、数据库事务、消息确认全丢。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • signal.Notify 监听 os.Interruptsyscall.SIGTERM,收到后触发 graceful shutdown 流程
  • HTTP server 关闭必须用 srv.Shutdown(),不能只调 srv.Close() —— 后者会立刻断连,前者会等活跃请求完成(可配超时)
  • main 函数末尾要 select{}time.Sleep(time.Hour) 阻塞住,否则进程提前退出,信号根本来不及响应
  • 第三方库(如 ginecho)的 graceful shutdown 要看文档,有些需额外中间件或包装器,别假设它自动支持

readinessProbe 返回 200 但流量仍打进来 —— 检查探针路径与业务 handler 是否共用同一端口和 mux

常见错误是把 /readyz 和业务路由注册到不同 HTTP server(比如一个走 :8080,一个走 :9090),但 readinessProbe 配的却是 :8080/readyz,而实际流量全打在 :9090 上。结果探针永远成功,但业务根本没起来。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • 确保 readiness 探针路径(如 /readyz)和业务 HTTP server 是同一个实例,共享 http.ServeMuxrouter
  • 探针 handler 应检查关键依赖:DB 连接池可用、redis ping 通、必要配置已加载。但别检查外部依赖全链路(比如调第三方 API),那会让 readiness 变得脆弱
  • 如果用多端口(如 metrics 单独开 :9090),readinessProbe 仍应指向主业务端口,否则 K8s 认为“服务就绪”,其实流量无处可去

容器退出码非 0 却没进 CrashLoopBackOff —— 查 terminationGracePeriodSeconds 和 preStop 生命周期

有时 Go 程序 panic 退出,exit code 是 2,但 Pod 状态卡在 Terminating 好几分钟才重建,或者干脆没重建——这通常是因为 terminationGracePeriodSeconds 设得太大(默认 30 秒),而 preStop 没配或执行失败,导致 K8s 等不到容器自然退出,最后强制发 SIGKILL

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • terminationGracePeriodSeconds 建议设为 30(够多数 Go 应用 graceful shutdown),别设成 300 —— 等五分钟?用户早刷出空白页了
  • preStop 最好配个 exec,比如 sleep 2,给 Go 程序留出接收 SIGTERM 的窗口;如果用 httpGet,注意它不保证一定触发,不如 exec 可靠
  • 在 preStop 里别做重操作(比如 dump 内存),它本身没有超时控制,失败会导致整个 termination 卡住

最常被忽略的一点:Go 的 http.Server.Shutdown() 超时时间,和 K8s 的 terminationGracePeriodSeconds 必须对齐。前者比后者长,shutdown 永远完不成;后者比前者短,K8s 直接杀进程,前者的超时就白设了。

text=ZqhQzanResources