Go通过docker/client SDK调用Docker API管理容器:需root权限访问docker.sock,正确处理镜像拉取流、日志解析及健康检查轮询,优先使用Docker原生restart policy。
Go 语言本身不直接“管理 Docker 容器”,而是通过调用 docker CLI 或对接 docker.sock 的 HTTP API 实现自动化运维。真正可行、生产可用的方式是使用官方 SDK:github.com/docker/docker/api/types 及其配套客户端 github.com/docker/docker/client。
docker/client 连接本地 Docker Daemon默认情况下,Docker 守护进程监听 unix:///var/run/docker.sock。Go 程序需以 root 或 docker 用户组权限运行,否则会报错 permission denied while trying to connect to the Docker daemon socket。
实操要点:
import "github.com/docker/docker/client"
client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) 最稳妥,它自动读取 DOCKER_HOST 和 DOCKER_API_VERSION 环境变量client.NewClientWithOpts(client.WithHost("unix:///var/run/docker.sock"), client.WithAPIVersionNegotiation())
cli.Ping(ctx) 是否返回 nil,避免后续操作 panic常见误区是直接调用 ImagePull 后立刻 ContainerCreate,但 ImagePull 返回的是一个 io.ReadCloser 流,需消费完才能确保镜像就绪;否则可能遇到 No such image 错误。
正确做法:
cli.ImagePull(ctx, "nginx:alpine", types.ImagePullOptions{}) 获取
响应流json.NewDecoder(resp).Decode(&pullEvent) 持续读取直到出现 "status": "Download complete" 或 "status": "Already exists"
cli.ContainerCreate(...),注意 Config.Image 必须与拉取的镜像名完全一致(含 tag)HostConfig.NetworkMode 设为 "bridge" 或 "host",别留空,否则默认是 "default",但部分 Docker 版本不认这个字符串resp, _ := cli.ImagePull(ctx, "redis:7-alpine", types.ImagePullOptions{})
defer resp.Close()
decoder := json.NewDecoder(resp)
for {
var event map[string]interface{}
if err := decoder.Decode(&event); err != nil {
break
}
if status, ok := event["status"].(string); ok && (status == "Download complete" || status == "Already exists") {
break
}
}
用 ContainerLogs 默认返回 raw stream,每条日志前带 8 字节头(含日志类型、长度),直接读会乱码。必须用 types.ContainerLogsOptions{Follow: true, ShowStdout: true, Timestamps: true} 并配合 stdcopy.StdCopy() 解包。
关键细节:
io.Copy(os.Stdout, logs) —— 它不解析日志头,输出不可读stdcopy.StdCopy(os.Stdout, os.Stderr, logs)(需导入 github.com/moby/stdcopy)"ERROR" 的日志),应先用 bufio.Scanner 按行拆分,再匹配;不能在 raw stream 上做字符串搜索Follow: true 时,logs 是长连接,需显式 ctx 控制超时或取消Docker 原生支持 HealthCheck,但 Go SDK 不提供“监听健康状态变更”的事件接口。所谓“自动重启”必须轮询 + 判断 + 主动操作,容易引发竞争和重复触发。
实际建议:
on-failure:5),由 daemon 层保障,比应用层轮询更可靠HealthCheck.Interval(例如设为 30s,则轮询至少 45s 一次),否则可能拿到过期状态cli.ContainerInspect(ctx, id),读取 Health.Status 字段,值为 "healthy" / "unhealthy" / "starting"
ContainerStop,等待几秒再 ContainerStart,避免 container is not running 报错真正的难点不在代码怎么写,而在于如何区分“临时网络抖动导致健康检查失败”和“进程已彻底卡死”。这需要结合日志上下文、资源指标(cgroups)、甚至外部探针,纯靠 Health.Status 做决策风险很高。