Files
ljwx-k8s/CACHE-IMAGE-GUIDE.md
2026-02-20 22:50:45 +08:00

12 KiB
Raw Permalink Blame History

镜像缓存工具使用指南

概述

cache-image.sh 是一个通用的 Kubernetes 镜像缓存工具,通过 EU Harbor 中转将镜像缓存到本地 Harbor,解决 Docker Hub 等公网仓库访问超时问题。

核心功能

  • ✅ 单个镜像缓存 - 快速缓存指定镜像
  • ✅ 批量缓存 - 从文件或命令行批量处理
  • ✅ 自动更新 Deployment - 缓存后自动更新 K8s 部署
  • ✅ 从 Pod 提取镜像 - 分析失败 Pod 并缓存其镜像
  • ✅ 智能镜像解析 - 自动处理各种镜像格式
  • ✅ 详细进度显示 - 完整的执行日志和统计

快速开始

基本用法

# 缓存单个镜像
./cache-image.sh nginx:alpine

# 缓存多个镜像
./cache-image.sh nginx:alpine redis:7 mysql:8.0

# 从文件批量缓存
./cache-image.sh -f images.txt

高级用法

# 自动修复 Deployment 镜像拉取问题
./cache-image.sh --update-deployment kubernetes-dashboard/kubernetes-dashboard

# 从失败的 Pod 获取镜像并缓存
./cache-image.sh --from-pod kubernetes-dashboard/dashboard-xxx

# 预览操作(不实际执行)
./cache-image.sh --dry-run nginx:alpine redis:7

使用场景

场景 1: Deployment 镜像拉取失败

问题:

kubectl get pods -n kubernetes-dashboard
NAME                                        READY   STATUS             RESTARTS   AGE
kubernetes-dashboard-79cbcf9fb6-hwpxp       0/1     ImagePullBackOff   0          5m

解决:

# 方案 A: 自动修复(推荐)
./cache-image.sh --update-deployment kubernetes-dashboard/kubernetes-dashboard

# 方案 B: 手动缓存
kubectl describe pod kubernetes-dashboard-79cbcf9fb6-hwpxp -n kubernetes-dashboard | grep Image:
# 输出: Image: kubernetesui/dashboard:v2.7.0

./cache-image.sh kubernetesui/dashboard:v2.7.0

# 然后手动更新
kubectl set image deployment/kubernetes-dashboard \
  kubernetes-dashboard=harbor.omniverseai.net:8080/library/dashboard:v2.7.0 \
  -n kubernetes-dashboard

场景 2: 新部署应用前预缓存

应用 YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myorg/myapp:v1.0.0
      - name: sidecar
        image: envoyproxy/envoy:v1.28.0

预缓存镜像:

./cache-image.sh myorg/myapp:v1.0.0 envoyproxy/envoy:v1.28.0

部署时使用本地镜像:

spec:
  template:
    spec:
      containers:
      - name: app
        image: harbor.omniverseai.net:8080/library/myapp:v1.0.0
      - name: sidecar
        image: harbor.omniverseai.net:8080/library/envoy:v1.28.0

场景 3: 批量预热镜像

创建镜像列表文件 (images.txt):

# Monitoring stack
prometheus/prometheus:v2.45.0
grafana/grafana:10.0.0
prom/node-exporter:v1.6.0

# Databases
mysql:8.0
redis:7.2-alpine
postgres:15-alpine

# Application
nginx:alpine
python:3.11-slim

批量缓存:

./cache-image.sh -f images.txt

输出示例:

[INFO] 从文件读取镜像列表: images.txt
[INFO] 找到 8 个镜像

[INFO] 进度: [1/8] 处理: prometheus/prometheus:v2.45.0
───────────────────────────────────────────────
[✓] 镜像缓存完成!
  本地路径: harbor.omniverseai.net:8080/library/prometheus:v2.45.0
───────────────────────────────────────────────

...

═══════════════════════════════════════════════
[INFO] 批量缓存完成统计
═══════════════════════════════════════════════
  总计: 8
  成功: 7
  失败: 1

场景 4: Helm Chart 部署前准备

检查 Helm Chart 需要的镜像:

# 查看将要部署的镜像
helm template my-release stable/mysql | grep "image:"

# 输出:
# image: "mysql:8.0"
# image: "busybox:1.35"

预缓存镜像:

./cache-image.sh mysql:8.0 busybox:1.35

部署时覆盖镜像:

