部署Golang服务到Kubernetes必须确保监听地址(0.0.0.0:PORT)、containerPort、探针port、Service targetPort四者严格一致,否则Pod虽显示Running却无法响应请求;须监听0.0.0.0、配分离的HTTP探针、静态编译、非root运行并正确处理SIGTERM。

部署 Golang 接口服务到 Kubernetes,不是“写完 http.ListenAndServe 就能跑”,而是必须让监听地址、容器端口声明、探针配置、Service 转发四者完全对齐,否则会卡在 Running 状态但无法响应任何请求。
Go 代码必须监听 0.0.0.0:PORT,不能用 127.0.0.1 或空 host
本地 go run main.go 能通,不代表部署后可用。Kubernetes 中每个 Pod 有独立网络命名空间,127.0.0.1 只指向容器自身,kube-proxy、Service、甚至同 Pod 内的 istio-proxy 都连不上。
-
http.ListenAndServe("127.0.0.1:8080", nil)→ 必然失败,curl超时,kubectl describe pod里可能看到Failed to connect to port -
http.ListenAndServe(":8080", nil)→ 行为因 Go 版本而异,某些版本仍默认绑定127.0.0.1,不保险 - 推荐写法:
http.ListenAndServe("0.0.0.0:"+port, nil),其中port来自os.Getenv("PORT"),默认"8080" - 如果用
http.Server,务必先net.Listen("tcp", addr),再.Serve(lis),避免探针在 socket bind 完成前就发起请求
containerPort、探针 port、targetPort 必须数值一致
这四个地方只要一个错位,就会出现 connection refused 或流量静默丢失。它们不是“建议一致”,而是 Kubernetes 流量路径的硬性依赖。
-
containerPort在 Deployment 的containers[].ports[]下,仅作元数据声明,但缺失或写错会导致探针失败、Service找不到目标端口 -
livenessProbe.httpGet.port和readinessProbe.httpGet.port必须等于containerPort,不能写成 Service 的port -
Service.spec.ports[].targetPort必须等于containerPort(可写数字,也可写名字,但名字必须和containerPort.name一致) - 别在 Dockerfile 里漏掉
EXPOSE 8080—— 虽非强制,但它是团队端口意识的锚点,漏了容易连锁出错
必须配两个分离的 HTTP 探针,且路径真实存在
Kubernetes 不看进程是否 running,只认探针返回码。没探针,或路径 404、状态码不是 2xx/3xx,Pod 就永远 Ready: 0/1 或反复重启。
立即学习“go语言免费学习笔记(深入)”;
-
/healthz(或/livez)专用于livenessProbe:只检查进程存活,不做 DB 查询,返回200 OK -
/readyz专用于readinessProbe:检查 DB 连接、下游服务可达性等真实就绪条件 -
initialDelaySeconds得留够启动时间:Golang 启动慢(尤其带 migration 或 cache warmup),livenessProbe建议 ≥30s,readinessProbe可设 5–10s - 别用 TCP 探针替代 HTTP 探针 —— TCP 成功只说明端口开了,不代表业务已 ready,滚动更新时极易误切流量
Dockerfile 必须静态编译 + 非 root 用户 + scratch 镜像
镜像一运行就报 standard_init_linux.go:228: exec user process caused: no such file or directory?八成是 CGO 没关,Alpine 镜像找不到 glibc。
- 构建阶段必须加
CGO_ENABLED=0 GOOS=linux,配合-ldflags '-s -w -extldflags "-static"' - 运行阶段用
FROM scratch或gcr.io/distroless/static-debian12,体积小、无 shell、无包管理器,攻击面极低 - 加
USER 65532:65532以非 root 用户运行,避免容器逃逸后获得高权限 - 别用
golang:alpine直接当运行镜像 —— 它带编译器、apk、sh,纯属冗余且危险
最容易被忽略的其实是 label 对齐和信号处理:Deployment 的 selector.matchLabels 和 Pod template 的 labels 必须逐字相同(包括空格和大小写),而 main 函数里没捕获 SIGTERM 并调用 http.Server.Shutdown(),会导致滚动更新时请求被直接中断。


















