之前我已经在另一篇博客中介绍过了,在小项目的异步 cd 中表现不错,它可以帮助我们自动拉取最新的镜像并更新容器。

如果你正在使用 Watchtower 来自动更新 Docker 容器,并使用 Amazon ECR (Elastic Container Registry) 作为你的私有镜像仓库,你可能会遇到一个棘手的问题:no basic auth credentials. Proceeding to next.

ECR 的身份验证机制是动态的,依赖于临时的 AWS 凭证,而不是静态的用户名和密码,所以需要一些额外的配置来帮助我们解决拉取 ECR 中镜像的问题。通过使用 amazon-ecr-credential-helper(一个 Docker 凭证助手)来解决这个问题。

解决方案概览

整体的解决方案其实官方在文档中已经给出,https://containrrr.dev/watchtower/private-registries/#credential_helpers

不过由于文档比较旧了,其中有一些部分需要调整一下。整体的策略是构建 amazon-ecr-credential-helper 二进制文件,将其存储在一个 Docker 卷(Volume)中,然后配置 Watchtower 容器来使用这个助手和相应的 Docker 配置文件。

步骤一:构建凭证助手 (Credential Helper)

首先,我们需要编译这个助手。我们不会将它安装在主机上,而是使用一个临时的 Docker 容器来构建它,并将结果输出到一个共享卷中。

创建 Dockerfile

创建一个名为 Dockerfile 的文件,内容如下

1
2
3
4
5
6
7
FROM golang:1.24

ENV CGO_ENABLED=0

RUN go install github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login@latest

WORKDIR /go/bin/

注意这里的内容就和官方的不一样,官方的镜像版本太老,导致无法正确安装

构建并将二进制文件存入卷

运行以下命令来创建卷、构建镜像,并运行一个临时容器将编译好的 docker-credential-ecr-login 二进制文件复制到卷中:

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 创建一个用于存储二进制文件的卷
$ docker volume create helper

# 2. 使用上面的 Dockerfile 构建镜像
$ docker build -t aws-ecr-dock-cred-helper .

# 3. 运行容器,将 /go/bin (包含二进制文件) 挂载到 helper 卷
# 容器启动后即退出,但二进制文件已保存在卷中
$ docker run -d --rm --name aws-cred-helper --volume helper:/go/bin aws-ecr-dock-cred-helper

# 4. 然后查看是否存在 helper,如果存在就表示已经可以了
$ docker volume ls

步骤二:配置 Docker 凭证

在你的工作目录(例如与 docker-compose.yml 同级)创建一个 .docker/config.json 文件。

注意:请将 <AWS_ACCOUNT_ID><AWS_ECR_REGION> 替换为你的实际 AWS 账户 ID 和 ECR 区域。

1
2
3
4
5
6
7
8
9
10
11
12
{
"credsStore" : "ecr-login",
"HttpHeaders" : {
"User-Agent" : "Docker-Client/19.03.1 (XXXXXX)"
},
"auths" : {
"<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_ECR_REGION>.amazonaws.com" : {}
},
"credHelpers": {
"<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_ECR_REGION>.amazonaws.com" : "ecr-login"
}
}

步骤三:配置 Watchtower (Docker Compose)

现在我们将所有部分组合在一起。创建一个 docker-compose.yml 文件来启动 Watchtower。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
services:
myapp:
image: 1651561651.dkr.ecr.us-east-1.amazonaws.com/myapp:latest
container_name: myapp
restart: unless-stopped
# 通过配置 label 能告诉 watchtower 需要关注哪些镜像的更新
labels:
- "com.centurylinklabs.watchtower.enable=true"
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./config.json:/config.json
- helper:/go/bin
environment:
- HOME=/
- PATH=$PATH:/go/bin
- WATCHTOWER_POLL_INTERVAL=10 # 间隔时间 10s 用于测试,实际可以根据所需调整
- WATCHTOWER_MONITOR_ONLY=true
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_CLEANUP=true
- AWS_REGION=us-east-1
- AWS_ACCESS_KEY_ID=AKxxxxx
- AWS_SECRET_ACCESS_KEY=xxxxx

# 最重要的是这个,需要将我们之前的 helper 挂载进去
volumes:
helper:
external: true

然后,你就可以进行测试了,启动后可以查看 watchtower 的日志可以看到是否已经能正常的拉取镜像,而没有出现认证的错误了。

需要注意的是,在实际的测试过程中发现有时候第一次部署的时候如果没有按照部署的步骤来操作,如果先部署了 watchtower 然后再重新挂载的镜像,一定要注意先删除实例 (docker compose down),以及清空 helper docker volume rm -f helper 然后重新执行步骤,从而能避免无法正确挂载或正常执行的问题。

总结

通过构建 amazon-ecr-credential-helper 并将其与 Watchtower 容器共享(通过 Docker 卷),同时提供正确的 Docker 配置文件和 AWS 环境变量,我们就设置了一个安全且自动化的流程。现在,Watchtower 可以监控你的 ECR 仓库,并在新镜像发布时自动更新你的服务。