脚本目前实现的功能,从dockerhub,quay.io等公有仓库镜像下载镜像,然后重新tag推送到内网harbor仓库。同时如果harbor仓库不存在对应的project则会调用harbor接口(用户需要有创建project的权限)进行创建。

1. 脚本内容

创建文件images_down.sh,内容如下:

#!/bin/bash

# ==============================================================================
# Script Name: images_pull_push.sh
# Description: 用于将 Docker 镜像从源仓库拉取并修改tag推动到目标 Harbor 仓库
# Author:       lldhsds
# Version:      1.0
# Date:         2024-11-20
# Usage:        详见 help 信息
# Dependencies: linux,docker, curl
# Notes:
#   1. Harbor 仓库必须支持 HTTPS 协议,并且运行脚本的终端已配置证书和仓库受信任。如果需要http协议,需要修改脚本中url中的请求协议。
#   2. 镜像列表文件中每行记录一个镜像,格式为 'repo/project/image:tag'。
# ==============================================================================

# 显示帮助信息
usage() {
    echo "Usage: "
    echo 
    echo "用法: $0 [-f <镜像列表文件>] [-d <目标镜像仓库>] [-u <目标仓库用户名>] [-p <目标仓库密码>]"
    echo
    echo "Description:"
    echo " -f <镜像列表文件>            : 镜像列表文件路径,例如 images.txt,注意路径"
    echo " -d <目标镜像仓库>            : 目标镜像仓库地址,例如 harbor.example.com。"
    echo " -u <目标仓库用户名>          : 目标仓库用户名,例如 admin,用户需要有创建harbor项目的权限。"
    echo " -p <目标仓库密码>            : 目标仓库密码,例如Harbor12345"
    echo
    echo "Example:"
    echo "# 1. 从 images.txt 列表文件中的镜像下载并推送到 harbor.example.com 仓库。"
    echo " $0 -f images.txt -d harbor.example.com -u admin -p Harbor12345"
    echo 
    echo "限制条件:"
    echo " 1. harbor仓库为https协议,并且运行脚本的终端已配置证书和仓库受信任。 "
    echo " 2. 镜像列表文件中每行记录一个镜像: "
    echo " 例如'docker.io/bitnami/mysql:5.7.43',由'repo/project/image:tag'组成。如果从dokcerhub拉取,不能省略repo和project,对于大部分镜像docker.io和library为默认的仓库和项目名称,文件中需要补全。"
    exit 1
}

# 解析参数
while getopts ":f:d:u:p:" opt; do
    case ${opt} in
        f) IMAGE_LIST="$OPTARG";;
        d) TARGET_REPO="$OPTARG";;
        u) HARBOR_USERNAME="$OPTARG";;
        p) HARBOR_PASSWORD="$OPTARG";;
        \? )usage;;
    esac
done

# 移除已解析的参数
shift $((OPTIND - 1))

# 检查参数数量
if [ -z "$IMAGE_LIST" ] || [ -z "$TARGET_REPO" ] || [ -z "$HARBOR_USERNAME" ] || [ -z "$HARBOR_PASSWORD" ]; then
    usage
fi

# 检查镜像列表文件是否存在
if [ ! -f "$IMAGE_LIST" ]; then
    echo "镜像列表文件 $IMAGE_LIST 不存在。"
    exit 1
fi

