268 lines
6.2 KiB
Bash
Executable File
268 lines
6.2 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
###############################################################################
|
||
# Harbor 按需拉取并缓存镜像脚本
|
||
# 解决 Proxy Cache 不工作的问题,使用手动拉取+推送方式
|
||
###############################################################################
|
||
|
||
set -eo pipefail
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
log_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[✓]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[✗]${NC} $1"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}[!]${NC} $1"
|
||
}
|
||
|
||
# Harbor 配置
|
||
HARBOR_HOST="${HARBOR_HOST:-harbor.omniverseai.net}"
|
||
HARBOR_PROJECT="${HARBOR_PROJECT:-library}"
|
||
PLATFORM="${PLATFORM:-linux/arm64}"
|
||
|
||
# 显示帮助
|
||
show_help() {
|
||
cat << EOF
|
||
Harbor 按需拉取并缓存镜像脚本
|
||
|
||
用法:
|
||
$0 IMAGE[:TAG] 拉取并缓存单个镜像
|
||
$0 -f FILE 从文件批量拉取镜像
|
||
$0 -l 列出已缓存的镜像
|
||
|
||
参数:
|
||
IMAGE[:TAG] 镜像名称和标签(默认标签: latest)
|
||
-f FILE 镜像列表文件(每行一个镜像)
|
||
-l 列出 Harbor 中已缓存的镜像
|
||
-h, --help 显示帮助信息
|
||
|
||
环境变量:
|
||
HARBOR_HOST Harbor 地址(默认: harbor.omniverseai.net)
|
||
HARBOR_PROJECT Harbor 项目名(默认: library)
|
||
PLATFORM 镜像平台(默认: linux/arm64)
|
||
|
||
示例:
|
||
# 拉取单个镜像
|
||
$0 nginx:alpine
|
||
|
||
# 拉取多个镜像
|
||
$0 redis:alpine
|
||
$0 postgres:16-alpine
|
||
$0 mysql:8.0
|
||
|
||
# 从文件批量拉取
|
||
echo "nginx:alpine" > images.txt
|
||
echo "redis:alpine" >> images.txt
|
||
$0 -f images.txt
|
||
|
||
# 列出已缓存镜像
|
||
$0 -l
|
||
|
||
工作流程:
|
||
1. 从 Docker Hub 拉取 ARM64 镜像
|
||
2. 标记为 Harbor 路径
|
||
3. 推送到 Harbor
|
||
4. 验证可用性
|
||
|
||
EOF
|
||
}
|
||
|
||
# 拉取并缓存单个镜像
|
||
pull_and_cache() {
|
||
local image=$1
|
||
|
||
# 添加默认标签
|
||
if [[ ! "$image" =~ ":" ]]; then
|
||
image="${image}:latest"
|
||
fi
|
||
|
||
log_info "=== 拉取并缓存镜像: $image ==="
|
||
echo ""
|
||
|
||
local harbor_image="${HARBOR_HOST}/${HARBOR_PROJECT}/${image}"
|
||
|
||
# 1. 从 Docker Hub 拉取
|
||
log_info "[1/4] 从 Docker Hub 拉取 ARM64 镜像..."
|
||
if docker pull --platform "$PLATFORM" "$image"; then
|
||
log_success "拉取成功: $image"
|
||
else
|
||
log_error "拉取失败: $image"
|
||
return 1
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# 2. 标记为 Harbor 镜像
|
||
log_info "[2/4] 标记为 Harbor 镜像..."
|
||
if docker tag "$image" "$harbor_image"; then
|
||
log_success "标记成功: $harbor_image"
|
||
else
|
||
log_error "标记失败"
|
||
return 1
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# 3. 推送到 Harbor
|
||
log_info "[3/4] 推送到 Harbor..."
|
||
if docker push "$harbor_image"; then
|
||
log_success "推送成功"
|
||
else
|
||
log_error "推送失败"
|
||
return 1
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# 4. 验证镜像架构
|
||
log_info "[4/4] 验证镜像架构..."
|
||
local arch=$(docker inspect "$harbor_image" --format '{{.Architecture}}' 2>/dev/null || echo "unknown")
|
||
|
||
if [ "$arch" = "arm64" ]; then
|
||
log_success "架构验证通过: $arch"
|
||
else
|
||
log_warning "架构: $arch (预期: arm64)"
|
||
fi
|
||
|
||
echo ""
|
||
log_success "✅ 镜像已成功缓存到 Harbor!"
|
||
echo ""
|
||
echo " 原始镜像: $image"
|
||
echo " Harbor 路径: $harbor_image"
|
||
echo " 架构: $arch"
|
||
echo ""
|
||
echo "在 Kubernetes 中使用:"
|
||
echo " image: $harbor_image"
|
||
echo ""
|
||
|
||
return 0
|
||
}
|
||
|
||
# 从文件批量拉取
|
||
pull_from_file() {
|
||
local file=$1
|
||
|
||
if [ ! -f "$file" ]; then
|
||
log_error "文件不存在: $file"
|
||
return 1
|
||
fi
|
||
|
||
log_info "从文件批量拉取: $file"
|
||
echo ""
|
||
|
||
local total=0
|
||
local success=0
|
||
local failed=0
|
||
|
||
while IFS= read -r line; do
|
||
# 跳过空行和注释
|
||
[[ -z "$line" ]] && continue
|
||
[[ "$line" =~ ^#.* ]] && continue
|
||
|
||
total=$((total + 1))
|
||
|
||
if pull_and_cache "$line"; then
|
||
success=$((success + 1))
|
||
else
|
||
failed=$((failed + 1))
|
||
fi
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
done < "$file"
|
||
|
||
log_info "批量拉取完成"
|
||
echo " 总数: $total"
|
||
echo " 成功: $success"
|
||
echo " 失败: $failed"
|
||
}
|
||
|
||
# 列出已缓存的镜像
|
||
list_cached() {
|
||
log_info "查询 Harbor 中已缓存的镜像..."
|
||
echo ""
|
||
|
||
local response=$(curl -s -k -u "admin:admin123" \
|
||
"http://192.168.1.83:8080/api/v2.0/projects/${HARBOR_PROJECT}/repositories")
|
||
|
||
echo "$response" | python3 -c "
|
||
import sys, json
|
||
try:
|
||
repos = json.load(sys.stdin)
|
||
print(f'Harbor {\"${HARBOR_PROJECT}\"} 项目中的镜像: {len(repos)} 个\n')
|
||
print('─' * 80)
|
||
for r in repos:
|
||
name = r['name'].replace('${HARBOR_PROJECT}/', '')
|
||
artifacts = r.get('artifact_count', 0)
|
||
print(f\" {name:<50} (Artifacts: {artifacts})\")
|
||
print('─' * 80)
|
||
print(f'\n使用方式: docker pull ${HARBOR_HOST}/${HARBOR_PROJECT}/IMAGE:TAG')
|
||
except Exception as e:
|
||
print(f'查询失败: {e}')
|
||
"
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
# 检查 Docker 是否运行
|
||
if ! docker info > /dev/null 2>&1; then
|
||
log_error "Docker 未运行,请先启动 Docker"
|
||
exit 1
|
||
fi
|
||
|
||
# 检查是否已登录 Harbor
|
||
if ! docker login "$HARBOR_HOST" -u admin -p admin123 > /dev/null 2>&1; then
|
||
log_warning "Harbor 登录失败,尝试继续..."
|
||
fi
|
||
|
||
# 解析参数
|
||
case "${1:-}" in
|
||
-h|--help|help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
-l|--list|list)
|
||
list_cached
|
||
exit 0
|
||
;;
|
||
-f|--file)
|
||
if [ -z "${2:-}" ]; then
|
||
log_error "请指定镜像列表文件"
|
||
echo "用法: $0 -f FILE"
|
||
exit 1
|
||
fi
|
||
pull_from_file "$2"
|
||
exit $?
|
||
;;
|
||
"")
|
||
log_error "请指定镜像名称"
|
||
echo ""
|
||
show_help
|
||
exit 1
|
||
;;
|
||
*)
|
||
pull_and_cache "$1"
|
||
exit $?
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# 执行主程序
|
||
main "$@"
|