高可用需构建、配置、运行时三阶段协同:静态编译须关CGO_ENABLED,镜像需含证书及时区,健康检查端点/healthz须独立轻量不查依赖,goroutine泄漏须通过context和连接池管控。

直接说结论:高可用不是靠单个模块堆出来的,而是靠构建、配置、运行时三阶段协同控制——缺一不可。你写得再稳的 http.Handler,如果编译没关 CGO_ENABLED、镜像没设资源限制、健康检查没暴露 /healthz,上线后照样被 Kubernetes 杀掉。
静态编译必须关掉 CGO
Go 默认启用 CGO,会链接系统 libc,在 Alpine 或 distroless 镜像里直接报错:standard_init_linux.go:228: exec user process caused: no such file or directory。这不是代码问题,是运行时缺失动态库。
-
CGO_ENABLED=0是硬性要求,尤其在容器环境 - 交叉编译命令要带完整目标平台参数:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o server . - 如果必须用 CGO(比如调用 C 库),就得换基础镜像,比如
gcr.io/distroless/base-debian12,但体积翻倍、攻击面扩大,不推荐
Docker 镜像不能只 COPY 二进制文件
很多团队用 FROM scratch 看似最小,但缺少证书和时区支持,导致 HTTPS 请求失败、日志时间错乱。这不是 bug,是镜像设计缺陷。
- 优先选
alpine:latest或gcr.io/distroless/static-debian12,它们自带 ca-certificates 和 tzdata -
apk --no-cache add ca-certificates必须显式执行,Alpine 的最小镜像默认不带证书包 - 启动前加一行:
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime,避免日志时间戳全为 UTC
健康检查端点必须独立且轻量
Kubernetes 的 livenessProbe 如果指向主业务路由(比如 /),一旦业务逻辑卡住或 DB 连不上,整个 Pod 就被反复重启,形成雪崩。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 单独开一个
/healthz,只检查 goroutine 数、内存使用率、本地监听端口是否就绪 - 不要查依赖服务(如 MySQL、Redis)——那是
readinessProbe的事 - 响应体留空或返回
200 OK即可,别 JSON 序列化、别打日志、别加 trace_id - 超时设为
1s,失败阈值failureThreshold: 3,避免误杀
goroutine 泄漏比 panic 更难发现
线上跑几天后 RSS 内存持续上涨,pprof 看到成千上万个 net/http.(*persistConn).readLoop,基本就是没关 client 连接池或没设 context.WithTimeout。
- 所有对外 HTTP 调用必须带 context:
http.DefaultClient.Do(req.WithContext(ctx)) - 自定义
http.Client时,Timeout和IdleConnTimeout都得设,否则连接永远不回收 - 用
runtime.NumGoroutine()在/debug/vars或自定义指标里暴露,设置告警阈值(比如 >5000 就触发) - 别信 “我用了 defer”,defer 不等于关闭资源;
resp.Body.Close()必须显式调,且要在 error 判断之后
真正卡住人的从来不是语法或框架,而是编译参数、镜像基础、probe 路径、context 传递这四条线——每一条断了,高可用就变成高不可用。

