# 函数:检查并创建 Harbor 项目
create_harbor_project() {
    PROJECT_NAME="$1"
    LIST_PRO_API="https://$TARGET_REPO/api/v2.0/projects/$PROJECT_NAME"
    echo $LIST_PRO_API

    # 检查项目是否存在
    RESPONSE=$(curl --noproxy -s -k -w '%{http_code}\n' -o /dev/null -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" -X 'GET' "$LIST_PRO_API" -H 'accept: application/json' -H 'X-Is-Resource-Name: false')

    if [ "$RESPONSE" -eq 404 ]; then
        echo "项目 ${PROJECT_NAME} 不存在,正在创建..."
        # echo $TARGET_REPO
        # 创建harbor项目的请求体,创建公开的项目
        json_playload="{\"project_name\": \"$PROJECT_NAME\",\"public\": true}"
        curl --noproxy -s -k -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" -X 'POST' "https://$TARGET_REPO/api/v2.0/projects" -H 'accept: application/json' -H 'Content-Type: application/json' -d "$json_playload"
        echo "项目 ${PROJECT_NAME} 创建完成。"
    else
        echo "项目 ${PROJECT_NAME} 已存在。"
        echo $TARGET_REPO   
    fi
}

# 登录 Harbor 仓库
# echo "登录 Harbor 仓库: $TARGET_REPO"
# echo "HARBOR_USERNAME: $HARBOR_USERNAME"
# echo "HARBOR_PASSWORD: $HARBOR_PASSWORD"
# echo "TARGET_REPO: $TARGET_REPO"
docker login -u "$HARBOR_USERNAME" -p "$HARBOR_PASSWORD" "$TARGET_REPO"

# 循环处理镜像列表
while IFS= read -r IMAGE; do
    # 获取目标项目名称
    IMAGE_PROJECT=$(echo "${IMAGE}" | cut -d':' -f1 | cut -d'/' -f2)
    IMAGE_NAME=$(echo "${IMAGE}" | cut -d'/' -f3)

    # 检查并创建 Harbor 项目
    create_harbor_project "${IMAGE_PROJECT}"

    # 拉取源镜像
    echo "拉取镜像: ${IMAGE}"
    docker pull "${IMAGE}"
   
    # 标记镜像为目标镜像
    TARGET_IMAGE="${TARGET_REPO}/${IMAGE_PROJECT}/${IMAGE_NAME}"
    echo "标记镜像: ${TARGET_IMAGE}"
    docker tag "${IMAGE}" "${TARGET_IMAGE}"

    # 推送到目标镜像仓库
    echo "推送镜像: ${TARGET_IMAGE}"
    docker push "${TARGET_IMAGE}"

done < "$IMAGE_LIST"

echo "所有镜像处理完成。"

2. 脚本使用指导

[root@harbor]# sh images_down.sh
Usage:

用法: images_down.sh [-f <镜像列表文件>] [-d <目标镜像仓库>] [-u <目标仓库用户名>] [-p <目标仓库密码>]

Description:
 -f <镜像列表文件>            : 镜像列表文件路径,例如 images.txt,注意路径
 -d <目标镜像仓库>            : 目标镜像仓库地址,例如 harbor.example.com。
 -u <目标仓库用户名>          : 目标仓库用户名,例如 admin,用户需要由创建harbor项目的权限。
 -p <目标仓库密码>            : 目标仓库密码,例如Harbor12345

Example:
# 1. 将 images.txt 列表文件中的镜像下载并推送到 harbor.example.com 仓库。
 images_down.sh -f images.txt -d harbor.example.com -u admin -p Harbor12345

限制条件:
 1. harbor仓库为https协议,并且运行脚本的终端已配置证书和仓库受信任。
 2. 镜像列表文件中每行记录一个镜像:
 例如'docker.io/bitnami/mysql:5.7.43',由'repo/project/image:tag'组成。如果从dokcerhub拉取,不能省略repo和project,对于大部分镜像docker.io和library为默认的仓库和项目名称,文件中需要补全。

3. 测试下载推送镜像

# 创建镜像列表文件,镜像格式为`repo/project/name:tag`
[root@harbor images_pull]# cat images.txt
docker.io/library/centos:6.10
docker.io/library/centos:7.9.2009
docker.io/library/centos:7.8.2003
docker.io/library/centos:8.4.2105

# 添加执行权限
[root@harbor images_pull]# chmod +x images_down.sh

# 执行脚本
[root@harbor images_pull]# ./images_down.sh -f images.txt -d harbor.jdzx.com -u admin -p Harbor12345
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
https://harbor.jdzx.local/api/v2.0/projects/library
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   385  100   385    0     0   1758      0 --:--:-- --:--:-- --:--:--  1757
项目 library 已存在。
harbor.jdzx.local
拉取镜像: docker.io/library/centos:6.10
6.10: Pulling from library/centos
...
74ddd0ec08fa: Pushed
8.4.2105: digest: sha256:a1801b843b1bfaf77c501e7a6d3f709401a1e0c83863037fa3aab063a7fdb9dc size: 529
所有镜像处理完成。

4. 限制条件

harbor仓库需要使用https协议部署,如果使用http部署,需要修改脚本内容里url的协议字段。
运行脚本的客户端需要配置仓库受信任、以及证书文件等。