goctl kube deploy生成的YAML不可直接上生产,必须手动修改镜像、探针路径(/healthz需确保注册,/readyz需显式添加)、命名空间、containerPort(默认80需改为8888)、selector与template labels严格一致,否则Pod卡在ContainerCreating或CrashLoopBackOff。

直接上生产环境的 goctl kube deploy 生成 YAML 不可直接用,必须手动改镜像、探针路径、命名空间、端口和 label 匹配项,否则 Deployment 卡在 ContainerCreating 或反复 CrashLoopBackOff。
goctl kube deploy 生成的 YAML 哪些字段必须改
这个命令只校验目录结构和配置文件存在性,不验证服务能否真正启动。生成后不改以下字段,90% 的部署会失败:
-
image字段默认是your-repo/{name}:latest—— 必须替换成 CI 打出的真实 tag,例如harbor.example.com/gozero/user-api:20260920-abc123;否则 K8s 认为镜像未变,跳过拉取,Pod 卡住 -
livenessProbe和readinessProbe默认都指向/healthz,但 go-zero 默认只注册了/healthz(liveness),没注册/readyz(readiness);若要用 readiness,得在main.go里显式加engine.GET("/readyz", ...)并做轻量依赖检查 -
namespace默认是default—— 生产环境必须改成prod、staging等实际命名空间,否则资源混杂、RBAC 和网络策略全部失效 -
ports.containerPort默认是80,而 go-zero HTTP 服务默认监听8888—— 不匹配会导致 Service 流量进不来,Pod 日志完全收不到请求 -
selector.matchLabels和template.metadata.labels必须完全一致(键、值、大小写、空格都不能差)—— 这是 Kubernetes 强制校验点,不一致会报invalid label selector或field is immutable,后续所有更新都会被拒绝
Go 服务代码里必须暴露 /healthz 和 /readyz
Kubernetes 的探针不会猜你的健康逻辑,它只发 HTTP GET 请求并看状态码。没这两个端点,或者返回非 2xx,Pod 就会被反复重启或无法加入 Service 流量池。
- 最简写法(
net/http):http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) });/readyz可加 DB 连通性检查,但别太重(比如超时控制在 2 秒内) - 用
gin框架:r.GET("/healthz", func(c *gin.Context) { c.Status(200) }) - 监听地址必须是
0.0.0.0:8888,不能写成127.0.0.1:8888—— Pod IP 是网卡地址,Kubelet 探针从宿主机 namespace 发起请求,连不上就判失败 - 启动日志里加
log.Printf("server started on :8888"),方便排查CrashLoopBackOff是卡在启动阶段还是探针阶段
Dockerfile 必须用多阶段构建 + CGO_ENABLED=0
镜像体积大、启动慢、运行时报 no such file or directory,90% 是 Dockerfile 写错了。Go 编译产物是静态二进制,没必要带 Go 工具链或 libc。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 构建阶段用
golang:1.22-alpine,关键命令必须含CGO_ENABLED=0 GOOS=linux go build -a -ldflags "-s -w" -o main .—— 关 CGO 防 Alpine 下 musl libc 报错,-s -w剥离调试信息,体积减少 30%~40% - 运行阶段必须切到
scratch或alpine:latest;FROM scratch最安全(无 shell、无包管理器),但若用了 cgo(如 sqlite),就得用alpine并apk add对应依赖 - 最终镜像里不能有
go run main.go或go命令 —— 那会把整个 Go 环境打进镜像,体积动辄 800MB+ - 必须用非 root 用户运行:
USER 65532:65532,避免容器逃逸后获得高权限
Deployment 中 livenessProbe 和 readinessProbe 必须分离
共用一个探针路径是线上雪崩常见诱因:DB 暂时不可用 → readinessProbe 失败 → 流量被摘;但如果它同时是 livenessProbe,就会触发重启 → 更加重 DB 压力。
-
livenessProbe应只检查进程存活,路径建议用/livez,不做任何外部依赖调用,timeoutSeconds: 2,initialDelaySeconds: 10 -
readinessProbe才该检查真实就绪态,路径用/readyz,可查 DB 连接池、下游 gRPC 健康、缓存连通性等,timeoutSeconds: 10,initialDelaySeconds: 20(留足启动时间) - 两者
failureThreshold建议设不同值:liveness设低(如 3),快速重启;readiness设高(如 10),避免滚动更新时误摘流量 - main 函数里必须监听
SIGTERM并调用http.Server.Shutdown()—— 否则 K8s 发信号后进程立刻退出,正在处理的请求直接中断,不是“不优雅”,是线上 5xx 根源
最容易被忽略的是 selector.matchLabels 和 template.metadata.labels 的严格一致性,以及 containerPort 与 Go 服务实际监听端口的对齐——这两处错一个字符,Deployment 就起不来,且错误提示非常模糊,得靠 kubectl describe deploy 和 kubectl get events 交叉比对才能定位。

















