如何实现Artifactory与ACR间Docker镜像自动同步?求GitLab CI脚本
实现Artifactory与ACR的Docker镜像自动同步(GitLab CI可用)
核心逻辑
- 定期查询Artifactory目标镜像的所有标签
- 对比上次同步记录,筛选出新增的
image:tag - 根据同步方向执行拉取/推送操作
- 更新同步记录,避免重复处理
Bash 脚本方案
#!/bin/bash set -euo pipefail # 配置参数(推荐在GitLab CI变量中定义) ARTIFACTORY_REGISTRY="your-artifactory-registry-url" ARTIFACTORY_REPO="your-docker-repo-name" ARTIFACTORY_USER="$ARTIFACTORY_USER" ARTIFACTORY_PASS="$ARTIFACTORY_PASS" ACR_REGISTRY="your-acr-name.azurecr.io" ACR_USER="$ACR_USER" ACR_PASS="$ACR_PASS" IMAGE_NAME="your-image-name" # 同步方向:push_to_acr 或 pull_to_artifactory SYNC_DIRECTION="push_to_acr" # 存储上次同步标签的文件 LAST_SYNC_TAGS="./last_sync_tags.txt" # 初始化同步记录文件 if [ ! -f "$LAST_SYNC_TAGS" ]; then touch "$LAST_SYNC_TAGS" fi # 获取Artifactory当前所有标签 get_artifactory_tags() { curl -s -u "$ARTIFACTORY_USER:$ARTIFACTORY_PASS" \ "$ARTIFACTORY_REGISTRY/v2/$ARTIFACTORY_REPO/$IMAGE_NAME/tags/list" | \ jq -r '.tags[]' } # 获取新增标签 current_tags=$(get_artifactory_tags) new_tags=$(comm -13 <(sort "$LAST_SYNC_TAGS") <(echo "$current_tags" | sort)) if [ -z "$new_tags" ]; then echo "No new tags to sync, exiting." exit 0 fi # 登录镜像仓库 docker login "$ARTIFACTORY_REGISTRY" -u "$ARTIFACTORY_USER" -p "$ARTIFACTORY_PASS" docker login "$ACR_REGISTRY" -u "$ACR_USER" -p "$ACR_PASS" # 执行同步 for tag in $new_tags; do echo "Syncing $IMAGE_NAME:$tag..." if [ "$SYNC_DIRECTION" = "push_to_acr" ]; then docker pull "$ARTIFACTORY_REGISTRY/$ARTIFACTORY_REPO/$IMAGE_NAME:$tag" docker tag "$ARTIFACTORY_REGISTRY/$ARTIFACTORY_REPO/$IMAGE_NAME:$tag" "$ACR_REGISTRY/$IMAGE_NAME:$tag" docker push "$ACR_REGISTRY/$IMAGE_NAME:$tag" elif [ "$SYNC_DIRECTION" = "pull_to_artifactory" ]; then docker pull "$ACR_REGISTRY/$IMAGE_NAME:$tag" docker tag "$ACR_REGISTRY/$IMAGE_NAME:$tag" "$ARTIFACTORY_REGISTRY/$ARTIFACTORY_REPO/$IMAGE_NAME:$tag" docker push "$ARTIFACTORY_REGISTRY/$ARTIFACTORY_REPO/$IMAGE_NAME:$tag" fi echo "Synced $IMAGE_NAME:$tag successfully." done # 更新同步记录 echo "$current_tags" > "$LAST_SYNC_TAGS" # 登出(可选) docker logout "$ARTIFACTORY_REGISTRY" docker logout "$ACR_REGISTRY"
Python 脚本方案
import os import subprocess import requests from typing import List # 配置参数(从环境变量读取,适配GitLab CI) ARTIFACTORY_REGISTRY = os.getenv("ARTIFACTORY_REGISTRY") ARTIFACTORY_REPO = os.getenv("ARTIFACTORY_REPO") ARTIFACTORY_USER = os.getenv("ARTIFACTORY_USER") ARTIFACTORY_PASS = os.getenv("ARTIFACTORY_PASS") ACR_REGISTRY = os.getenv("ACR_REGISTRY") ACR_USER = os.getenv("ACR_USER") ACR_PASS = os.getenv("ACR_PASS") IMAGE_NAME = os.getenv("IMAGE_NAME") SYNC_DIRECTION = os.getenv("SYNC_DIRECTION", "push_to_acr") LAST_SYNC_TAGS_PATH = "./last_sync_tags.txt" def get_artifactory_tags() -> List[str]: """获取Artifactory中目标镜像的所有标签""" url = f"{ARTIFACTORY_REGISTRY}/v2/{ARTIFACTORY_REPO}/{IMAGE_NAME}/tags/list" response = requests.get(url, auth=(ARTIFACTORY_USER, ARTIFACTORY_PASS)) response.raise_for_status() return response.json().get("tags", []) def get_new_tags(current_tags: List[str]) -> List[str]: """对比上次同步记录,返回新增标签""" if not os.path.exists(LAST_SYNC_TAGS_PATH): return current_tags with open(LAST_SYNC_TAGS_PATH, "r") as f: last_tags = [line.strip() for line in f if line.strip()] return list(set(current_tags) - set(last_tags)) def docker_login(registry: str, username: str, password: str): """登录Docker仓库""" subprocess.run( ["docker", "login", registry, "-u", username, "-p", password], check=True, capture_output=True ) def sync_image(tag: str): """同步单个镜像标签""" print(f"Syncing {IMAGE_NAME}:{tag}...") if SYNC_DIRECTION == "push_to_acr": source_img = f"{ARTIFACTORY_REGISTRY}/{ARTIFACTORY_REPO}/{IMAGE_NAME}:{tag}" target_img = f"{ACR_REGISTRY}/{IMAGE_NAME}:{tag}" elif SYNC_DIRECTION == "pull_to_artifactory": source_img = f"{ACR_REGISTRY}/{IMAGE_NAME}:{tag}" target_img = f"{ARTIFACTORY_REGISTRY}/{ARTIFACTORY_REPO}/{IMAGE_NAME}:{tag}" else: raise ValueError(f"Invalid sync direction: {SYNC_DIRECTION}") subprocess.run(["docker", "pull", source_img], check=True) subprocess.run(["docker", "tag", source_img, target_img], check=True) subprocess.run(["docker", "push", target_img], check=True) print(f"Synced {IMAGE_NAME}:{tag} successfully.") def update_sync_record(tags: List[str]): """更新同步记录文件""" with open(LAST_SYNC_TAGS_PATH, "w") as f: f.write("\n".join(tags)) if __name__ == "__main__": current_tags = get_artifactory_tags() new_tags = get_new_tags(current_tags) if not new_tags: print("No new tags to sync, exiting.") exit(0) # 登录仓库 docker_login(ARTIFACTORY_REGISTRY, ARTIFACTORY_USER, ARTIFACTORY_PASS) docker_login(ACR_REGISTRY, ACR_USER, ACR_PASS) # 同步所有新增标签 for tag in new_tags: sync_image(tag) # 更新记录 update_sync_record(current_tags) # 登出(可选) subprocess.run(["docker", "logout", ARTIFACTORY_REGISTRY]) subprocess.run(["docker", "logout", ACR_REGISTRY])
GitLab CI 配置示例
stages: - sync sync-images: stage: sync image: docker:latest services: - docker:dind variables: # 这些变量建议在GitLab项目CI/CD设置中配置为受保护变量 ARTIFACTORY_REGISTRY: "your-artifactory-registry-url" ARTIFACTORY_REPO: "your-docker-repo-name" ACR_REGISTRY: "your-acr-name.azurecr.io" IMAGE_NAME: "your-image-name" SYNC_DIRECTION: "push_to_acr" # 或 pull_to_artifactory before_script: # 安装依赖(二选一) - apk add --no-cache jq curl # 针对Bash脚本 # - apk add --no-cache python3 py3-pip && pip install requests # 针对Python脚本 script: - chmod +x ./sync_script.sh && ./sync_script.sh # Bash脚本执行方式 # - python ./sync_script.py # Python脚本执行方式 # 定时触发:每小时执行一次 schedule: - cron: "0 * * * *" timezone: Asia/Shanghai # 可选:配置Artifactory Webhook触发CI流水线(新镜像上传时实时触发)
注意事项
- 所有敏感凭证(用户名/密码)请在GitLab项目的CI/CD设置中配置为受保护变量,禁止明文写入代码
- 使用Docker-in-Docker模式时,需确保GitLab Runner配置了
privileged: true权限 - 可根据业务需求调整定时任务频率,或配置Artifactory的Webhook实现实时同步
内容的提问来源于stack exchange,提问作者Sridattha
相关产品推荐
相关产品推荐

