容器更新工具里 watchtower 名气最大,但它的思路是轮询:每隔一段时间去问镜像仓库「有没有新镜像」。轮询间隔摆在那里,更新不够即时,还得给它开仓库的读权限。
autodok 走的是另一条路:不轮询,只等通知。它暴露一个 HTTP API,CI 流水线构建完新镜像后 POST 一下,它就把镜像拉下来、用原配置重启容器。主动权在流水线手里,更新时机精确到「这次构建」。
项目地址
- GitHub:cars10/autodok(Rust 编写,MIT 协议)
- Docker Hub:cars10/autodok
项目非常精简,只暴露两个端点:
| 端点 | 鉴权 | 作用 |
|---|---|---|
GET /health | 不需要 | 健康检查,Docker daemon 可达时返回 200 |
POST /update | 需要 API Key | 拉取指定镜像并用原配置重启指定容器 |
推荐的工作流:
- 推送代码触发 CI
- CI 构建并推送新镜像
- CI 向 autodok 发
POST /update,带上容器名和新镜像 tag - autodok 拉镜像、按原配置(卷、环境变量等)重启容器
部署方式
用 docker compose 起一个容器就行,官方仓库自带 compose.yml:
services:
autodok:
image: cars10/autodok
container_name: autodok
restart: always
#ports:
# - 3000:3000
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
#- /home/user/.docker/config.json:/config.json
三个关键点:
- 挂载
docker.sock:autodok 靠它操作本机容器,这是它能「重启别的容器」的前提 ./data目录:API Key 持久化在这里,启动后从日志(docker compose logs -f autodok)或./data/api_key文件里都能拿到- 端口默认注释掉了:推荐用 nginx / traefik 反代一个域名出来(比如
autodok.example.com),比直接暴露端口好管理,也方便流水线跨机访问
如果从私有镜像仓库拉镜像,把最后一行 volume 的注释去掉,挂上你 docker login 过的 config.json,autodok 会复用里面的凭据。
curl 手动触发
最朴素的用法,排查问题或临时更新时很实用:
# 健康检查(不需要鉴权)
curl -X GET "http://autodok.example.com/health"
# 触发更新:把容器 myapp 更新到 myapp:1.2.0
curl -X POST "http://autodok.example.com/update" \
-H "Authorization: $API_KEY" \
-H "content-type: application/json" \
-d '{"container": "myapp", "image": "registry.example.com/myapp:1.2.0"}'
# 成功响应
# {"message":"Container 'myapp' restarted with new image 'registry.example.com/myapp:1.2.0'"}
container 必须是正在运行中的容器名;image 必须是仓库里已存在的镜像和 tag。
与 GitLab CI 结合
在 .gitlab-ci.yml 里,构建推送镜像之后加一个 deploy 阶段:
stages:
- build
- deploy
build-image:
stage: build
script:
- docker build -t registry.example.com/myapp:$CI_COMMIT_SHORT_SHA .
- docker push registry.example.com/myapp:$CI_COMMIT_SHORT_SHA
deploy:
stage: deploy
image: curlimages/curl:latest
script:
- |
curl -X POST "$AUTODOK_URL/update" \
-H "Authorization: $AUTODOK_API_KEY" \
-H "content-type: application/json" \
--max-time 300 \
-d "{\"container\": \"myapp\", \"image\": \"registry.example.com/myapp:$CI_COMMIT_SHORT_SHA\"}"
rules:
- if: $CI_COMMIT_BRANCH == "main"
两个变量在 GitLab 项目 Settings → CI/CD → Variables 里配置:AUTODOK_URL(autodok 的访问地址)和 AUTODOK_API_KEY(记得勾选 Masked)。deploy 阶段的 runner 用 curlimages/curl 镜像即可,不需要 docker。
与 Jenkins 结合
Jenkinsfile 里同样在推送镜像后加一步:
stage('Deploy') {
steps {
sh '''
curl -X POST "$AUTODOK_URL/update" \
-H "Authorization: $AUTODOK_API_KEY" \
-H "content-type: application/json" \
--max-time 300 \
-d '{"container": "myapp", "image": "registry.example.com/myapp:'"$GIT_COMMIT_SHORT"'"}'
'''
}
}
AUTODOK_URL 和 AUTODOK_API_KEY 放在 Jenkins 凭据里(Credentials Binding 注入为环境变量),GIT_COMMIT_SHORT 可以在前面 stage 里用 git rev-parse --short HEAD 提取。
如果装了 HTTP Request 插件,也可以不用 curl,写成声明式:
stage('Deploy') {
steps {
httpRequest url: "${env.AUTODOK_URL}/update",
httpMode: 'POST',
contentType: 'APPLICATION_JSON',
customHeaders: [[name: 'Authorization', value: env.AUTODOK_API_KEY]],
requestBody: '{"container": "myapp", "image": "registry.example.com/myapp:latest"}',
timeout: 300
}
}
实践中的几个坑
- 超时设置:
POST /update的耗时 = 拉镜像 + 重启容器。大镜像可能要几分钟,除了 curl 的--max-time,前面反代(nginx 的proxy_read_timeout)和 CI 客户端的超时也要同步放大,否则镜像还没拉完请求先断了——注意请求断了不影响 autodok 继续执行,只是你拿不到结果 - 一次只能更新一个容器:要更新多个容器就发多个请求,串行执行
- API Key 保管:它等于这台机器容器层的操作权限,别写死在仓库里,一律走 CI 变量/凭据注入
小结
autodok 的功能边界很清晰:不做镜像扫描、不做编排、不轮询,就老老实实提供「给我一个容器名和新镜像,我帮你原地更新」的 API。对「代码 push 后希望测试环境立刻更新」这类场景,比 watchtower 的轮询更精准,也比在流水线里直接 ssh 上去敲命令干净得多。项目很小,源码读完不用一顿饭的时间,值得上手一试。