helm install my-release stable/mysql \
  --set image.repository=harbor.omniverseai.net:8080/library/mysql \
  --set image.tag=8.0

命令参考

选项说明

选项 说明 示例
-f, --file 从文件读取镜像列表 -f images.txt
-p, --project 指定 Harbor 项目(默认 library) -p myproject
-u, --update-deployment 缓存后自动更新 Deployment --update-deployment ns/deploy
--from-pod 从 Pod 提取镜像并缓存 --from-pod ns/pod-name
--dry-run 仅显示操作,不实际执行 --dry-run
-h, --help 显示帮助信息 -h

镜像格式支持

脚本会自动解析以下格式的镜像:

输入格式 解析结果 本地 Harbor 路径
nginx:alpine nginx:alpine harbor.omniverseai.net:8080/library/nginx:alpine
nginx nginx:latest harbor.omniverseai.net:8080/library/nginx:latest
mysql/mysql-server:8.0 mysql-server:8.0 harbor.omniverseai.net:8080/library/mysql-server:8.0
docker.io/library/redis:7 redis:7 harbor.omniverseai.net:8080/library/redis:7
gcr.io/project/image:v1 image:v1 harbor.omniverseai.net:8080/library/image:v1
quay.io/org/app:latest app:latest harbor.omniverseai.net:8080/library/app:latest

规则:

  • 自动移除 registry 前缀(docker.io, gcr.io, quay.io 等)
  • 自动移除组织前缀(保留镜像名)
  • 未指定 tag 时自动添加 :latest
  • 统一推送到本地 Harbor 的 library 项目

工作流程

单个镜像缓存流程

输入镜像: nginx:alpine
    ↓
[1/5] 从 EU Harbor 拉取
    ├─ 尝试: harbor.eu.lingjingwanxiang.cn/library/nginx:alpine
    └─ 失败则尝试: harbor.eu.lingjingwanxiang.cn/dockerhub-proxy/library/nginx:alpine
    ↓
[2/5] 标记为本地镜像
    → docker tag ... harbor.omniverseai.net:8080/library/nginx:alpine
    ↓
[3/5] 推送到本地 Harbor
    → docker push harbor.omniverseai.net:8080/library/nginx:alpine
    ↓
[4/5] 验证拉取
    → docker pull harbor.omniverseai.net:8080/library/nginx:alpine
    ↓
[5/5] 检查架构
    → docker inspect ... --format '{{.Architecture}}'
    ↓
✅ 完成

自动更新 Deployment 流程

输入: kubernetes-dashboard/kubernetes-dashboard
    ↓
[1] 获取 Deployment 信息
    → kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard
    ↓
[2] 提取所有容器镜像
    → kubernetesui/dashboard:v2.7.0
    → kubernetesui/metrics-scraper:v1.0.8
    ↓
[3] 批量缓存镜像
    → ./cache-image.sh kubernetesui/dashboard:v2.7.0 kubernetesui/metrics-scraper:v1.0.8
    ↓
[4] 更新 Deployment 镜像
    → kubectl set image deployment/kubernetes-dashboard \
        kubernetes-dashboard=harbor.omniverseai.net:8080/library/dashboard:v2.7.0 \
        dashboard-metrics-scraper=harbor.omniverseai.net:8080/library/metrics-scraper:v1.0.8
    ↓
[5] 验证 Pod 重启
    → kubectl get pods -n kubernetes-dashboard
    ↓
✅ 完成

集成到 Claude 工作流

在部署脚本中使用

#!/bin/bash
# deploy-my-app.sh

set -e

# 定义镜像
IMAGES=(
    "myorg/myapp:v1.0.0"
    "redis:7.2-alpine"
    "nginx:alpine"
)

# 预缓存镜像
echo "=== 预缓存镜像 ==="
/path/to/cache-image.sh "${IMAGES[@]}"

# 部署应用
echo "=== 部署应用 ==="
kubectl apply -f deployment.yaml

# 等待就绪
echo "=== 等待 Pod 就绪 ==="
kubectl rollout status deployment/myapp -n default

作为 Pre-flight Check

#!/bin/bash
# preflight-check.sh

# 检查并缓存 Helm Chart 依赖镜像
CHART_IMAGES=$(helm template my-release ./mychart | grep "image:" | awk '{print $2}' | tr -d '"' | sort -u)

echo "=== 预缓存 Helm Chart 镜像 ==="
for image in $CHART_IMAGES; do
    /path/to/cache-image.sh "$image" || {
        echo "警告: 镜像 $image 缓存失败"
    }
