193 lines
5.7 KiB
Bash
Executable File
193 lines
5.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# 直接从源拉取镜像并推送到本地 Harbor
|
|
# 用于处理 EU Harbor 无法代理的镜像
|
|
|
|
set -e
|
|
|
|
# 颜色定义
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
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"; }
|
|
log_step() { echo -e "${CYAN}[→]${NC} $1"; }
|
|
|
|
LOCAL_HARBOR="harbor.omniverseai.net"
|
|
HARBOR_PROJECT="library"
|
|
|
|
# 解析镜像名称(移除 registry 前缀)
|
|
parse_image_name() {
|
|
local input_image="$1"
|
|
local image_name=""
|
|
local image_tag="latest"
|
|
|
|
# 移除 registry 前缀
|
|
if [[ "$input_image" =~ ^[^/]+\.[^/]+/ ]]; then
|
|
input_image=$(echo "$input_image" | sed -E 's|^[^/]+\.[^/]+/||')
|
|
fi
|
|
|
|
# 分离镜像名和标签
|
|
if [[ "$input_image" =~ : ]]; then
|
|
image_name="${input_image%:*}"
|
|
image_tag="${input_image##*:}"
|
|
else
|
|
image_name="$input_image"
|
|
fi
|
|
|
|
echo "${image_name}:${image_tag}"
|
|
}
|
|
|
|
# 扁平化镜像名
|
|
normalize_for_local_storage() {
|
|
local image="$1"
|
|
echo "$image" | sed 's|/|-|g'
|
|
}
|
|
|
|
# 直接缓存单个镜像
|
|
direct_cache_image() {
|
|
local input_image="$1"
|
|
|
|
log_info "=== 直接缓存镜像: $input_image ==="
|
|
|
|
# 解析镜像名
|
|
local normalized_image=$(parse_image_name "$input_image")
|
|
local local_image_name=$(normalize_for_local_storage "$normalized_image")
|
|
local dest_image="${LOCAL_HARBOR}/${HARBOR_PROJECT}/${local_image_name}"
|
|
|
|
log_info "本地存储为: $local_image_name"
|
|
|
|
# 直接从源拉取
|
|
log_step "[1/4] 从源拉取镜像(300秒超时)..."
|
|
if ! timeout 300 docker pull "$input_image" 2>&1 | grep -v "WARNING"; then
|
|
log_error "拉取失败: $input_image"
|
|
return 1
|
|
fi
|
|
log_success "拉取成功"
|
|
|
|
# 标记
|
|
log_step "[2/4] 标记镜像..."
|
|
if ! docker tag "$input_image" "$dest_image"; then
|
|
log_error "标记失败"
|
|
return 1
|
|
fi
|
|
log_success "标记成功"
|
|
|
|
# 推送
|
|
log_step "[3/4] 推送到本地 Harbor..."
|
|
if ! docker push "$dest_image" 2>&1 | grep -v "WARNING"; then
|
|
log_error "推送失败"
|
|
return 1
|
|
fi
|
|
log_success "推送成功"
|
|
|
|
# 验证
|
|
log_step "[4/4] 验证..."
|
|
docker rmi "$dest_image" &> /dev/null || true
|
|
if ! docker pull "$dest_image" &> /dev/null; then
|
|
log_error "验证失败"
|
|
return 1
|
|
fi
|
|
log_success "验证成功"
|
|
|
|
echo ""
|
|
log_success "✅ 镜像缓存完成!"
|
|
echo " 原始镜像: $input_image"
|
|
echo " 本地路径: $dest_image"
|
|
echo ""
|
|
|
|
return 0
|
|
}
|
|
|
|
# 登录本地 Harbor
|
|
log_step "登录本地 Harbor..."
|
|
if ! echo 'admin123' | docker login "$LOCAL_HARBOR" -u admin --password-stdin > /dev/null 2>&1; then
|
|
log_error "本地 Harbor 登录失败"
|
|
exit 1
|
|
fi
|
|
log_success "登录成功"
|
|
echo ""
|
|
|
|
# 失败的镜像列表
|
|
FAILED_IMAGES=(
|
|
"registry.k8s.io/pause:3.9"
|
|
"registry.k8s.io/coredns/coredns:v1.11.3"
|
|
"registry.k8s.io/ingress-nginx/controller:v1.11.3"
|
|
"registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20231226-1a7112e06"
|
|
"registry.k8s.io/autoscaling/vpa-updater:1.2.0"
|
|
"registry.k8s.io/autoscaling/vpa-admission-controller:1.2.0"
|
|
"registry.k8s.io/dns/k8s-dns-node-cache:1.23.0"
|
|
"registry.k8s.io/sig-storage/livenessprobe:v2.14.0"
|
|
"registry.k8s.io/gateway-api/admission-server:v1.2.0"
|
|
"registry.k8s.io/dashboard/dashboard:v2.7.0"
|
|
"registry.k8s.io/dashboard/metrics-scraper:v1.0.9"
|
|
"nicolaka/netshoot:latest"
|
|
"quay.io/jetstack/cert-manager-controller:v1.16.2"
|
|
"quay.io/jetstack/cert-manager-webhook:v1.16.2"
|
|
"quay.io/jetstack/cert-manager-cainjector:v1.16.2"
|
|
"quay.io/jetstack/cert-manager-acmesolver:v1.16.2"
|
|
"quay.io/argoproj/argocd:v2.13.2"
|
|
"quay.io/argoproj/argocd:v2.13.1"
|
|
"quay.io/prometheus/prometheus:v2.55.1"
|
|
"quay.io/prometheus/prometheus:v2.55.0"
|
|
"quay.io/prometheus/alertmanager:v0.27.0"
|
|
"quay.io/prometheus/node-exporter:v1.8.2"
|
|
"quay.io/prometheus/blackbox-exporter:v0.25.0"
|
|
"grafana/tempo:2.7.3"
|
|
"aquasec/trivy:0.58.2"
|
|
"velero/velero:v1.15.0"
|
|
"ghcr.io/external-secrets/external-secrets:v0.11.0"
|
|
"quay.io/bitnami/sealed-secrets-controller:v0.27.2"
|
|
"ghcr.io/stakater/reloader:v1.2.3"
|
|
)
|
|
|
|
TOTAL=${#FAILED_IMAGES[@]}
|
|
SUCCESS=0
|
|
FAILED=0
|
|
FAILED_LIST=()
|
|
|
|
log_info "开始处理 $TOTAL 个失败的镜像..."
|
|
echo ""
|
|
|
|
for i in "${!FAILED_IMAGES[@]}"; do
|
|
image="${FAILED_IMAGES[$i]}"
|
|
current=$((i + 1))
|
|
|
|
echo ""
|
|
log_info "进度: [$current/$TOTAL] 处理: $image"
|
|
echo "───────────────────────────────────────────────"
|
|
|
|
if direct_cache_image "$image"; then
|
|
((SUCCESS++))
|
|
else
|
|
((FAILED++))
|
|
FAILED_LIST+=("$image")
|
|
fi
|
|
|
|
echo "───────────────────────────────────────────────"
|
|
done
|
|
|
|
# 显示总结
|
|
echo ""
|
|
echo "═══════════════════════════════════════════════"
|
|
log_info "直接缓存完成统计"
|
|
echo "═══════════════════════════════════════════════"
|
|
echo " 总计: $TOTAL"
|
|
echo " 成功: $SUCCESS"
|
|
echo " 失败: $FAILED"
|
|
|
|
if [[ $FAILED -gt 0 ]]; then
|
|
echo ""
|
|
log_error "仍然失败的镜像:"
|
|
for img in "${FAILED_LIST[@]}"; do
|
|
echo " ✗ $img"
|
|
done
|
|
fi
|
|
echo "═══════════════════════════════════════════════"
|
|
echo ""
|