#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: check_crash_reason.sh
# 功能描述: 自动诊断 Pod CrashLoopBackOff 的核心环境原因 (纯只读)
# 使用方式: ./check_crash_reason.sh <pod-name> [namespace]
# ==============================================================================
set -o pipefail
POD_NAME=$1
NAMESPACE=${2:-"default"}
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'
if [ -z "$POD_NAME" ]; then
echo -e "${RED}用法: $0 <pod-name> [namespace]${PLAIN}"
exit 1
fi
echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE} Pod CrashLoopBackOff 异常原因自动诊断链路 ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"
# 1. 检查退出码与 OOM
echo -e "\n${YELLOW}[1/4] 分析上一次崩溃退出码 (Exit Code)...${PLAIN}"
LAST_STATE=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.containerStatuses[0].lastState.terminated}')
if [ -z "$LAST_STATE" ] || [ "$LAST_STATE" == "{}" ]; then
echo -e " -> ${RED}[WARN] 未获取到 lastState 信息,容器可能尚未启动或处于其他异常状态。${PLAIN}"
else
EXIT_CODE=$(echo "$LAST_STATE" | grep -o '"exitCode":[^,]*' | awk -F':' '{print $2}')
REASON=$(echo "$LAST_STATE" | grep -o '"reason":[^,]*' | awk -F'"' '{print $4}')
echo -e " -> 提取到退出码: ${RED}${EXIT_CODE}${PLAIN}, 退出原因: ${RED}${REASON}${PLAIN}"
if [ "$EXIT_CODE" == "137" ] || [ "$REASON" == "OOMKilled" ]; then
echo -e " -> ${RED}[诊断] 触发强制终止 (137/OOMKilled),请排查 limits.memory 配额或存活探针日志。${PLAIN}"
elif [ "$EXIT_CODE" == "0" ]; then
echo -e " -> ${YELLOW}[诊断] 退出码为 0。请检查启动脚本是否在后台执行 (如包含 & 或 nohup),容器要求主进程前台常驻。${PLAIN}"
else
echo -e " -> ${YELLOW}[INFO] 进程异常退出,需结合下方配置检查与崩溃日志进一步定位。${PLAIN}"
fi
fi
# 2. 检查权限限制配置
echo -e "\n${YELLOW}[2/4] 检查安全上下文配置 (SecurityContext)...${PLAIN}"
RUN_AS_USER=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.securityContext.runAsUser}')
if [ -z "$RUN_AS_USER" ]; then
RUN_AS_USER=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.containers[0].securityContext.runAsUser}')
fi
if [ -n "$RUN_AS_USER" ]; then
echo -e " -> ${RED}[WARN] 检测到非 root 运行配置: runAsUser=${RUN_AS_USER}。${PLAIN}"
echo -e " -> ${YELLOW}[诊断] 请排查容器是否因权限不足导致写入文件或绑定端口时报 Permission denied。${PLAIN}"
else
echo -e " -> ${GREEN}[OK] 未配置 runAsUser 限制。${PLAIN}"
fi
# 3. 检查存活探针配置
echo -e "\n${YELLOW}[3/4] 检查存活探针配置与超时风险...${PLAIN}"
LIVENESS=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.containers[0].livenessProbe}')
if [ -n "$LIVENESS" ]; then
DELAY=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.containers[0].livenessProbe.initialDelaySeconds}')
echo -e " -> 检测到存活探针配置,initialDelaySeconds = ${DELAY}s。"
EVENTS=$(kubectl get events -n "$NAMESPACE" --field-selector involvedObject.name="$POD_NAME" | grep -i "Liveness probe failed")
if [ -n "$EVENTS" ]; then
echo -e " -> ${RED}[诊断] 检测到存活探针失败事件!应用启动耗时可能超过探针预期,建议调大 initialDelaySeconds 或配置 startupProbe。${PLAIN}"
else
echo -e " -> ${GREEN}[OK] 暂未在 Events 中发现探针失败记录。${PLAIN}"
fi
else
echo -e " -> ${GREEN}[OK] 未配置 livenessProbe。${PLAIN}"
fi
# 4. 提取上一实例崩溃日志
echo -e "\n${YELLOW}[4/4] 提取崩溃前最后 10 行日志 (--previous)...${PLAIN}"
kubectl logs "$POD_NAME" -n "$NAMESPACE" --previous --tail=10 2>/dev/null | while read -r line; do
echo -e " | $line"
done || echo -e " -> ${YELLOW}无法获取上一实例日志 (容器可能已被重建或未曾成功启动)。${PLAIN}"
echo -e "\n${BLUE}================================================================${PLAIN}"
echo -e "${BLUE}诊断结束。若以上配置均正常且日志报找不到文件,请检查 volumeMounts 是否覆盖了原目录。${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"