多阶段构建是Go应用Docker打包的硬性要求,必须用golang:alpine构建阶段(禁用CGO、加-ldflags优化),运行阶段优先选scratch或alpine;Kubernetes中需合理配置resources、liveness/readiness探针,并通过Secret/ConfigMap注入配置,严禁敏感信息硬编码。

Go应用用Docker打包、再扔进Kubernetes跑,不是“能不能”,而是“怎么配才不翻车”。核心就两条:镜像得小且安全,YAML得稳且可观察。
Dockerfile必须用多阶段构建
单阶段直接FROM golang:latest跑生产?CPU和内存会告诉你什么叫“自讨苦吃”。多阶段是硬性要求,不是优化项。
- 构建阶段用
golang:1.21-alpine或更高(别用latest,版本漂移会破坏CI一致性) - 务必加
CGO_ENABLED=0 GOOS=linux,否则二进制会动态链接libc,进scratch或alpine直接exec format error - 最终运行镜像优先选
scratch(零依赖)或alpine:3.20(需ca-certificates和时区支持),别用debian或ubuntu基础镜像 - 编译时加
-ldflags "-s -w",去掉调试符号,镜像体积能砍掉30%以上
典型错误:standard_init_linux.go:228: exec user process caused: no such file or directory——八成是没关CGO,或者COPY路径写错,比如COPY --from=builder /app/main .但实际build输出在/app/server。
Deployment里资源限制和探针不能省
Kubernetes不会替你管OOM或假死。不设resources.limits,Pod可能被节点OOMKilled;不配livenessProbe,goroutine泄漏后服务还挂着“Running”状态。
-
requests决定调度——设太低,Pod可能挤在一台Node上;设太高,资源浪费且调度失败率上升。常见起点:cpu: "250m",memory: "256Mi" -
limits必须≤节点可用资源,且建议limits.memory比requests.memory高1.5~2倍,给GC留余地 -
livenessProbe路径必须是真实健康检查端点(如/health),initialDelaySeconds要大于应用冷启动时间,Go HTTP服务常设30秒 -
readinessProbe延迟可以更短(5秒),确保流量只打到真正ready的实例
典型错误:CrashLoopBackOff持续出现,查日志发现panic: runtime error: invalid memory address——其实是limits.memory设太小,GC频繁触发+内存不足导致崩溃,不是代码问题。
环境变量和配置文件要从镜像里剥离
把数据库地址、密钥写死在main.go或Dockerfile里,等于把生产密码塞进Git历史。Kubernetes提供了标准解法,不用自己造轮子。
- 敏感配置(密码、token)走
Secret,挂载为文件或环境变量,type: Opaque即可 - 非敏感配置(日志等级、超时时间)用
ConfigMap,更新后滚动重启自动生效 - 不要在Dockerfile里
COPY configs/ ./configs然后硬编码路径——改个配置就得重build镜像 - Go代码里用
os.Getenv()读环境变量,或用viper统一管理,避免flag混用
典型错误:config.yaml里写db_url: "mysql://root:123456@db:3306/app",镜像推到公共仓库后密码泄露。正确做法是db_url: "mysql://$(DB_USER):$(DB_PASS)@$(DB_HOST):$(DB_PORT)/app",由K8s注入环境变量。
本地验证不能跳过docker-compose
直接丢Deployment到K8s集群调,等于蒙眼开车。先用docker-compose up验证单机多容器协作是否正常,能提前暴露80%的网络和配置问题。
- Compose里用
depends_on控制启动顺序,但注意它只等容器“start”,不等服务“ready”——数据库端口开了≠MySQL已accept连接 - 加
healthcheck字段,让Compose真正等服务就绪:healthcheck: { test: ["CMD", "mysqladmin", "ping", "-h", "localhost"], timeout: 20s, retries: 10 } - 服务间调用用服务名(
user-service),不是localhost——这和K8s Service DNS机制对齐,避免上线后改代码 - Compose文件里
build.context指向项目根目录,确保Dockerfile和go.mod路径一致
最后提醒一句:scratch镜像里没有sh,kubectl exec -it pod-name -- sh会失败。调试时临时切回alpine,上线前再换回来——这个细节,90%的人第一次都会卡住。


















