Skip to content

Docker 使用 Github Actions 同步镜像到 Aliyun 仓库

创建 Github Actions 配置文件

  • 创建并上传该文件到 github 仓库:.github/workflows/sync-images.yml
  • 创建 Github 仓库密钥: ALIYUN_USERNAME 和 ALIYUN_PASSWORD
    • settings -> Secrets and Variables -> Actions -> New repository secret
    • ALIYUN_USERNAME: 阿里云 docker 拉取镜像时的账号
    • ALIYUN_PASSWORD: 阿里云 docker 拉取镜像时的密码
yml
name: Sync Images to Aliyun ACR
run-name: Copy ${{ github.event.inputs.source }}/${{ inputs.source_repo }} to ${{ github.event.inputs.destination }}/${{ github.event.inputs.destination_namespace }}/${{ inputs.destination_repo }}
on:
  workflow_dispatch:
    inputs:
      source:
        description: '镜像源 (Registry)'
        required: true
        default: 'docker.io'
      destination:
        description: '目标源 (Registry)'
        required: true
        default: 'registry.cn-hangzhou.aliyuncs.com'
      destination_namespace:
        description: '目标源命名空间 (Namespace)'
        required: true
        default: 'aday-sync'
      source_repo:
        description: '仓库及标签 (格式 repo:tag)'
        required: true
        default: ''
      destination_repo:
        description: '目标仓库及标签 (格式 repo:tag)'
        required: true
        default: ''

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Install Skopeo
        run: |
          sudo apt update
          sudo apt install -y skopeo

      - name: Sync multi-arch Image to Aliyun ACR
        env:
          # 源镜像(Docker Hub)
          SRC: '${{ github.event.inputs.source }}/${{ github.event.inputs.source_repo }}'
          # 目标镜像
          DST: '${{ github.event.inputs.destination }}/${{ github.event.inputs.destination_namespace }}/${{ github.event.inputs.destination_repo }}'
        run: |
          skopeo copy \
            --dest-creds "${{ secrets.ALIYUN_USERNAME }}:${{ secrets.ALIYUN_PASSWORD }}" \
            --all \
            "docker://${SRC}" \
            "docker://${DST}"

skopeo 同步镜像时可能发生的异常情况:

  1. unknown manifest class for application/vnd.oci.empty.v1+json" 错误。
  2. unsupported image-specific operation on artifact with type \"application/vnd.docker.attestation.manifest.v1+json\" 错误。

解决办法:

此时可用 Github Actions 中的 docker/build-push-action 插件重新 rebuild 打包再推送。

参考:Dockerfiles仓库下的 .github/workflows/sync-images-rebuild.yml

同步镜像

进入仓库的 Actions, 页面左侧找到 Sync Images to Aliyun ACR workflow, 点击页面右侧 Run workflow 按钮,填写参数,点击 Run workflow 按钮即可。

  • 参数说明:
    • 镜像源 (Registry):docker.io
    • 目标源 (Registry):registry.cn-hangzhou.aliyuncs.com
    • 目标源命名空间 (Namespace):aday-sync
    • 仓库及标签 (格式 repo:tag):debian:12.13
    • 目标仓库及标签 (格式 repo:tag):debian:12.13

一般只需要填写源仓库及标签,以及目标仓库及标签 即可,其它参数默认即可。 aday-sync 为默认 Aliyun ACR 命名空间名称。可以根据实际命名空间的名称修改。

注意:

text
1. tag 名称只能包含小写字母、数字、点、中划线、下划线,因此需要将 tag 中的点替换为中划线。
2. 而很多源镜像的 tag 中带有源 namespace 及中划线,在目标 tag 中必须移除不合法的中划线。
3. 建议用下划线 `_` 代替源镜像中的正斜杠 `/`,避免冲突,及达到看见 tag 名称就可以很方便的推测出源镜像的名称。

示例 1:
源镜像:kasmweb/debian-bookworm-desktop:1.18.0
目标镜像:registry.cn-hangzhou.aliyuncs.com/aday-sync/kasmweb_debian-bookworm-desktop:1.18.0

