Files
ljwx-k8s/watch-and-fix-images.sh
2026-02-20 22:50:45 +08:00

149 lines
4.4 KiB
Bash
Executable File

#!/bin/bash
# Kubernetes 镜像拉取问题后台监控和自动修复
# 用途:持续监控集群中的 ImagePull 错误并自动修复
# Note: 不使用 set -e,因为这是一个持续运行的监控脚本
# 允许单个命令失败而不退出整个脚本
CACHE_SCRIPT="/Users/brunogao/work/codes/tools/macos/k8s/cache-image.sh"
CHECK_INTERVAL="${CHECK_INTERVAL:-30}"
LOG_FILE="/tmp/image-watch.log"
FIX_LOG="/tmp/image-fix.log"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo -e "$msg" | tee -a "$LOG_FILE"
}
log_info() { log "${BLUE}[INFO]${NC} $1"; }
log_success() { log "${GREEN}[✓]${NC} $1"; }
log_error() { log "${RED}[✗]${NC} $1"; }
log_warning() { log "${YELLOW}[!]${NC} $1"; }
# 检查脚本是否存在
if [[ ! -x "$CACHE_SCRIPT" ]]; then
log_error "缓存脚本不存在或不可执行: $CACHE_SCRIPT"
exit 1
fi
# 信号处理
cleanup() {
log_info "收到停止信号,退出监控..."
exit 0
}
trap cleanup SIGINT SIGTERM
# 主监控循环
log_info "=== 启动 Kubernetes 镜像拉取监控 ==="
log_info "检查间隔: ${CHECK_INTERVAL}s"
log_info "日志文件: $LOG_FILE"
log_info "修复日志: $FIX_LOG"
log_info "按 Ctrl+C 停止"
echo ""
# 统计
TOTAL_CHECKS=0
TOTAL_FIXES=0
TOTAL_FAILURES=0
while true; do
((TOTAL_CHECKS++))
# 查找所有 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)"' 2>/dev/null || true)
if [[ -n "$FAILED_PODS" ]]; then
log_warning "检测到镜像拉取失败"
echo ""
# 去重 Deployment
declare -A PROCESSED
while IFS= read -r pod; do
[[ -z "$pod" ]] && continue
NS="${pod%/*}"
POD_NAME="${pod##*/}"
# 获取 Pod 的 owner
OWNER_INFO=$(kubectl get pod "$POD_NAME" -n "$NS" -o json 2>/dev/null | \
jq -r '.metadata.ownerReferences[]? |
select(.kind | test("ReplicaSet|StatefulSet")) |
"\(.kind)/\(.name)"' || true)
if [[ -z "$OWNER_INFO" ]]; then
log_warning " 跳过 $pod (无法获取 owner)"
continue
fi
OWNER_KIND="${OWNER_INFO%/*}"
OWNER_NAME="${OWNER_INFO##*/}"
# ReplicaSet 需要获取 Deployment
if [[ "$OWNER_KIND" == "ReplicaSet" ]]; then
if [[ "$OWNER_NAME" =~ ^(.+)-[a-z0-9]+$ ]]; then
DEPLOY_NAME="${BASH_REMATCH[1]}"
DEPLOY_KEY="$NS/$DEPLOY_NAME"
RESOURCE_TYPE="deployment"
else
log_warning " 跳过 $pod (无法解析 Deployment 名称)"
continue
fi
elif [[ "$OWNER_KIND" == "StatefulSet" ]]; then
DEPLOY_KEY="$NS/$OWNER_NAME"
DEPLOY_NAME="$OWNER_NAME"
RESOURCE_TYPE="statefulset"
else
log_warning " 跳过 $pod (不支持的类型: $OWNER_KIND)"
continue
fi
# 避免重复处理
if [[ -n "${PROCESSED[$DEPLOY_KEY]}" ]]; then
continue
fi
log_info "🔧 自动修复: $DEPLOY_KEY ($RESOURCE_TYPE)"
if "$CACHE_SCRIPT" --update-deployment "$DEPLOY_KEY" >> "$FIX_LOG" 2>&1; then
log_success " 修复成功"
((TOTAL_FIXES++))
PROCESSED[$DEPLOY_KEY]=1
else
log_error " 修复失败 (查看日志: $FIX_LOG)"
((TOTAL_FAILURES++))
fi
echo ""
done <<< "$FAILED_PODS"
if [[ ${#PROCESSED[@]} -gt 0 ]]; then
log_success "本轮共修复 ${#PROCESSED[@]} 个资源"
log_info "等待 Pod 重启..."
sleep 10
fi
echo ""
fi
# 每隔一段时间显示统计
if (( TOTAL_CHECKS % 10 == 0 )); then
log_info "统计: 检查 $TOTAL_CHECKS 次, 修复 $TOTAL_FIXES 次, 失败 $TOTAL_FAILURES 次"
fi
sleep "$CHECK_INTERVAL"
done