Files
macos-tools/harbor/scripts/batch-cache-images.sh
gaoandClaude 8f47f4e9ea feat: 完善 DevOps 工具链配置和优化 Harbor 服务
- 更新 Gitea runner 配置,支持优化的 CI/CD 环境
- 完善 Harbor HTTPS 配置和远程访问支持
- 优化 Harbor 日志配置,改用 json-file 驱动
- 添加 Docker 构建加速和代理缓存脚本
- 新增完整的文档和使用指南
- 添加 AI 开发工具集成配置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
2025-11-03 06:39:53 +08:00

352 lines
9.8 KiB
Bash
Executable File

#!/bin/bash
# 批量镜像缓存脚本
# 根据清单批量缓存项目所需镜像到 Harbor
set -e
HARBOR_REGISTRY="192.168.1.83:8443"
HARBOR_USER="admin"
HARBOR_PASS="admin123"
IMAGES_LIST="./project-images.list"
LOG_FILE="./cache-images.log"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
error() {
echo -e "${RED}ERROR: $1${NC}" | tee -a "$LOG_FILE"
}
success() {
echo -e "${GREEN}SUCCESS: $1${NC}" | tee -a "$LOG_FILE"
}
warning() {
echo -e "${YELLOW}WARNING: $1${NC}" | tee -a "$LOG_FILE"
}
info() {
echo -e "${BLUE}INFO: $1${NC}" | tee -a "$LOG_FILE"
}
# 显示帮助信息
show_help() {
echo "批量镜像缓存工具"
echo ""
echo "用法: $0 [选项] [筛选条件]"
echo ""
echo "选项:"
echo " -f, --file <文件> 指定镜像清单文件 (默认: project-images.list)"
echo " -p, --priority <级别> 按优先级筛选 (high/medium/low)"
echo " -t, --type <类型> 按类型筛选 (runtime/build/k8s/application/security/tools)"
echo " -d, --dry-run 仅显示将要缓存的镜像,不实际执行"
echo " -c, --continue 遇到错误时继续执行"
echo " -j, --parallel <数量> 并发数量 (默认: 3)"
echo " -h, --help 显示帮助信息"
echo ""
echo "示例:"
echo " $0 # 缓存所有镜像"
echo " $0 -p high # 只缓存高优先级镜像"
echo " $0 -t runtime # 只缓存运行时镜像"
echo " $0 -p high -t runtime # 缓存高优先级的运行时镜像"
echo " $0 -d # 预览模式,不实际执行"
}
# 解析命令行参数
DRY_RUN=false
CONTINUE_ON_ERROR=false
PARALLEL_JOBS=3
PRIORITY_FILTER=""
TYPE_FILTER=""
while [[ $# -gt 0 ]]; do
case $1 in
-f|--file)
IMAGES_LIST="$2"
shift 2
;;
-p|--priority)
PRIORITY_FILTER="$2"
shift 2
;;
-t|--type)
TYPE_FILTER="$2"
shift 2
;;
-d|--dry-run)
DRY_RUN=true
shift
;;
-c|--continue)
CONTINUE_ON_ERROR=true
shift
;;
-j|--parallel)
PARALLEL_JOBS="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
error "未知参数: $1"
show_help
exit 1
;;
esac
done
# 检查镜像清单文件
if [ ! -f "$IMAGES_LIST" ]; then
error "镜像清单文件不存在: $IMAGES_LIST"
exit 1
fi
# 登录 Harbor
info "登录 Harbor..."
if ! echo "$HARBOR_PASS" | docker login "$HARBOR_REGISTRY" -u "$HARBOR_USER" --password-stdin > /dev/null 2>&1; then
error "Harbor 登录失败"
exit 1
fi
success "Harbor 登录成功"
# 解析镜像清单
parse_images_list() {
local images_to_cache=()
while IFS= read -r line; do
# 跳过注释和空行
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
# 解析行: 镜像:标签 项目 描述 优先级
if [[ "$line" =~ ^([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)$ ]]; then
image_full="${BASH_REMATCH[1]}"
target_project="${BASH_REMATCH[2]}"
description="${BASH_REMATCH[3]}"
priority="${BASH_REMATCH[4]}"
# 应用筛选条件
if [ -n "$PRIORITY_FILTER" ] && [ "$priority" != "$PRIORITY_FILTER" ]; then
continue
fi
if [ -n "$TYPE_FILTER" ] && [ "$target_project" != "$TYPE_FILTER" ]; then
continue
fi
images_to_cache+=("$image_full|$target_project|$description|$priority")
fi
done < "$IMAGES_LIST"
echo "${images_to_cache[@]}"
}
# 缓存单个镜像
cache_single_image() {
local image_info="$1"
local image_full=$(echo "$image_info" | cut -d'|' -f1)
local target_project=$(echo "$image_info" | cut -d'|' -f2)
local description=$(echo "$image_info" | cut -d'|' -f3)
local priority=$(echo "$image_info" | cut -d'|' -f4)
local image_name=$(echo "$image_full" | cut -d':' -f1)
local image_tag=$(echo "$image_full" | cut -d':' -f2)
# 构建 Harbor 镜像名
local harbor_image="${HARBOR_REGISTRY}/${target_project}/${image_name}:${image_tag}"
info "缓存镜像: $image_full -> $target_project ($description)"
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] 将缓存: $harbor_image"
return 0
fi
# 检查镜像是否已存在
if docker pull "$harbor_image" > /dev/null 2>&1; then
success "镜像已存在: $harbor_image"
return 0
fi
# 拉取原始镜像
info " 拉取原始镜像: $image_full"
if ! docker pull "$image_full" > /dev/null 2>&1; then
error " 拉取失败: $image_full"
[ "$CONTINUE_ON_ERROR" = true ] && return 1 || exit 1
fi
# 标记并推送到 Harbor
info " 推送到 Harbor: $harbor_image"
if ! docker tag "$image_full" "$harbor_image" > /dev/null 2>&1; then
error " 标记失败: $harbor_image"
[ "$CONTINUE_ON_ERROR" = true ] && return 1 || exit 1
fi
if ! docker push "$harbor_image" > /dev/null 2>&1; then
error " 推送失败: $harbor_image"
[ "$CONTINUE_ON_ERROR" = true ] && return 1 || exit 1
fi
success " 缓存成功: $harbor_image"
# 清理本地镜像(可选)
# docker rmi "$harbor_image" > /dev/null 2>&1 || true
return 0
}
# 并发缓存函数
cache_images_parallel() {
local images_array=("$@")
local total=${#images_array[@]}
local pids=()
local active=0
local completed=0
info "开始缓存 $total 个镜像 (并发数: $PARALLEL_JOBS)"
for image_info in "${images_array[@]}"; do
# 等待有可用的并发槽位
while [ $active -ge $PARALLEL_JOBS ]; do
for pid in "${pids[@]}"; do
if ! kill -0 "$pid" 2>/dev/null; then
wait "$pid"
((completed++))
((active--))
# 从 pids 数组中移除已完成的 pid
pids=("${pids[@]/$pid/}")
break
fi
done
[ $active -ge $PARALLEL_JOBS ] && sleep 1
done
# 启动新的缓存任务
{
cache_single_image "$image_info"
} &
pids+=("$!")
((active++))
# 显示进度
local progress=$((completed + active))
info "进度: $progress/$total ($(( progress * 100 / total ))%)"
done
# 等待所有任务完成
for pid in "${pids[@]}"; do
wait "$pid"
((completed++))
done
success "所有镜像缓存完成! 总计: $total 个"
}
# 显示统计信息
show_statistics() {
info "生成缓存统计报告..."
echo ""
echo "📊 缓存统计报告"
echo "=================="
local total_images=0
declare -A project_counts
declare -A priority_counts
while IFS= read -r line; do
[[ "$line" =~ ^[[:space:]]*# ]] && continue
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
if [[ "$line" =~ ^([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)$ ]]; then
target_project="${BASH_REMATCH[2]}"
priority="${BASH_REMATCH[4]}"
((total_images++))
((project_counts["$target_project"]++))
((priority_counts["$priority"]++))
fi
done < "$IMAGES_LIST"
echo "总镜像数量: $total_images"
echo ""
echo "按项目分类:"
for project in "${!project_counts[@]}"; do
echo " $project: ${project_counts[$project]} 个"
done
echo ""
echo "按优先级分类:"
for priority in "${!priority_counts[@]}"; do
echo " $priority: ${priority_counts[$priority]} 个"
done
if [ -n "$PRIORITY_FILTER" ] || [ -n "$TYPE_FILTER" ]; then
echo ""
echo "筛选条件:"
[ -n "$PRIORITY_FILTER" ] && echo " 优先级: $PRIORITY_FILTER"
[ -n "$TYPE_FILTER" ] && echo " 类型: $TYPE_FILTER"
fi
}
# 主函数
main() {
info "开始批量镜像缓存任务..."
info "镜像清单: $IMAGES_LIST"
[ -n "$PRIORITY_FILTER" ] && info "优先级筛选: $PRIORITY_FILTER"
[ -n "$TYPE_FILTER" ] && info "类型筛选: $TYPE_FILTER"
[ "$DRY_RUN" = true ] && info "模式: 预览 (不实际执行)"
echo ""
# 解析镜像清单
local images_to_cache
IFS=$'\n' read -r -d '' -a images_to_cache < <(parse_images_list && printf '\0')
if [ ${#images_to_cache[@]} -eq 0 ]; then
warning "没有找到符合条件的镜像"
exit 0
fi
# 显示统计信息
show_statistics
echo ""
info "准备缓存 ${#images_to_cache[@]} 个镜像"
if [ "$DRY_RUN" = false ]; then
# 并发缓存镜像
cache_images_parallel "${images_to_cache[@]}"
else
echo ""
echo "预览模式 - 将要缓存的镜像:"
for image_info in "${images_to_cache[@]}"; do
local image_full=$(echo "$image_info" | cut -d'|' -f1)
local target_project=$(echo "$image_info" | cut -d'|' -f2)
local description=$(echo "$image_info" | cut -d'|' -f3)
echo " $image_full -> $target_project ($description)"
done
fi
echo ""
success "批量镜像缓存任务完成!"
echo ""
echo "📝 日志文件: $LOG_FILE"
echo "🔗 Harbor Web: https://192.168.1.83:8443"
echo "👤 登录信息: admin / admin123"
}
# 执行主函数
main "$@"