ImagePullBackOff主因是镜像不可达、认证缺失、标签错误或节点无镜像;需依次验证镜像地址与标签、Secret配置、节点网络与证书、仓库可达性及镜像是否已推送至集群可访问仓库。

直接上手就能跑通,不需要先搭 CI/CD 或买云服务。本地构建镜像 + kubectl apply 就能完成一次真实部署,但有几个关键点不处理好,Pod 会卡在 ImagePullBackOff 或 CrashLoopBackOff 状态。
本地构建镜像时必须用多阶段构建
Go 应用静态编译后是单二进制文件,没必要把整个 golang 镜像塞进生产环境。多阶段构建能砍掉 90% 以上的镜像体积,还能避免 CGO_ENABLED=1 带来的 Alpine 兼容问题。
- 第一阶段用
golang:1.21-alpine编译,COPY go.mod go.sum再go mod download,比直接COPY . .更利于 Docker 缓存复用 - 第二阶段推荐
FROM scratch(不是alpine),只要加CGO_ENABLED=0 GOOS=linux,生成的二进制就能跑;如果用了net/http的 DNS 解析,得换回alpine并RUN apk --no-cache add ca-certificates - 别漏掉
WORKDIR和EXPOSE 8080,Kubernetes 不依赖EXPOSE,但人看 YAML 时会下意识查这个端口是否对齐
docker build 后必须推送到可被集群访问的仓库
Kubernetes 集群节点默认无法访问你本机的 docker daemon,所以 docker build -t localhost:5000/myapp . 这种本地 registry 方案只适合单节点 Minikube 测试,生产环境必须推到外部仓库。
- 用
docker tag my-go-app:latest your-registry.com/myorg/myapp:v1.2.3打标,版本号别用latest,否则Deployment不会触发滚动更新 - 私有仓库需要提前在集群里创建
Secret:kubectl create secret docker-registry regcred --docker-server=your-registry.com --docker-username=xxx --docker-password=xxx - Minikube 用户可以跳过推送:用
eval $(minikube docker-env)把本地docker指向 Minikube 内置 daemon,再docker build -t my-go-app .,然后image: my-go-app就能直接拉到
Deployment 中的 imagePullPolicy 容易被忽略
本地测试时经常写 imagePullPolicy: Never,但这只在镜像真正在节点上存在时才生效。一旦换了节点、清了缓存或用了 Kind 集群,Never 会导致 ErrImageNeverPull。
- 开发阶段建议统一用
imagePullPolicy: IfNotPresent,既不会强制重拉,又比Always少一次网络校验 - 如果你用的是
scratch镜像,注意它不含/bin/sh,exec进容器会失败,调试只能靠日志或livenessProbe返回码 -
resources.requests必须设,哪怕很小,否则 Kubernetes 调度器可能把 Pod 分配到没资源的节点上,状态卡在Pending
探针配置不对,服务就“假活”
Go 服务启动快,但 HTTP server 启动和路由注册、DB 连接池初始化不是原子操作。readinessProbe 如果没设 initialDelaySeconds,Kubernetes 可能在 handler 还没注册完时就把流量切过去,返回 404。
-
livenessProbe的initialDelaySeconds要 ≥ 应用冷启动耗时(比如 DB 连接超时 + 迁移执行时间),否则会反复重启 - 路径别写成
/healthz却在代码里只实现了/health,404 就算失败,Pod 会被杀掉 - Go 程序要监听
os.Signal处理SIGTERM,否则kubectl delete pod后进程不退出,Kubernetes 等到terminationGracePeriodSeconds(默认 30s)才强杀,影响滚动更新速度
最常被跳过的其实是健康检查端点本身的健壮性——/ready 应该检查数据库连接、下游服务连通性等真实依赖,而不是只返回 200 OK。这点在本地跑通后,一上测试环境就暴露。


















