高效清理Harbor镜像:仅保留最新develop和baseline
1. 目标
删除镜像develop和baseline 仅保留最新上传的develop和baseline镜像 master节点不动
2.以下是脚本
vim clean_harbor.sh#!/bin/bash # ============ 配置区 ============ HARBOR_URL="http://10.0.1.138" USERNAME="admin" PASSWORD="Harbor12345" KEYWORDS=("develop" "baseline") # 每个关键词独立保留 KEEP_COUNT 个 KEEP_COUNT=5 # 每个关键词各保留最近几个 DRY_RUN=true # ← 确认无误后改为 false # ================================ AUTH=$(echo -n "${USERNAME}:${PASSWORD}" | base64) get_json() { curl -sk -H "Authorization: Basic ${AUTH}" \ -H "Content-Type: application/json" \ "${HARBOR_URL}/api/v2.0/$1" } delete_artifact() { curl -sk -X DELETE \ -H "Authorization: Basic ${AUTH}" \ "${HARBOR_URL}/api/v2.0/$1" -o /dev/null -w "%{http_code}" } TOTAL_DELETED=0 PROCESSED_REPOS=0 echo "🔍 开始扫描 Harbor: ${HARBOR_URL}" echo "📋 关键词(各自保留${KEEP_COUNT}个): ${KEYWORDS[*]} | master等其余tag不动 | DRY_RUN: ${DRY_RUN}" echo "" PROJECTS=$(get_json "projects?page_size=100" | jq -r '.[].name' 2>/dev/null) if [ -z "$PROJECTS" ]; then echo "❌ 无法获取项目列表" exit 1 fi for PROJ in $PROJECTS; do REPOS=$(get_json "projects/${PROJ}/repositories?page_size=100" | jq -r '.[].name' 2>/dev/null) [ -z "$REPOS" ] && continue for REPO_FULL in $REPOS; do PROCESSED_REPOS=$((PROCESSED_REPOS + 1)) REPO_NAME="${REPO_FULL#${PROJ}/}" # 分页获取全部 artifacts ALL="[]" PAGE=1 while true; do PJ=$(get_json "projects/${PROJ}/repositories/${REPO_NAME}/artifacts?page_size=100&page=${PAGE}" 2>/dev/null) PC=$(echo "$PJ" | jq 'length' 2>/dev/null) [ -z "$PC" ] || [ "$PC" -eq 0 ] && break ALL=$(echo "$ALL" "$PJ" | jq -s '.[0]+.[1]' 2>/dev/null) [ "$PC" -lt 100 ] && break PAGE=$((PAGE + 1)) done REPO_DELETED=0 # 🔑 核心改动:每个关键词独立分组、独立保留 for KW in "${KEYWORDS[@]}"; do MATCHED=$(echo "$ALL" | jq --arg kw "$KW" ' [.[] | select( (.tags // []) | map(.name) | any(test($kw)) )] | sort_by(.push_time) | reverse ' 2>/dev/null) COUNT=$(echo "$MATCHED" | jq 'length' 2>/dev/null) [ -z "$COUNT" ] || [ "$COUNT" -eq 0 ] && continue if [ "$COUNT" -le "$KEEP_COUNT" ]; then echo "⏭️ ${REPO_FULL} [${KW}]: ${COUNT}个 ≤ ${KEEP_COUNT}, 跳过" continue fi TO_DELETE=$((COUNT - KEEP_COUNT)) echo "📦 ${REPO_FULL} [${KW}]: ${COUNT}个, 保留${KEEP_COUNT}, 待删${TO_DELETE}" DEL_ITEMS=$(echo "$MATCHED" | jq -r --argjson keep "$KEEP_COUNT" \ ".[\$keep:][] | \"\(.push_time)\t\(.digest)\t\([.tags[].name] | join(\",\"))\"") while IFS=$'\t' read -r PUSH_TIME DIGEST TAGS; do [ -z "$DIGEST" ] && continue echo " 🗑️ [${PUSH_TIME}] ${DIGEST:0:12} (${TAGS})" if [ "$DRY_RUN" = "false" ]; then CODE=$(delete_artifact "projects/${PROJ}/repositories/${REPO_NAME}/artifacts/${DIGEST}") if [ "$CODE" = "200" ]; then echo " ✔ 已删除" TOTAL_DELETED=$((TOTAL_DELETED + 1)) REPO_DELETED=$((REPO_DELETED + 1)) else echo " ✘ 失败 (HTTP $CODE)" fi else TOTAL_DELETED=$((TOTAL_DELETED + 1)) REPO_DELETED=$((REPO_DELETED + 1)) fi done <<< "$DEL_ITEMS" done [ "$REPO_DELETED" -eq 0 ] && echo "⏭️ ${REPO_FULL}: 无需清理" done done echo "" echo "============================================================" ACTION="将删除"; [ "$DRY_RUN" = "false" ] && ACTION="已删除" echo "📊 扫描${PROCESSED_REPOS}仓库 | ${ACTION}${TOTAL_DELETED}个artifact" [ "$DRY_RUN" = "true" ] && [ "$TOTAL_DELETED" -gt 0 ] && \ echo -e "\n⚠️ DRY_RUN模式,未实际删除!改 DRY_RUN=false 后重新执行。"bash clean_harbor.sh