go程序需在main函数中显式调用os.exit(n)(n≠0)使docker识别失败;panic默认退出码为2,不推荐;应封装run()函数返回Error,统一处理退出码,并用docker inspect或$?验证exitcode。

Go程序如何让Docker知道它失败了
Go进程退出时返回非零状态码,Docker才能把容器标为 Exited (1) 而不是 Exited (0)。默认情况下,log.Fatal 或 os.Exit(1) 是有效的,但很多人用 panic 或忽略错误,导致容器“假成功”。
-
panic不会触发 Docker 的失败识别——它只是崩溃,但 Go 运行时默认 exit code 是 2,而部分 Docker 版本或编排工具(如 kubernetes)对非 1 状态码处理不一致,建议显式控制 - 主函数末尾没写
os.Exit(0)也不影响,Go 默认成功返回 0;但若中间有os.Exit(n),后续逻辑就不再执行,要小心位置 - 使用
log.Fatal等价于fmt.Println + os.Exit(1),可用,但不适合需要自定义错误码的场景(比如数据库连不上返回 10,配置缺失返回 20)
main() 函数里怎么安全地传递不同错误码
别在任意位置调用 os.Exit,尤其别在 goroutine 里——它会直接终止整个进程,可能丢数据、漏清理。应该把错误集中到 main() 出口处统一处理。
- 把启动逻辑封装成返回
error的函数,比如func run() error,main 中用if err := run(); err != nil { os.Exit(1) } - 需要区分错误类型?用自定义错误并实现
Interface{ Code() int },例如:type ExitError struct{ msg string; code int } func (e *ExitError) Error() string { return e.msg } func (e *ExitError) Code() int { return e.code }然后
main中检查并提取err.(interface{ Code() int }).Code() - 注意:不要用
errors.Is或errors.As直接断言*exec.ExitError——那是子进程错误,不是你自己的退出码
Docker run -i –rm 启动时看不到 exit code?
本地调试时,docker run 默认不显示容器退出码,只打印日志。你得主动查,否则以为程序“没报错”,其实早就 os.Exit(1) 了。
- 加
--rm后容器自动删,必须立刻用$?拿上一条命令的退出码:docker run --rm myapp; echo "exit code: $?" - 或者用
docker run -d启动后查:docker inspect <cid> --format='{{.State.ExitCode}}'</cid> - 如果看到
ExitCode: 0但日志里明明有failed to connect,说明你的 Go 代码根本没调os.Exit,可能只是fmt.printf了错误然后默默结束了
为什么 Kubernetes 里 Pod 状态是 CrashLoopBackOff 却 log 显示 success
常见原因是:Go 程序启动后很快退出(比如配置校验失败),但没设好 health check,K8s 认为它“启动即死”,反复重启;而你只看 kubectl logs,没看 kubectl describe pod 里的 Exit Code 和 Reason 字段。
立即学习“go语言免费学习笔记(深入)”;
- K8s 的
livenessProbe或readinessProbe失败不会改容器退出码,它们只影响 Pod 状态;真正决定CrashLoopBackOff的是容器本身退出且RestartPolicy=Always(默认) - 检查
describe pod输出里的Last State→Terminated→Exit Code,再对照 Go 代码里os.Exit的参数 - 一个典型坑:用
time.Sleep模拟 long-running 服务,但忘了加select {}或信号监听,main 函数执行完就退出,code 0 —— K8s 看到“正常退出”,但业务根本没起来
Docker 和 Go 都不替你决定“什么算失败”,这个判断权和出口控制必须收在 main 函数里,而且得让 exit code 可观测、可追溯。别依赖日志关键词,更别靠猜。