跳到主要内容

autodok:让 CI 流水线按需更新容器的轻量工具

阅读需 3 分钟

容器更新工具里 watchtower 名气最大,但它的思路是轮询:每隔一段时间去问镜像仓库「有没有新镜像」。轮询间隔摆在那里,更新不够即时,还得给它开仓库的读权限。

autodok 走的是另一条路:不轮询,只等通知。它暴露一个 HTTP API,CI 流水线构建完新镜像后 POST 一下,它就把镜像拉下来、用原配置重启容器。主动权在流水线手里,更新时机精确到「这次构建」。

项目地址​

项目非常精简,只暴露两个端点:

端点鉴权作用
GET /health不需要健康检查,Docker daemon 可达时返回 200
POST /update需要 API Key拉取指定镜像并用原配置重启指定容器

推荐的工作流:

  1. 推送代码触发 CI
  2. CI 构建并推送新镜像
  3. CI 向 autodok 发 POST /update,带上容器名和新镜像 tag
  4. 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 上去敲命令干净得多。项目很小,源码读完不用一顿饭的时间,值得上手一试。