676 lines
14 KiB
Markdown
676 lines
14 KiB
Markdown
# 自动镜像缓存解决方案
|
||
|
||
## 方案对比
|
||
|
||
| 方案 | 透明度 | 自动化 | 复杂度 | 推荐度 |
|
||
|------|--------|--------|--------|--------|
|
||
| **1. OrbStack Registry Mirrors** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ |
|
||
| **2. Docker Pull Wrapper** | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
|
||
| **3. kubectl Wrapper** | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
|
||
| **4. K8s Mutating Webhook** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||
| **5. Claude Code Integration** | ⭐⭐⭐ | ⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐ |
|
||
|
||
---
|
||
|
||
## ⭐ 推荐方案:多层防护策略
|
||
|
||
### 层级 1: OrbStack Registry Mirrors(最透明)
|
||
|
||
**原理**: 配置 OrbStack 使用本地 Harbor 作为 registry mirror,所有镜像拉取自动走缓存。
|
||
|
||
**优点**:
|
||
- ✅ 完全透明,无需修改代码
|
||
- ✅ 对所有容器运行时生效
|
||
- ✅ 性能最优
|
||
- ✅ 无需 wrapper
|
||
|
||
**缺点**:
|
||
- ⚠️ 需要预先缓存镜像
|
||
- ⚠️ 仅支持 Docker Hub
|
||
|
||
**实现**: 见下文
|
||
|
||
---
|
||
|
||
### 层级 2: Docker/kubectl Wrapper(智能拦截)
|
||
|
||
**原理**: 创建命令 wrapper,自动检测失败并缓存镜像。
|
||
|
||
**优点**:
|
||
- ✅ 自动检测和修复
|
||
- ✅ 支持所有 registry
|
||
- ✅ 可记录日志
|
||
|
||
**缺点**:
|
||
- ⚠️ 需要使用 wrapper 命令
|
||
|
||
**实现**: 见下文
|
||
|
||
---
|
||
|
||
### 层级 3: Claude Code CLAUDE.md 集成(AI 辅助)
|
||
|
||
**原理**: 在 CLAUDE.md 中添加自动修复指令,Claude 遇到镜像问题自动执行。
|
||
|
||
**优点**:
|
||
- ✅ Claude 自动识别
|
||
- ✅ 无需人工干预
|
||
- ✅ 灵活智能
|
||
|
||
**实现**: 见下文
|
||
|
||
---
|
||
|
||
## 实现方案
|
||
|
||
### 方案 1: OrbStack Registry Mirrors(推荐)
|
||
|
||
#### 配置文件位置
|
||
```
|
||
~/.orbstack/config/docker.json
|
||
```
|
||
|
||
#### 配置内容
|
||
```json
|
||
{
|
||
"registry-mirrors": [
|
||
"http://harbor.omniverseai.net:8080"
|
||
],
|
||
"insecure-registries": [
|
||
"harbor.omniverseai.net:8080"
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 自动配置脚本
|
||
```bash
|
||
#!/bin/bash
|
||
# setup-orbstack-registry-mirror.sh
|
||
|
||
ORBSTACK_CONFIG="$HOME/.orbstack/config/docker.json"
|
||
HARBOR_MIRROR="http://harbor.omniverseai.net:8080"
|
||
|
||
# 创建配置目录
|
||
mkdir -p "$(dirname "$ORBSTACK_CONFIG")"
|
||
|
||
# 生成配置
|
||
cat > "$ORBSTACK_CONFIG" << EOF
|
||
{
|
||
"registry-mirrors": [
|
||
"$HARBOR_MIRROR"
|
||
],
|
||
"insecure-registries": [
|
||
"$HARBOR_MIRROR"
|
||
],
|
||
"max-concurrent-downloads": 10,
|
||
"max-concurrent-uploads": 5
|
||
}
|
||
EOF
|
||
|
||
echo "✅ OrbStack 配置已更新"
|
||
echo "⚠️ 需要重启 OrbStack 生效"
|
||
echo ""
|
||
echo "重启命令: orb restart"
|
||
```
|
||
|
||
#### 验证
|
||
```bash
|
||
# 检查配置
|
||
cat ~/.orbstack/config/docker.json
|
||
|
||
# 重启 OrbStack
|
||
orb restart
|
||
|
||
# 测试拉取
|
||
docker pull nginx:alpine
|
||
|
||
# 查看是否使用镜像
|
||
docker pull nginx:alpine 2>&1 | grep -i harbor
|
||
```
|
||
|
||
#### 限制
|
||
- ⚠️ 仅适用于 Docker Hub 镜像
|
||
- ⚠️ 需要提前缓存镜像到 Harbor
|
||
- ⚠️ gcr.io, quay.io 等不走 mirror
|
||
|
||
---
|
||
|
||
### 方案 2: Docker Pull Wrapper
|
||
|
||
#### 创建 Wrapper
|
||
```bash
|
||
#!/bin/bash
|
||
# /usr/local/bin/docker-cached
|
||
|
||
REAL_DOCKER="/usr/bin/docker"
|
||
CACHE_SCRIPT="/Users/brunogao/work/codes/tools/macos/k8s/cache-image.sh"
|
||
|
||
# 如果是 pull 命令
|
||
if [[ "$1" == "pull" ]]; then
|
||
IMAGE="$2"
|
||
|
||
echo "🔍 检查镜像: $IMAGE"
|
||
|
||
# 尝试直接拉取
|
||
if ! $REAL_DOCKER pull "$IMAGE" 2>&1 | tee /tmp/docker-pull.log | grep -q "error\|failed\|timeout"; then
|
||
echo "✅ 直接拉取成功"
|
||
exit 0
|
||
fi
|
||
|
||
# 拉取失败,使用缓存
|
||
echo "⚠️ 直接拉取失败,使用 Harbor 缓存..."
|
||
|
||
if [[ -x "$CACHE_SCRIPT" ]]; then
|
||
$CACHE_SCRIPT "$IMAGE"
|
||
|
||
# 拉取本地 Harbor 镜像
|
||
LOCAL_IMAGE="harbor.omniverseai.net:8080/library/${IMAGE##*/}"
|
||
$REAL_DOCKER pull "$LOCAL_IMAGE"
|
||
|
||
# 重新标记为原始名称
|
||
$REAL_DOCKER tag "$LOCAL_IMAGE" "$IMAGE"
|
||
|
||
echo "✅ 已从 Harbor 缓存拉取"
|
||
else
|
||
echo "❌ 缓存脚本不存在: $CACHE_SCRIPT"
|
||
exit 1
|
||
fi
|
||
else
|
||
# 其他命令直接透传
|
||
exec $REAL_DOCKER "$@"
|
||
fi
|
||
```
|
||
|
||
#### 使用方式
|
||
```bash
|
||
# 方式 1: alias
|
||
alias docker='docker-cached'
|
||
|
||
# 方式 2: PATH 优先级
|
||
export PATH="/usr/local/bin:$PATH"
|
||
|
||
# 使用
|
||
docker pull nginx:alpine # 自动缓存
|
||
```
|
||
|
||
---
|
||
|
||
### 方案 3: kubectl Wrapper(智能检测)
|
||
|
||
#### 创建 Wrapper
|
||
```bash
|
||
#!/bin/bash
|
||
# /usr/local/bin/kubectl-cached
|
||
|
||
REAL_KUBECTL="/usr/local/bin/kubectl"
|
||
CACHE_SCRIPT="/Users/brunogao/work/codes/tools/macos/k8s/cache-image.sh"
|
||
|
||
# 执行原始命令
|
||
$REAL_KUBECTL "$@"
|
||
EXIT_CODE=$?
|
||
|
||
# 如果是 apply/create 命令,检查 ImagePull 错误
|
||
if [[ "$1" =~ ^(apply|create)$ ]]; then
|
||
sleep 5 # 等待 Pod 创建
|
||
|
||
# 检查是否有 ImagePull 失败
|
||
FAILED_PODS=$($REAL_KUBECTL get pods --all-namespaces -o json | \
|
||
jq -r '.items[] | select(.status.containerStatuses[]?.state.waiting?.reason | test("ImagePull|ErrImage")) | "\(.metadata.namespace)/\(.metadata.name)"')
|
||
|
||
if [[ -n "$FAILED_PODS" ]]; then
|
||
echo ""
|
||
echo "⚠️ 检测到镜像拉取失败,自动修复中..."
|
||
echo ""
|
||
|
||
while IFS= read -r pod; do
|
||
NS="${pod%/*}"
|
||
POD_NAME="${pod##*/}"
|
||
|
||
# 获取 Deployment/StatefulSet
|
||
OWNER=$($REAL_KUBECTL get pod "$POD_NAME" -n "$NS" -o json | \
|
||
jq -r '.metadata.ownerReferences[]? | select(.kind | test("ReplicaSet|StatefulSet")) | .name')
|
||
|
||
if [[ "$OWNER" =~ ^(.+)-[a-z0-9]+$ ]]; then
|
||
DEPLOY_NAME="${BASH_REMATCH[1]}"
|
||
|
||
echo "🔧 修复: $NS/$DEPLOY_NAME"
|
||
$CACHE_SCRIPT --update-deployment "$NS/$DEPLOY_NAME"
|
||
fi
|
||
done <<< "$FAILED_PODS"
|
||
|
||
echo ""
|
||
echo "✅ 自动修复完成"
|
||
fi
|
||
fi
|
||
|
||
exit $EXIT_CODE
|
||
```
|
||
|
||
#### 使用方式
|
||
```bash
|
||
# alias
|
||
alias kubectl='kubectl-cached'
|
||
|
||
# 部署
|
||
kubectl apply -f deployment.yaml # 自动检测并修复
|
||
```
|
||
|
||
---
|
||
|
||
### 方案 4: Claude Code CLAUDE.md 集成
|
||
|
||
#### 在项目根目录添加配置
|
||
|
||
**位置**: `/Users/brunogao/work/codes/tools/macos/CLAUDE.md`
|
||
|
||
**内容**:
|
||
```markdown
|
||
# Claude Code 配置
|
||
|
||
## 镜像拉取问题自动修复
|
||
|
||
当遇到以下情况时,自动执行镜像缓存:
|
||
|
||
### 识别条件
|
||
- Kubernetes Pod 状态为 `ImagePullBackOff` 或 `ErrImagePull`
|
||
- kubectl 命令返回镜像拉取错误
|
||
- docker pull 命令超时或失败
|
||
|
||
### 自动修复流程
|
||
1. 检测到镜像拉取失败
|
||
2. 自动执行: `/Users/brunogao/work/codes/tools/macos/k8s/cache-image.sh --update-deployment <namespace>/<deployment>`
|
||
3. 等待 Pod 重启
|
||
4. 验证修复成功
|
||
|
||
### 快速修复命令
|
||
```bash
|
||
# 自动修复 Deployment
|
||
cd /Users/brunogao/work/codes/tools/macos/k8s
|
||
./cache-image.sh --update-deployment <namespace>/<deployment>
|
||
```
|
||
|
||
### 手动缓存
|
||
```bash
|
||
# 缓存单个镜像
|
||
./cache-image.sh <image>:<tag>
|
||
|
||
# 批量缓存
|
||
./cache-image.sh <image1> <image2> <image3>
|
||
```
|
||
|
||
### 重要提示
|
||
- ✅ 优先使用 `--update-deployment` 自动修复
|
||
- ✅ 所有镜像会缓存到 `harbor.omniverseai.net:8080/library/`
|
||
- ✅ 脚本支持 Docker Hub, GCR, Quay 等所有公共仓库
|
||
- ⚠️ 遇到 ImagePull 问题必须立即执行,不要等待
|
||
```
|
||
|
||
---
|
||
|
||
### 方案 5: 综合自动化脚本
|
||
|
||
#### 创建智能监控脚本
|
||
```bash
|
||
#!/bin/bash
|
||
# watch-and-fix-images.sh
|
||
# 持续监控并自动修复镜像拉取问题
|
||
|
||
CACHE_SCRIPT="/Users/brunogao/work/codes/tools/macos/k8s/cache-image.sh"
|
||
CHECK_INTERVAL=30
|
||
|
||
echo "🔍 启动镜像拉取监控..."
|
||
echo "检查间隔: ${CHECK_INTERVAL}s"
|
||
echo ""
|
||
|
||
while true; do
|
||
# 查找所有 ImagePull 失败的 Pods
|
||
FAILED_PODS=$(kubectl get pods --all-namespaces -o json 2>/dev/null | \
|
||
jq -r '.items[] |
|
||
select(.status.containerStatuses[]?.state.waiting?.reason |
|
||
test("ImagePull|ErrImage")) |
|
||
"\(.metadata.namespace)/\(.metadata.name)"')
|
||
|
||
if [[ -n "$FAILED_PODS" ]]; then
|
||
echo "⚠️ [$(date '+%Y-%m-%d %H:%M:%S')] 检测到镜像拉取失败"
|
||
echo ""
|
||
|
||
# 去重 Deployment
|
||
declare -A FIXED_DEPLOYS
|
||
|
||
while IFS= read -r pod; do
|
||
[[ -z "$pod" ]] && continue
|
||
|
||
NS="${pod%/*}"
|
||
POD_NAME="${pod##*/}"
|
||
|
||
# 获取 Deployment
|
||
OWNER=$(kubectl get pod "$POD_NAME" -n "$NS" -o json 2>/dev/null | \
|
||
jq -r '.metadata.ownerReferences[]? |
|
||
select(.kind | test("ReplicaSet")) | .name')
|
||
|
||
if [[ "$OWNER" =~ ^(.+)-[a-z0-9]+$ ]]; then
|
||
DEPLOY_NAME="${BASH_REMATCH[1]}"
|
||
DEPLOY_KEY="$NS/$DEPLOY_NAME"
|
||
|
||
# 避免重复修复
|
||
if [[ -z "${FIXED_DEPLOYS[$DEPLOY_KEY]}" ]]; then
|
||
echo "🔧 自动修复: $DEPLOY_KEY"
|
||
|
||
if $CACHE_SCRIPT --update-deployment "$DEPLOY_KEY" &>> /tmp/image-fix.log; then
|
||
echo " ✅ 修复成功"
|
||
FIXED_DEPLOYS[$DEPLOY_KEY]=1
|
||
else
|
||
echo " ❌ 修复失败,查看日志: /tmp/image-fix.log"
|
||
fi
|
||
|
||
echo ""
|
||
fi
|
||
fi
|
||
done <<< "$FAILED_PODS"
|
||
|
||
echo "等待 Pod 重启..."
|
||
sleep 10
|
||
fi
|
||
|
||
sleep $CHECK_INTERVAL
|
||
done
|
||
```
|
||
|
||
#### 后台运行
|
||
```bash
|
||
# 启动监控
|
||
nohup ./watch-and-fix-images.sh > /tmp/image-watch.log 2>&1 &
|
||
|
||
# 查看日志
|
||
tail -f /tmp/image-watch.log
|
||
|
||
# 停止监控
|
||
pkill -f watch-and-fix-images.sh
|
||
```
|
||
|
||
---
|
||
|
||
## 最佳实践:组合方案
|
||
|
||
### 推荐配置(多层防护)
|
||
|
||
```
|
||
层级 1: OrbStack Registry Mirror
|
||
↓ (90% 镜像直接缓存)
|
||
层级 2: kubectl wrapper 自动检测
|
||
↓ (剩余镜像自动修复)
|
||
层级 3: Claude Code CLAUDE.md
|
||
↓ (AI 辅助决策)
|
||
层级 4: 手动 cache-image.sh
|
||
↓ (兜底方案)
|
||
```
|
||
|
||
### 实施步骤
|
||
|
||
#### Step 1: 配置 OrbStack Mirror
|
||
```bash
|
||
./setup-orbstack-registry-mirror.sh
|
||
orb restart
|
||
```
|
||
|
||
#### Step 2: 安装 kubectl wrapper
|
||
```bash
|
||
sudo cp kubectl-cached /usr/local/bin/
|
||
echo 'alias kubectl="kubectl-cached"' >> ~/.zshrc
|
||
source ~/.zshrc
|
||
```
|
||
|
||
#### Step 3: 配置 Claude Code
|
||
```bash
|
||
# 在项目根目录 CLAUDE.md 添加镜像修复指令
|
||
cat >> /Users/brunogao/work/codes/tools/macos/CLAUDE.md << 'EOF'
|
||
|
||
## 自动镜像缓存
|
||
|
||
遇到 ImagePullBackOff 时自动执行:
|
||
```bash
|
||
cd /Users/brunogao/work/codes/tools/macos/k8s
|
||
./cache-image.sh --update-deployment <namespace>/<deployment>
|
||
```
|
||
EOF
|
||
```
|
||
|
||
#### Step 4: (可选)启动后台监控
|
||
```bash
|
||
# 开机自启动
|
||
cat > ~/Library/LaunchAgents/com.k8s.image-watcher.plist << 'EOF'
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>Label</key>
|
||
<string>com.k8s.image-watcher</string>
|
||
<key>ProgramArguments</key>
|
||
<array>
|
||
<string>/Users/brunogao/work/codes/tools/macos/k8s/watch-and-fix-images.sh</string>
|
||
</array>
|
||
<key>RunAtLoad</key>
|
||
<true/>
|
||
<key>KeepAlive</key>
|
||
<true/>
|
||
<key>StandardOutPath</key>
|
||
<string>/tmp/image-watch.log</string>
|
||
<key>StandardErrorPath</key>
|
||
<string>/tmp/image-watch-error.log</string>
|
||
</dict>
|
||
</plist>
|
||
EOF
|
||
|
||
launchctl load ~/Library/LaunchAgents/com.k8s.image-watcher.plist
|
||
```
|
||
|
||
---
|
||
|
||
## 方案对比详细分析
|
||
|
||
### 1. OrbStack Registry Mirror
|
||
|
||
**工作原理**:
|
||
```
|
||
docker pull nginx:alpine
|
||
↓
|
||
OrbStack 拦截
|
||
↓
|
||
首先查询: harbor.omniverseai.net:8080/library/nginx:alpine
|
||
├─ 存在 → 直接使用 ✅
|
||
└─ 不存在 → 回退到 Docker Hub
|
||
```
|
||
|
||
**优点**:
|
||
- 完全透明
|
||
- 性能最高
|
||
- 无需代码修改
|
||
|
||
**缺点**:
|
||
- 仅支持 Docker Hub
|
||
- 需要预缓存
|
||
|
||
**适用场景**:
|
||
- 常用基础镜像(nginx, redis, mysql)
|
||
- 已知镜像列表
|
||
|
||
---
|
||
|
||
### 2. Docker/kubectl Wrapper
|
||
|
||
**工作原理**:
|
||
```
|
||
kubectl apply -f deployment.yaml
|
||
↓
|
||
kubectl-cached 拦截
|
||
↓
|
||
执行原始命令
|
||
↓
|
||
检测 ImagePull 错误
|
||
├─ 无错误 → 完成 ✅
|
||
└─ 有错误 → 自动执行 cache-image.sh
|
||
↓
|
||
缓存镜像
|
||
↓
|
||
更新 Deployment
|
||
↓
|
||
完成 ✅
|
||
```
|
||
|
||
**优点**:
|
||
- 自动检测
|
||
- 支持所有 registry
|
||
- 可记录日志
|
||
|
||
**缺点**:
|
||
- 首次部署会失败
|
||
- 需要等待修复
|
||
|
||
**适用场景**:
|
||
- 新镜像部署
|
||
- 不确定的镜像
|
||
|
||
---
|
||
|
||
### 3. Claude Code Integration
|
||
|
||
**工作原理**:
|
||
```
|
||
Claude 执行 kubectl apply
|
||
↓
|
||
检测到 ImagePullBackOff
|
||
↓
|
||
读取 CLAUDE.md 指令
|
||
↓
|
||
自动执行: ./cache-image.sh --update-deployment
|
||
↓
|
||
继续部署 ✅
|
||
```
|
||
|
||
**优点**:
|
||
- AI 自动决策
|
||
- 灵活智能
|
||
- 无需人工干预
|
||
|
||
**缺点**:
|
||
- 依赖 Claude Code
|
||
- 需要明确指令
|
||
|
||
**适用场景**:
|
||
- Claude 辅助开发
|
||
- 自动化部署
|
||
|
||
---
|
||
|
||
## 测试验证
|
||
|
||
### 测试场景 1: 新镜像部署
|
||
|
||
```bash
|
||
# 清除本地镜像
|
||
docker rmi nginx:alpine
|
||
|
||
# 测试 OrbStack mirror
|
||
docker pull nginx:alpine
|
||
# 预期: 从 Harbor 快速拉取
|
||
|
||
# 测试 kubectl wrapper
|
||
kubectl apply -f test-deployment.yaml
|
||
# 预期: 自动检测并修复
|
||
```
|
||
|
||
### 测试场景 2: GCR 镜像
|
||
|
||
```bash
|
||
# 测试 cache-image.sh
|
||
./cache-image.sh gcr.io/distroless/static:latest
|
||
|
||
# 验证缓存
|
||
docker pull harbor.omniverseai.net:8080/library/static:latest
|
||
```
|
||
|
||
---
|
||
|
||
## 监控和日志
|
||
|
||
### 查看 OrbStack Mirror 日志
|
||
```bash
|
||
# OrbStack 日志
|
||
tail -f ~/.orbstack/logs/docker.log | grep -i mirror
|
||
```
|
||
|
||
### 查看 wrapper 日志
|
||
```bash
|
||
# kubectl wrapper
|
||
tail -f /tmp/kubectl-cached.log
|
||
|
||
# docker wrapper
|
||
tail -f /tmp/docker-pull.log
|
||
```
|
||
|
||
### 查看自动修复日志
|
||
```bash
|
||
tail -f /tmp/image-fix.log
|
||
tail -f /tmp/image-watch.log
|
||
```
|
||
|
||
---
|
||
|
||
## 故障排查
|
||
|
||
### OrbStack Mirror 不生效
|
||
|
||
```bash
|
||
# 检查配置
|
||
cat ~/.orbstack/config/docker.json
|
||
|
||
# 检查 OrbStack 状态
|
||
orb status
|
||
|
||
# 重启 OrbStack
|
||
orb restart
|
||
|
||
# 验证
|
||
docker info | grep -i mirror
|
||
```
|
||
|
||
### Wrapper 未执行
|
||
|
||
```bash
|
||
# 检查 PATH
|
||
echo $PATH
|
||
|
||
# 检查 alias
|
||
alias | grep kubectl
|
||
|
||
# 检查权限
|
||
ls -l /usr/local/bin/kubectl-cached
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
| 场景 | 推荐方案 |
|
||
|------|----------|
|
||
| **日常使用** | OrbStack Mirror + kubectl wrapper |
|
||
| **Claude 开发** | CLAUDE.md + cache-image.sh |
|
||
| **生产环境** | 所有方案组合 |
|
||
| **快速修复** | cache-image.sh --update-deployment |
|
||
|
||
**最佳实践**:
|
||
1. 配置 OrbStack Mirror(透明加速)
|
||
2. 安装 kubectl wrapper(自动修复)
|
||
3. 配置 Claude Code(AI 辅助)
|
||
4. 保留 cache-image.sh(兜底方案)
|
||
|
||
**维护建议**:
|
||
- 定期同步常用镜像到 Harbor
|
||
- 监控 wrapper 日志
|
||
- 更新 CLAUDE.md 指令
|
||
|
||
---
|
||
|
||
**最后更新**: 2025-12-24
|
||
**作者**: Claude AI + Bruno Gao
|