- 更新 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]>
352 lines
9.8 KiB
Bash
Executable File
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 "$@" |