示例 2:
源镜像:jenkins/jenkins:2.558-jdk21
目标镜像:registry.cn-hangzhou.aliyuncs.com/aday-sync/jenkins_jenkins:2.558-jdk21

使用 skopeo 检查远程仓库同步镜像的 manifest 列表

shell
# 安装
sudo apt install -y skopeo

# 安装 jq 工具,用来格式化输出 json 和过滤
sudo apt install -y jq

skopeo inspect --raw docker://registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 | jq

# 查看 Digest
skopeo inspect --raw docker://registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 | jq '.Digest'

# 查看 manifests 里的 platform
skopeo inspect --raw docker://registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 | jq '.manifests[].platform'

如果无法安装 jq 工具,可以使用 python3(格式化json) + grep(过滤),如下:

shell
# json 格式化输出
skopeo inspect --raw docker://registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 | python3 -m json.tool

# grep 筛选出架构信息。
# -A 3 表示显示匹配行及其后 3 行。'platform' 为筛选关键词。
skopeo inspect --raw docker://registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 | python3 -m json.tool | grep -A 3 'platform'

使用 docker 检查本地拉取的镜像的 manifest 列表

shell
docker pull registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13
# 跨平台镜像(运行时要先安装好 QEMU 模拟器)
docker pull --platform linux/arm64/v8 registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13

# 检查本地拉取的镜像的 manifest 列表
docker manifest inspect registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13

# 可以检测拉取下来的镜像的实际架构(检测可能为空,不靠谱)
docker image inspect registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 | grep Architecture

docker_pull.sh 脚本

从 Aliyun 拉取镜像自动 tag 为 docker hub 原始镜像的 tag。

shell
#!/bin/bash
set -euo pipefail

# 检查参数
if [ $# -ne 2 ]; then
    echo "输入参数错误!"
    echo "=================================================="
    echo "用法: $0 <阿里云完整镜像> <本地目标镜像>"
    echo "示例: $0 registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 debian:12.13"
    echo "=================================================="
    exit 1
fi

# 接收两个参数
ALIYUN_IMAGE="$1"
LOCAL_IMAGE="$2"

echo "============================================="
echo "📥 镜像拉取 & 重打标签"
echo "源镜像:$ALIYUN_IMAGE"
echo "目标镜像:$LOCAL_IMAGE"
echo "============================================="

# 1. 拉取阿里云镜像
echo -e "\n[1/3] 拉取镜像..."
docker pull "${ALIYUN_IMAGE}"

# 2. 打成本地标签
echo -e "\n[2/3] 重打标签:${LOCAL_IMAGE}"
docker tag "${ALIYUN_IMAGE}" "${LOCAL_IMAGE}"

# 3. 删除阿里云标签
echo -e "\n[3/3] 删除源镜像标签..."
docker rmi "${ALIYUN_IMAGE}"

echo -e "\n✅ 操作完成!本地镜像:${LOCAL_IMAGE}"
echo ""
# 列出所有以 LOCAL_IMAGE 名称开头的镜像,只显示前 5 行。
echo -e "\n执行命令:docker images | grep -E "^${LOCAL_IMAGE%%:*}" | head -5"
docker images | grep -E "^${LOCAL_IMAGE%%:*}" | head -3

脚本执行格式:

shell
docker_pull.sh <Aliyun ACR 仓库镜> <Docker Hub 镜像(或者也可以修改为自定义镜像标签>

示例:

shell
./docker_pull.sh registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 debian:12.13

添加到环境变量可读取的位置

shell
# 放到环境变量的位置,并且去掉 .sh 后缀
sudo mv docker_pull.sh /usr/local/bin/docker_pull
# 设置可执行权限
sudo chmod +x /usr/local/bin/docker_pull

为什么要去掉 docker_pull.sh 的 .sh 后缀? 携带 .sh 后缀的脚本执行时也要带 .sh 后缀; 而我们希望执行时直接 docker_pull <阿里云完整镜像> <本地目标镜像>,而不是 docker_pull.sh <阿里云完整镜像> <本地目标镜像>

任意目录直接执行

shell
docker_pull registry.cn-hangzhou.aliyuncs.com/aday-sync/debian:12.13 debian:12.13