scratch镜像无/bin/sh,cmd必须用绝对路径且显式设workdir;cgo_enabled=0和goos=linux须同时生效,否则启动报no such file or directory;项目含cgo时不可用scratch。

scratch镜像里没有/bin/sh,CMD必须用绝对路径
直接写 CMD ["./main"] 在 FROM scratch 镜像里会报错:exec: "./main": stat ./main: no such file or directory。这不是文件没拷进去,而是当前工作目录未设置,相对路径失效。
正确做法是显式设置 WORKDIR 并用绝对路径调用:
WORKDIR /app-
COPY --from=builder /app/main .(确保二进制在/app下) -
CMD ["/app/main"]或更推荐ENTRYPOINT ["/app/main"](让进程成为 PID 1,能正常接收 SIGTERM)
CGO_ENABLED=0 和 GOOS=linux 必须同时生效,否则镜像启动就 panic
只设 CGO_ENABLED=0 但漏掉 GOOS=linux,在 macOS 或 Windows 上构建时,二进制仍可能隐式依赖 host 系统的动态链接库,进容器后报错:standard_init_linux.go:228: exec user process caused: no such file or directory。
构建命令必须写成一行,且两个环境变量都生效:
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main .- 如果项目用了 cgo(比如 SQLite、某些加密库),
scratch就不能用,得换gcr.io/distroless/cc-debian12或alpine并手动apk add依赖
Deployment 中 containerPort 和 Go 监听端口必须一致,否则探针连不上
Kubernetes 的 containerPort 字段虽不强制生效,但它是探针发起请求的依据。如果 Go 代码监听 :8080,而 Deployment 写了 containerPort: 3000,那 readinessProbe 就会一直连 :3000,返回 connection refused。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
实操建议:
- Go 启动日志里加
log.Printf("server started on :8080"),以它为准 - Deployment 中
containerPort设为相同值(如8080) - Service 的
targetPort也必须等于这个值,和containerPort是否一致无关,只看实际监听端口
livenessProbe initialDelaySeconds 太小会导致滚动更新卡住
Go 的 http.ListenAndServe 是阻塞调用,但 Kubernetes 探针在容器启动后立刻发请求。如果 initialDelaySeconds: 5,而服务冷启动要 8 秒(比如加载配置、连 DB、初始化缓存),探针就会在 server 还没 bind 完就返回 503,触发误杀。
避免方式:
- 把
initialDelaySeconds设为至少 10–15 秒(根据你服务真实启动耗时加余量) - readinessProbe 的
initialDelaySeconds可比 livenessProbe 更长(比如 20 秒),因为它还要等依赖就绪 - 代码里别用
time.Sleep模拟等待,改用net.Listen("tcp", ":8080")获取 listener 后再Serve(),确保端口真正 ready
最常被忽略的是:scratch 镜像里没有 /bin/sh,所以任何带 shell 语法的 CMD(比如 CMD server 或 CMD sh -c "exec /app/main")都会失败;还有就是 GOOS=linux 这个参数,很多人只记得关 CGO,却忘了它——跨平台构建时这俩缺一不可。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