done

# 部署 Helm Chart
helm install my-release ./mychart

在 CI/CD 中使用

Gitea Actions / GitHub Actions:

name: Deploy to Kubernetes

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Cache Docker images
        run: |
          chmod +x ./cache-image.sh
          ./cache-image.sh -f .ci/images.txt

      - name: Deploy to K8s
        run: |
          kubectl apply -f k8s/
          kubectl rollout status deployment/myapp

故障排查

问题 1: EU Harbor 连接失败

错误:

[✗] 两个源都拉取失败: nginx:alpine

解决:

# 检查 EU Harbor 连通性
curl -I https://harbor.eu.lingjingwanxiang.cn

# 检查 Docker 代理配置
docker info | grep -i proxy

# 手动登录测试
docker login harbor.eu.lingjingwanxiang.cn -u admin -p Harbor12345

问题 2: 本地 Harbor 推送失败

错误:

[✗] 推送失败
denied: requested access to the resource is denied

解决:

# 检查本地 Harbor 状态
curl http://192.168.1.83:8080/api/v2.0/health

# 检查 library 项目是否存在
curl -u admin:admin123 http://192.168.1.83:8080/api/v2.0/projects | jq '.[] | select(.name=="library")'

# 重新登录
docker login harbor.omniverseai.net:8080 -u admin -p admin123

问题 3: Deployment 更新失败

错误:

Error from server (NotFound): deployments.apps "kubernetes-dashboard" not found

解决:

# 检查 Deployment 是否存在
kubectl get deployment -n kubernetes-dashboard

# 检查命名空间格式
./cache-image.sh --update-deployment kubernetes-dashboard/kubernetes-dashboard
#                                      ↑ namespace       ↑ deployment name

高级技巧

1. 指定自定义 Harbor 项目

# 推送到 myproject 项目
./cache-image.sh -p myproject nginx:alpine

# 结果: harbor.omniverseai.net:8080/myproject/nginx:alpine

2. 批量处理带注释的镜像列表

images.txt:

# Production images
nginx:1.24-alpine          # Web server
redis:7.2-alpine           # Cache

# Development images
python:3.11-slim           # Dev runtime
postgres:15-alpine         # Dev database
./cache-image.sh -f images.txt
# 自动跳过注释和空行

3. 组合使用 kubectl 和 cache-image

# 获取集群中所有使用的镜像
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | \
  sort -u > cluster-images.txt

# 全部缓存到本地
./cache-image.sh -f cluster-images.txt

4. 并行缓存(使用 GNU Parallel)

# 安装 parallel
brew install parallel

# 并行缓存(4 个并发)
cat images.txt | parallel -j4 ./cache-image.sh {}

性能优化

缓存统计

# 单个镜像平均耗时: ~30-60 秒
# - 拉取: 15-30s(取决于镜像大小)
# - 推送: 10-20s
# - 验证: 5-10s

# 批量缓存 10 个镜像: ~5-10 分钟

加速建议

  1. 使用本地 Docker 缓存

    • Docker 会缓存已拉取的 layer
    • 相同 base image 只需拉取一次
  2. 批量操作优先

    # 推荐(单次登录)
    ./cache-image.sh img1 img2 img3
    
    # 不推荐(多次登录)
    ./cache-image.sh img1
    ./cache-image.sh img2
    ./cache-image.sh img3
    
  3. 使用 --dry-run 预检

    ./cache-image.sh --dry-run -f large-list.txt
    # 快速验证镜像格式和数量
    

最佳实践

1. 部署前预缓存

# 创建项目镜像清单
cat > myapp-images.txt << EOF
myapp/backend:v1.2.3
myapp/frontend:v1.2.3
redis:7.2-alpine
postgres:15
EOF

# 部署前缓存
./cache-image.sh -f myapp-images.txt

# 然后部署
kubectl apply -f myapp-deployment.yaml

2. 定期同步关键镜像

# 创建 cron 任务
0 2 * * * /path/to/cache-image.sh -f /path/to/critical-images.txt

3. 版本化镜像管理

# 按环境组织
./cache-image.sh -f images-prod.txt
./cache-image.sh -f images-staging.txt
./cache-image.sh -f images-dev.txt

相关文档


脚本位置: /Users/brunogao/work/codes/tools/macos/k8s/cache-image.sh 最后更新: 2025-12-24 版本: v1.0