kubectl get pod 的状态字段,或是频繁执行 kubectl describe pod 盲目排查问题。但实际上,K8s 中 Pod 非运行状态多达数十种,不同异常状态的故障根源与处理方案差异极大。kubectl、crictl 等核心工具的深度排查技巧,全方位助力高效定位疑难问题。
1. Pod 生命周期与状态解析
1.1 Pod 生命周期阶段
Pod 的生命周期分为多个阶段(phase),通过 status.phase 字段表示:
# kubectl get pod -o yaml 中的 Pod 状态
status:
phase: Running # Pending | Running | Succeeded | Failed | Unknown
conditions:
- type: Initialized # 初始化容器是否完成
status: "True"
- type: Ready # Pod 是否可以接收流量
status: "True"
- type: ContainersReady # 所有容器是否就绪
status: "True"
- type: PodScheduled # 是否已调度到节点
status: "True"
Phase 与 Conditions 的关系:
-
Pending+PodScheduled=False→ 调度失败 -
Pending+Initialized=False→ 初始化容器失败 -
Pending+ContainersReady=False→ 容器启动失败 -
Running+Ready=False→ 存活探针失败 -
Failed→ 容器进程退出且未配置 restartPolicy
1.2 Pod 状态快速诊断
#!/bin/bash
# k8s_pod_status_diag.sh
# Pod 状态快速诊断脚本
POD_NAME="${1:-}"
NAMESPACE="${2:-default}"
if [ -z "$POD_NAME" ]; then
echo "用法: $0 <Pod名称> [命名空间]"
exit 1
fi
echo "========================================"
echo "Pod 状态诊断"
echo "Pod: $POD_NAME"
echo "命名空间: $NAMESPACE"
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
echo ""
# 获取 Pod 概要信息
echo "【Pod 概要】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o wide
echo ""
# 获取 Pod 详细信息
echo "【Pod 详细状态】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o yaml | grep -A 20 "status:"
echo ""
# 获取最近事件
echo "【相关事件】"
kubectl get events -n "$NAMESPACE" \
--field-selector involvedObject.name="$POD_NAME" \
--sort-by='.lastTimestamp' | tail -20
echo ""
# 获取容器状态
echo "【容器状态】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.containerStatuses[*]}' | python3 -m json.tool 2>/dev/null || \
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.containerStatuses}'
echo ""
# 检查容器重启次数
echo "【容器重启统计】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.containerStatuses[*].restartCount}'
echo ""
1.3 常见 Pod 状态速查
|
|
|
|
|---|---|---|
Pending |
|
|
ContainerCreating |
|
|
Running |
|
Ready 状态和探针 |
CrashLoopBackOff |
|
|
ImagePullBackOff |
|
|
ErrImagePull |
|
|
Evicted |
|
|
Terminating |
|
|
Unknown |
|
|
2. 镜像相关问题
2.1 镜像拉取失败的常见原因
镜像相关问题是 Pod 启动失败最常见的原因之一。ImagePullBackOff 和 ErrImagePull 是两个典型的镜像拉取失败状态。
# 查看 Pod 事件中的镜像相关错误
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 5 "ImagePull"
# 示例输出:
# Warning Failed 45s (x4 over 2m) kubelet Error: ImagePullBackOff
# Warning Failed 45s kubelet Failed to pull image "myregistry.com/myapp:v1":
# rpc error: code = Unknown desc = failed to pull and unpack image
# 查看详细的拉取错误
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 10 "Warning"
镜像名称拼写错误:
# 错误示例:镜像名拼写错误
# kubectl run myapp --image=myap:v1 # 拼写错误,应该是 myapp
# 正确做法:使用完整的镜像路径
kubectl run myapp --image=registry.example.com/myorg/myapp:v1.0.0
私有镜像认证问题:
# 创建 Secret 保存仓库认证信息
kubectl create secret docker-registry myregistry-secret \
--docker-server=registry.example.com \
--docker-username=admin \
--docker-password=StrongPassword2026! \
--docker-email=admin@example.com \
-n mynamespace
# 在 Pod 中引用 Secret
kubectl get pod myapp-abc123 -o yaml | kubectl replace --force -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: myapp-abc123
spec:
imagePullSecrets:
- name: myregistry-secret
containers:
- name: myapp
image: registry.example.com/myorg/myapp:v1.0.0
EOF
# 或在 ServiceAccount 中关联(影响该 SA 下所有 Pod)
kubectl patch serviceaccount default \
-p '{"imagePullSecrets":[{"name":"myregistry-secret"}]}' \
-n mynamespace
镜像 tag 指向错误:
# 使用 latest tag 的风险
# latest 指向的镜像可能随时变化,导致部署不确定性
# 正确做法:使用不可变的 tag(版本号、commit hash、时间戳)
# good: myapp:v1.2.3
# good: myapp:sha256:abc123...
# good: myapp:2026-04-10
# bad: myapp:latest
2.2 镜像预热与拉取策略
#!/bin/bash
# preload_images.sh
# 节点镜像预热脚本(减少 Pod 启动时间)
set -euo pipefail
IMAGES=(
"registry.example.com/myorg/base:v1.0"
"registry.example.com/myorg/app:v2.1"
"registry.example.com/myorg/nginx:alpine"
)
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
for image in "${IMAGES[@]}"; do
log "预热镜像: $image"
docker pull "$image"
done
log "镜像预热完成"
imagePullPolicy 配置:
# 镜像拉取策略
spec:
containers:
- name: myapp
image: myapp:v1.0
imagePullPolicy: Always # Always | IfNotPresent | Never
# 策略说明:
# Always: 每次启动都拉取镜像(默认用于 :latest tag)
# IfNotPresent: 本地存在则使用本地,不存在则拉取(默认用于指定 tag)
# Never: 从不拉取,仅使用本地镜像
2.3 镜像健康检查脚本
#!/bin/bash
# check_images.sh
# 检查集群中所有节点使用的镜像
set -euo pipefail
NAMESPACE="${1:-}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
get_images_from_nodes() {
kubectl get nodes -o jsonpath='{.items[*].status.nodeInfo.containerRuntimeVersion}'
}
get_pod_images() {
if [ -n "$NAMESPACE" ]; then
kubectl get pods -n "$NAMESPACE" -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | sort | uniq
else
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | sort | uniq
fi
}
log "===== 所有 Pod 使用的镜像 ====="
get_pod_images
log "===== 节点信息 ====="
kubectl get nodes -o wide
log "===== 镜像 Pod 分布 ====="
for image in $(get_pod_images); do
count=$(kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' | grep -c "$image" || echo "0")
log "$image: $count 个 Pod"
done
3. 资源不足与调度失败
3.1 调度失败的表现
Pod 处于 Pending 状态且 PodScheduled=False,通常是调度失败或资源不足。
# 查看调度失败的原因
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 20 "Events:"
# 典型输出:
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Warning FailedScheduling 32s default-scheduler 0/5 nodes are available:
# 3 Insufficient memory, 2 node(s) had taints that the pod didn't tolerate.
# 查看节点资源状态
kubectl describe nodes | grep -A 5 "Allocated resources"
# Allocated resources:
# Resource Requests Limits
# cpu 2500m (62%) 6 (150%)
# memory 4Gi (80%) 8Gi (160%)
3.2 资源请求与限制
# 查看 Pod 的资源请求和限制
kubectl get pod myapp-abc123 -n mynamespace -o jsonpath='{.spec.containers[*].resources}'
# {"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"250m","memory":"128Mi"}}
资源请求(requests)vs 资源限制(limits):
-
requests:调度时使用的资源量,节点必须满足 requests 才能调度 -
limits:容器运行时的资源上限,超出 limits 会被限制(CPU)或 OOM Kill(内存)
# 典型的资源配置
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: myapp
image: myapp:v1
resources:
requests:
cpu: "250m" # 0.25 核 CPU
memory: "128Mi" # 128 MB 内存
limits:
cpu: "1000m" # 最多 1 核 CPU
memory: "512Mi" # 最多 512 MB 内存
3.3 调度失败排查脚本
#!/bin/bash
# k8s_scheduler_diag.sh
# Kubernetes 调度失败诊断脚本
set -euo pipefail
NAMESPACE="${1:-default}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
echo "===== 调度失败的 Pod ====="
kubectl get pods -n "$NAMESPACE" --field-selector=status.phase=Pending -o wide
echo ""
echo "===== 节点资源概览 ====="
kubectl top nodes 2>/dev/null || echo "metrics-server 未安装或不可用"
echo ""
echo "===== 各节点已分配资源 ====="
kubectl describe nodes | grep -A 10 "Allocated resources"
echo ""
echo "===== 存在资源不足的 Pod 事件 ====="
for pod in $(kubectl get pods -n "$NAMESPACE" --field-selector=status.phase=Pending -o name); do
echo "--- $pod ---"
kubectl describe "$pod" -n "$NAMESPACE" | grep -E "(Insufficient|FailedScheduling|tolerations)" | head -5
done
echo ""
echo "===== 节点污点情况 ====="
kubectl get nodes -o custom-columns=NODE:.metadata.name,TAINTS:.spec.taints
echo ""
echo "===== 未调度的 Pod 及原因 ====="
kubectl get pods -n "$NAMESPACE" --field-selector=status.phase=Pending -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[?(@.type=="PodScheduled")].reason}{"\n"}{end}'
3.4 资源不足的解决方案
# 方案1: 扩容节点
kubectl scale deployment myapp --replicas=3
# 方案2: 降低 Pod 资源请求
kubectl patch deployment myapp -p '{
"spec": {
"template": {
"spec": {
"containers":[{
"name": "myapp",
"resources": {
"requests": {
"cpu": "100m",
"memory": "64Mi"
}
}
}]
}
}
}
}'
# 方案3: 驱逐低优先级 Pod(为高优先级 Pod 腾出空间)
kubectl get pods --sort-by='.spec.priority' -n "$NAMESPACE"
# 方案4: 添加新节点(配合集群自动扩缩容)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Node
metadata:
name: new-node
spec:
providerID: aws:///i-xxxxx
EOF
# 方案5: 调整 Pod 优先级
kubectl patch deployment myapp -p '{
"spec": {
"template": {
"spec": {
"priorityClassName": "high-priority"
}
}
}
}'
3.5 污点与容忍诊断
#!/bin/bash
# k8s_taint_diag.sh
# 污点与容忍诊断脚本
set -euo pipefail
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
log "===== 节点污点 ====="
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{range .spec.taints[*]} - {.key}={.effect} (Added by {.addedBy}){"\n"}{end}{"\n"}{end}'
echo ""
log "===== Pod 容忍 ====="
kubectl get pods -A -o custom-columns=\
NAMESPACE:.metadata.namespace,\
NAME:.metadata.name,\
TOLERATIONS:.spec.tolerations
echo ""
log "===== 无法调度的 Pod(污点原因)====="
for pod in $(kubectl get pods -A --field-selector=status.phase=Pending -o name); do
reason=$(kubectl get "$pod" -o jsonpath='{.status.conditions[?(@.type=="PodScheduled")].reason}')
msg=$(kubectl get "$pod" -o jsonpath='{.status.conditions[?(@.type=="PodScheduled")].message}')
if echo "$msg" | grep -qi "taint"; then
echo "$pod: $reason - $msg"
fi
done
4. 存储挂载异常
4.1 存储相关问题
Pod 启动时需要挂载 PersistentVolume(PV)或配置映射(ConfigMap)、密钥(Secret)。存储问题会导致 Pod 停留在 ContainerCreating 状态。
# 查看存储挂载相关错误
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 10 "MountPropagation"
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 5 "VolumeMount"
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 10 "Volumes:"
# 常见错误:
# "MountVolume.SetUp failed" - 卷挂载失败
# "Unable to attach or mount volumes" - 无法挂载卷
# "multi-attach error" - 卷被多个 Pod 同时挂载
4.2 PVC 状态诊断
#!/bin/bash
# k8s_pvc_diag.sh
# PVC 状态诊断脚本
set -euo pipefail
NAMESPACE="${1:-default}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
log "===== PVC 状态 ====="
kubectl get pvc -n "$NAMESPACE"
echo ""
log "===== PVC 详细信息 ====="
kubectl describe pvc -n "$NAMESPACE"
echo ""
log "===== PVC 绑定状态为 Pending 的 Pod ====="
kubectl get pods -n "$NAMESPACE" -o jsonpath='{range .items[*]}
Pod: {.metadata.name}
Status: {.status.phase}
Conditions: {.status.conditions[?(@.type=="PodScheduled")].reason}
Volumes: {.spec.volumes[*].name}
{"\n"}{end}' | grep -A 3 "Status: Pending"
echo ""
log "===== StorageClass 信息 ====="
kubectl get storageclass
kubectl describe storageclass
echo ""
log "===== 检查 PV 状态 ====="
kubectl get pv
kubectl describe pv
4.3 ConfigMap 和 Secret 问题
# 查看 ConfigMap 相关错误
kubectl describe pod myapp-abc123 | grep -A 5 "ConfigMap"
# 查看 Secret 相关错误
kubectl describe pod myapp-abc123 | grep -A 5 "Secret"
# 检查 ConfigMap 是否存在
kubectl get configmap myconfig -n mynamespace
# 检查 Secret 是否存在
kubectl get secret mysecret -n mynamespace
# 检查引用的 ConfigMap/Secret 版本
kubectl get pod myapp-abc123 -o jsonpath='{.spec.volumes[*].configMap.name}'
kubectl get pod myapp-abc123 -o jsonpath='{.spec.volumes[*].secret.secretName}'
# ConfigMap 更新后强制 Pod 重新加载(通常需要重启 Pod)
kubectl rollout restart deployment myapp -n mynamespace
# 或手动删除 Pod 触发重建
kubectl delete pod myapp-abc123 -n mynamespace
4.4 HostPath 问题
# HostPath 卷问题排查
kubectl describe pod myapp-abc123 | grep -A 10 "HostPath"
# 检查宿主机路径是否存在
kubectl get pod myapp-abc123 -o jsonpath='{.spec.volumes[*].hostPath.path}'
# 例如:/data/logs
# 在节点上检查
# kubectl exec -it myapp-abc123 -n mynamespace -- sh
# 在节点上检查:
# ls -la /data/logs
# 常见 HostPath 问题:
# 1. 路径不存在
# 2. 路径存在但权限不足
# 3. 路径是文件而非目录
5. 网络配置问题
5.1 网络相关问题表现
Pod 处于 ContainerCreating 状态但不是网络插件( CNI)问题,就是 Service/NetworkPolicy 配置问题。
# 查看 CNI 相关错误
kubectl describe pod myapp-abc123 -n mynamespace | grep -A 5 "NetworkPlugin"
kubectl describe pod myapp-abc123 -n mynamespace | grep -i "cni\|network"
# 查看 Pod IP 分配情况
kubectl get pod myapp-abc123 -n mynamespace -o jsonpath='{.status.podIP}'
kubectl get pod myapp-abc123 -n mynamespace -o jsonpath='{.status.podIPs}'
5.2 DNS 问题排查
DNS 是 Kubernetes 网络中最容易出问题的环节之一。
#!/bin/bash
# k8s_dns_diag.sh
# DNS 问题诊断脚本
set -euo pipefail
POD_NAME="${1:-}"
NAMESPACE="${2:-default}"
if [ -z "$POD_NAME" ]; then
echo "用法: $0 <Pod名称> [命名空间]"
exit 1
fi
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
log "===== 检查 Pod DNS 配置 ====="
kubectl exec -it "$POD_NAME" -n "$NAMESPACE" -- cat /etc/resolv.conf
echo ""
log "===== 测试 DNS 解析 ====="
kubectl exec -it "$POD_NAME" -n "$NAMESPACE" -- nslookup kubernetes.default 2>&1 | head -5
kubectl exec -it "$POD_NAME" -n "$NAMESPACE" -- nslookup google.com 2>&1 | head -5
echo ""
log "===== 测试网络连通性 ====="
kubectl exec -it "$POD_NAME" -n "$NAMESPACE" -- ping -c 3 8.8.8.8
echo ""
log "===== 查看 CoreDNS Pod 状态 ====="
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20
echo ""
log "===== CoreDNS 配置 ====="
kubectl get configmap coredns -n kube-system -o yaml
5.3 Service 和 Endpoint 问题
# 查看 Service 关联的 Endpoints
kubectl get endpoints myapp-service -n mynamespace
# 如果 Endpoints 为空,说明没有 Pod 匹配 Service 的 selector
kubectl describe service myapp-service -n mynamespace | grep -A 5 "Selector"
# 检查 Pod 是否匹配 Service Selector
kubectl get pods -n mynamespace -l app=myapp --show-labels
# 端到端连通性测试
kubectl exec -it test-pod -n mynamespace -- wget -qO- http://myapp-service.mynamespace.svc.cluster.local:8080/health
5.4 网络策略问题
# 查看 Pod 的网络策略
kubectl get networkpolicy -A
# 检查特定 Pod 是否被 NetworkPolicy 限制
kubectl describe pod myapp-abc123 -n mynamespace | grep -i "policy"
# 测试 Pod 间网络(从测试 Pod 访问目标 Pod)
kubectl run test-pod --image=busybox:1.36 -n mynamespace --restart=Never --rm -it -- wget -qO- http://myapp-service.mynamespace.svc.cluster.local:8080/
6. 深度排障工具
6.1 kubectl debug 高级用法
Kubernetes 1.20+ 提供了 kubectl debug 命令,可以在不修改 Pod 的情况下进行调试。
# 在 Pod 所在节点上启动调试容器
kubectl debug myapp-abc123 -n mynamespace -it --image=busybox:1.36 --share-processes --copy-to=myapp-debug
# 查看调试容器的 Shell
kubectl exec -it myapp-debug -n mynamespace -- sh
# 复制 Pod 的网络命名空间进行调试
kubectl debug myapp-abc123 -n mynamespace --image=busybox:1.36 -it --container=myapp --copy-to=myapp-netdebug
# 检查节点级别问题(节点调试)
kubectl debug node/my-node -it --image=busybox:1.36
# 在节点 shell 中执行:
# ls /var/log/pods/
# crictl ps
# crictl logs <container-id>
6.2 crictl 工具
crictl 是 container runtime interface(CRI)的 CLI 工具,用于直接与 containerd 或 CRI-O 交互。当 kubectl 无法访问时(如节点网络故障),crictl 是最后一道排障手段。
# 查看容器列表
crictl ps -a
# 查看镜像列表
crictl images
# 查看容器日志
crictl logs <container-id>
# 查看容器详细信息
crictl inspect <container-id>
# 进入容器(如果支持 exec)
crictl exec -it <container-id> sh
# 查看容器挂载
crictl inspect <container-id> | grep -A 20 "mounts"
# 重启容器(相当于 kubelet 重建)
crictl stop <container-id>
crictl rm <container-id>
# 然后删除 Pod,kubelet 会重新创建
# 查看沙箱/pause 容器
crictl pods
# 资源统计
crictl stats
6.3 kubelet 日志分析
# 查看 kubelet 日志(systemd 环境)
journalctl -u kubelet -n 100 --no-pager
# 查看特定 Pod 的 kubelet 日志(按 pod UID 过滤)
journalctl --no-pager | grep -E "podUID|myapp-abc123"
# kubelet 日志中的常见信息
# "Container runtime network not ready" - CNI 未就绪
# "Image garbage collection failed" - 镜像垃圾回收失败
# "Failed to start container" - 容器启动失败
# "Failed to kill pod" - 删除 Pod 失败
# 检查 kubelet 配置
kubectl get cm kubelet-config -n kube-system -o yaml
# 查看 kubelet 服务状态
systemctl status kubelet
systemctl restart kubelet
6.4 完整排障脚本
#!/bin/bash
# k8s_deep_diag.sh
# Kubernetes Pod 深度排障脚本
set -euo pipefail
POD_NAME="${1:-}"
NAMESPACE="${2:-default}"
if [ -z "$POD_NAME" ]; then
echo "用法: $0 <Pod名称> [命名空间]"
exit 1
fi
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
echo "========================================"
echo "Kubernetes Pod 深度排障"
echo "Pod: $POD_NAME"
echo "命名空间: $NAMESPACE"
echo "========================================"
# 1. 基本状态
echo ""
echo "【1. Pod 基本状态】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o wide
STATUS=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.phase}')
log_ok "Pod Phase: $STATUS"
# 2. 事件分析
echo ""
echo "【2. Pod 事件】"
EVENTS=$(kubectl get events -n "$NAMESPACE" \
--field-selector involvedObject.name="$POD_NAME" \
--sort-by='.lastTimestamp' | tail -10)
if echo "$EVENTS" | grep -qi "error\|fail\|backoff"; then
log_error "发现错误/失败事件"
fi
echo "$EVENTS"
# 3. 容器状态
echo ""
echo "【3. 容器状态】"
CONTAINERS=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{range .status.containerStatuses[*]}
名称: {.name}
状态: {.state}
重启: {.restartCount}
{"\n"}{end}')
echo "$CONTAINERS"
# 4. 镜像检查
echo ""
echo "【4. 镜像状态】"
IMAGES=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{range .spec.containers[*]}Container: {.name}, Image: {.image}{"\n"}{end}')
echo "$IMAGES"
# 5. 资源配置
echo ""
echo "【5. 资源请求与限制】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.containers[*].resources}' | python3 -m json.tool
# 6. 节点状态
echo ""
echo "【6. 调度的节点】"
NODE=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.nodeName}')
if [ -n "$NODE" ]; then
log_ok "节点: $NODE"
kubectl describe node "$NODE" | grep -A 5 "Allocated resources"
kubectl top node "$NODE" 2>/dev/null || log_warn "metrics-server 不可用"
else
log_error "Pod 未调度到任何节点"
fi
# 7. PVC 挂载
echo ""
echo "【7. PVC 挂载】"
PVS=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{range .spec.volumes[*]}{if .persistentVolumeClaim}PVC: {.persistentVolumeClaim.claimName}{"\n"}{end}{end}')
if [ -n "$PVS" ]; then
echo "$PVS"
for pvc in $(echo "$PVS" | grep PVC | awk '{print $2}'); do
kubectl get pvc "$pvc" -n "$NAMESPACE"
done
else
log_ok "无 PVC 挂载"
fi
# 8. ConfigMap/Secret
echo ""
echo "【8. ConfigMap/Secret】"
CM_COUNT=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{len .spec.volumes}' 2>/dev/null || echo "0")
if [ "$CM_COUNT" -gt 0 ]; then
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{range .spec.volumes[*]}{.name}: {.configMap.name}{.secret.secretName}{"\n"}{end}'
fi
# 9. 网络状态
echo ""
echo "【9. 网络状态】"
POD_IP=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.status.podIP}')
log_ok "Pod IP: $POD_IP"
# 10. 存活探针
echo ""
echo "【10. 探针配置】"
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.containers[*].livenessProbe}' | python3 -m json.tool 2>/dev/null || \
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.containers[*].readinessProbe}' | python3 -m json.tool
echo ""
echo "========================================"
echo "排障完成"
echo "========================================"
7. 常见故障场景与处理
7.1 CrashLoopBackOff
CrashLoopBackOff 表示容器启动后立即崩溃,kubelet 反复尝试重启。
# 查看容器退出原因
kubectl logs myapp-abc123 -n mynamespace --previous
# 查看容器退出码
kubectl get pod myapp-abc123 -n mynamespace -o jsonpath='{.status.containerStatuses[*].lastState.terminated.exitCode}'
# 常见退出码:
# 0: 正常退出(可能是 main 进程提前退出)
# 1: 一般错误(应用崩溃)
# 137: 被 SIGKILL(内存不足或 OOM)
# 139: 段错误(SIGSEGV)
# 143: 优雅退出(SIGTERM)
# 典型原因分析
# 1. 应用启动失败(配置错误、依赖不可用)
# 2. 内存不足被 OOM Kill
# 3. 健康检查失败
# 4. 权限问题
OOM Kill 处理:
# 检查节点是否存在 OOM
dmesg | grep -i "oom" | tail -20
# 检查 Pod 的内存限制
kubectl get pod myapp-abc123 -o jsonpath='{.spec.containers[*].resources.limits.memory}'
# 增加内存限制
kubectl patch deployment myapp -n mynamespace -p '{
"spec": {
"template": {
"spec": {
"containers":[{
"name": "myapp",
"resources": {
"limits": {"memory": "1Gi"}
}
}]
}
}
}
}'
7.2 Init 容器失败
# Init 容器失败会导致主容器永远无法启动
kubectl get pod myapp-abc123 -o jsonpath='{.status.initContainerStatuses}'
# 查看 Init 容器日志
kubectl logs myapp-abc123 -n mynamespace -c my-init-container --previous
# 常见 Init 容器问题:
# 1. Init 容器镜像拉取失败
# 2. Init 容器执行失败(exit code != 0)
# 3. Init 容器超时
# 解决方案
# 1. 检查 Init 容器配置
kubectl get pod myapp-abc123 -o yaml | grep -A 10 "initContainers"
# 2. 延长 Init 容器超时时间
kubectl patch deployment myapp -n mynamespace -p '{
"spec": {
"template": {
"spec": {
"initContainers":[{
"name": "my-init",
"resources": {},
"imagePullPolicy": "IfNotPresent"
}]
}
}
}
}'
7.3 存活探针失败
# 存活探针失败会导致 Pod 被重启
kubectl describe pod myapp-abc123 | grep -A 5 "Liveness"
# 临时禁用探针进行测试
kubectl run myapp-test --image=myapp:v1 --restart=Never -- \
/bin/sh -c "sleep 3600"
# 常见探针问题:
# 1. 探针路径错误(应用未提供 /health 端点)
# 2. 探针端口错误
# 3. 应用启动过慢(需要 initialDelaySeconds)
# 4. 探针超时时间过短
# 调整探针配置
kubectl patch deployment myapp -n mynamespace -p '{
"spec": {
"template": {
"spec": {
"containers":[{
"name": "myapp",
"livenessProbe": {
"httpGet": {"path": "/health", "port": 8080},
"initialDelaySeconds": 30,
"periodSeconds": 10,
"failureThreshold": 3
}
}]
}
}
}
}'
7.4 Evicted(被驱逐)
Pod 被驱逐通常是因为节点资源压力或运维操作。
# 查看被驱逐的 Pod
kubectl get pods -n mynamespace --field-selector=status.phase=Failed | grep Evicted
# 驱逐原因
kubectl describe pod myapp-abc123 | grep -A 3 "Reason: Evicted"
# 常见驱逐原因:
# 1. 节点内存压力 (MemoryPressure)
# 2. 节点磁盘压力 (DiskPressure)
# 3. 节点 PID 压力 (PIDPressure)
# 4. 运维主动驱逐 (kubectl drain)
# 处理:删除被驱逐的 Pod,Deployment 会创建新的
kubectl delete pod myapp-abc123 -n mynamespace --grace-period=0 --force
# 检查节点资源状态
kubectl describe node | grep -E "MemoryPressure|DiskPressure|PIDPressure|Conditions"
7.5 Terminating 状态卡住
Pod 删除后一直处于 Terminating 状态。
# 查看 Terminating 原因
kubectl describe pod myapp-abc123 | grep -A 10 "Conditions"
# 常见原因:
# 1. Finalizers 未完成
# 2. 存储卷未卸载
# 3. 网络插件问题
# 4. 容器未响应 SIGTERM
# 强制删除(谨慎使用)
kubectl delete pod myapp-abc123 -n mynamespace --grace-period=0 --force
# 检查 finalizers
kubectl get pod myapp-abc123 -o jsonpath='{.spec.finalizers}'
# 如果是 finalizers 问题,移除 finalizers
kubectl patch pod myapp-abc123 -n mynamespace -p '{"metadata":{"finalizers":[]}}' --type=merge
# 检查 NFS 等存储挂载是否卡住
mount | grep nfs
8. 生产环境运维脚本
8.1 Pod 健康检查与自动恢复
#!/bin/bash
# k8s_pod_health_manager.sh
# Pod 健康检查与自动恢复脚本
set -euo pipefail
NAMESPACE="${1:-default}"
LOG_FILE="/var/log/k8s_pod_health.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 检查非 Running 状态的 Pod
check_pods() {
local problematic_pods=$(kubectl get pods -n "$NAMESPACE" --field-selector=status.phase!=Running -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null)
if [ -z "$problematic_pods" ]; then
log "所有 Pod 运行正常"
return 0
fi
for pod in $problematic_pods; do
STATUS=$(kubectl get pod "$pod" -n "$NAMESPACE" -o jsonpath='{.status.phase}')
REASON=$(kubectl get pod "$pod" -n "$NAMESPACE" -o jsonpath='{.status.conditions[?(@.type=="PodScheduled")].reason}')
log "发现问题 Pod: $pod (状态: $STATUS, 原因: $REASON)"
case "$STATUS" in
CrashLoopBackOff)
log "尝试重启: $pod"
kubectl delete pod "$pod" -n "$NAMESPACE" 2>/dev/null
;;
ImagePullBackOff)
log "检查镜像配置: $pod"
kubectl describe pod "$pod" -n "$NAMESPACE" | grep -A 5 "ImagePull"
;;
Evicted)
log "删除被驱逐的 Pod: $pod"
kubectl delete pod "$pod" -n "$NAMESPACE" --grace-period=0 --force 2>/dev/null
;;
Pending)
log "检查调度问题: $pod"
kubectl describe pod "$pod" -n "$NAMESPACE" | grep -A 10 "Events:"
;;
esac
done
}
# 检查 Deployment 副本数
check_replicas() {
kubectl get deployments -n "$NAMESPACE" -o custom-columns=\
NAME:.metadata.name,\
DESIRED:.spec.replicas,\
AVAILABLE:.status.availableReplicas,\
READY:.status.readyReplicas | tail -n +2 | while read LINE; do
NAME=$(echo "$LINE" | awk '{print $1}')
DESIRED=$(echo "$LINE" | awk '{print $2}')
AVAILABLE=$(echo "$LINE" | awk '{print $3}')
if [ "$AVAILABLE" != "$DESIRED" ]; then
log "副本数不足: $NAME (期望: $DESIRED, 可用: $AVAILABLE)"
fi
done
}
main() {
log "===== 开始 Pod 健康检查 ====="
check_pods
check_replicas
log "===== 检查完成 ====="
}
# 配合 cron 使用
# */5 * * * * /opt/scripts/k8s_pod_health_manager.sh default >> /var/log/k8s_pod_health.log 2>&1
main "$@"
8.2 批量 Pod 操作脚本
#!/bin/bash
# k8s_batch_ops.sh
# 批量 Pod 操作脚本
set -euo pipefail
OPERATION="${1:-status}"
NAMESPACE="${2:-default}"
LABEL="${3:-}"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 获取 Pod 列表
get_pods() {
if [ -n "$LABEL" ]; then
kubectl get pods -n "$NAMESPACE" -l "$LABEL" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
else
kubectl get pods -n "$NAMESPACE" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
fi
}
case "$OPERATION" in
list|status)
log "Pod 列表:"
kubectl get pods -n "$NAMESPACE" ${LABEL:+-l "$LABEL"} -o wide
;;
restart)
log "重启所有 Pod..."
for pod in $(get_pods); do
log "重启: $pod"
kubectl delete pod "$pod" -n "$NAMESPACE"
done
;;
scale)
REPLICAS="${4:-1}"
DEPLOYMENT="${5:-}"
if [ -n "$DEPLOYMENT" ]; then
log "扩缩容 Deployment $DEPLOYMENT 到 $REPLICAS 副本"
kubectl scale deployment "$DEPLOYMENT" -n "$NAMESPACE" --replicas="$REPLICAS"
else
log "错误: 需要指定 Deployment 名称"
exit 1
fi
;;
drain)
NODE="${4:-}"
if [ -n "$NODE" ]; then
log "驱逐节点 $NODE 上的 Pod"
kubectl drain "$NODE" -n "$NAMESPACE" --delete-emptydir-data --ignore-daemonsets --force
else
log "错误: 需要指定节点名称"
exit 1
fi
;;
*)
echo "用法: $0 {list|status|restart|scale|drain} [命名空间] [标签选择器] [额外参数]"
echo "示例:"
echo " $0 list # 列出所有 Pod"
echo " $0 restart default app=web # 重启带有 app=web 标签的 Pod"
echo " $0 scale default '' '' 3 myapp # 扩缩容 myapp 到 3 副本"
echo " $0 drain default '' node-1 # 驱逐 node-1 上的 Pod"
exit 1
;;
esac
8.3 日志收集与分析
#!/bin/bash
# k8s_log_collector.sh
# Pod 日志收集脚本
set -euo pipefail
NAMESPACE="${1:-default}"
APP_NAME="${2:-}"
OUTPUT_DIR="/var/log/k8s_logs/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 收集特定应用的所有 Pod 日志
if [ -n "$APP_NAME" ]; then
PODS=$(kubectl get pods -n "$NAMESPACE" -l app="$APP_NAME" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
for pod in $PODS; do
log "收集 Pod 日志: $pod"
kubectl logs "$pod" -n "$NAMESPACE" --tail=1000 > "$OUTPUT_DIR/${pod}.log" 2>&1
kubectl logs "$pod" -n "$NAMESPACE" --previous --tail=1000 > "$OUTPUT_DIR/${pod}_previous.log" 2>&1 || true
done
log "日志收集完成: $OUTPUT_DIR"
else
log "收集所有 Pod 最新日志..."
kubectl get pods -n "$NAMESPACE" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | while read pod; do
kubectl logs "$pod" -n "$NAMESPACE" --tail=500 > "$OUTPUT_DIR/${pod}.log" 2>&1
done
log "日志收集完成: $OUTPUT_DIR"
fi
# 生成日志分析报告
log "生成分析报告..."
cat > "$OUTPUT_DIR/report.txt" <<EOF
========================================
Kubernetes Pod 日志分析报告
命名空间: $NAMESPACE
应用: ${APP_NAME:-全部}
收集时间: $(date)
========================================
EOF
# 统计错误日志
if [ -n "$APP_NAME" ]; then
PODS=$(kubectl get pods -n "$NAMESPACE" -l app="$APP_NAME" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
for pod in $PODS; do
ERROR_COUNT=$(grep -ci "error\|exception\|fail\|fatal" "$OUTPUT_DIR/${pod}.log" 2>/dev/null || echo "0")
echo "$pod: $ERROR_COUNT 个错误" >> "$OUTPUT_DIR/report.txt"
done
fi
log "报告已生成: $OUTPUT_DIR/report.txt"
9. 排障流程图与总结
9.1 Pod 排障决策树
Pod 状态不是 Running?
│
├── Pending
│ ├── 检查 kubectl describe pod Events
│ │ ├── "FailedScheduling" → 资源不足/调度失败 → 参见调度诊断
│ │ ├── "Unschedulable" → 资源不足 → 增加节点或减少请求
│ │ └── "didn't have free ports" → 端口冲突
│ └── kubectl get events --field-selector involvedObject.name=<pod>
│
├── ContainerCreating
│ ├── 检查 kubectl describe pod
│ │ ├── "ImagePullBackOff" → 镜像拉取失败 → 检查镜像地址/认证
│ │ ├── "ErrImagePull" → 同上,早期阶段
│ │ ├── "MountVolume.SetUp failed" → 存储问题 → 检查 PVC/PV
│ │ └── "NetworkPlugin" → CNI 问题 → 检查网络插件
│ └── crictl ps -a 查看容器状态
│
├── CrashLoopBackOff
│ ├── kubectl logs <pod> --previous 查看崩溃日志
│ ├── 检查退出码
│ │ ├── 137 → 内存不足 OOM
│ │ ├── 1 → 应用错误
│ │ └── 0 → 进程正常退出但不应该退出
│ └── 检查资源限制和实际使用
│
├── ImagePullBackOff
│ ├── 检查镜像名称是否正确
│ ├── 检查 imagePullSecrets 是否配置
│ ├── 手动测试 docker pull 镜像
│ └── 检查私有仓库认证
│
├── Running 但 Ready=False
│ ├── 检查存活探针和就绪探针
│ ├── kubectl logs 查看探针路径
│ └── 检查应用 /health 端点
│
├── Evicted
│ ├── 检查节点资源状态
│ └── 重新调度 Pod
│
└── Terminating 卡住
├── 检查 finalizers
├── 检查存储挂载
└── 强制删除
9.2 核心排障命令速查
# Pod 基本信息
kubectl get pod <pod-name> -o wide
kubectl describe pod <pod-name>
kubectl get pod <pod-name> -o yaml
# 日志
kubectl logs <pod-name> --tail=100
kubectl logs <pod-name> --previous # 上一次运行的日志
# 事件
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name> --sort-by='.lastTimestamp'
# 资源使用
kubectl top pod <pod-name>
kubectl top node <node-name>
# 调度
kubectl get pods -n <namespace> --field-selector=status.phase=Pending
# 存储
kubectl get pvc -n <namespace>
kubectl describe pvc <pvc-name>
# 网络
kubectl get svc -n <namespace>
kubectl get endpoints <svc-name> -n <namespace>
# 节点级调试
kubectl debug <pod-name> -it --image=busybox -- sh
crictl ps -a
crictl logs <container-id>
journalctl -u kubelet -n 50 --no-pager
9.3 预防措施
-
资源配置合理:requests 和 limits 根据实际负载设置,避免资源耗尽和 OOM -
镜像预热:在 Pod 调度前预先拉取镜像,减少启动时间 -
健康检查合理:探针路径、端口、超时时间需与应用实际情况匹配 -
监控告警:对 Pod 非 Running 状态设置告警,及时发现问题 -
定期演练:模拟各类故障,验证排障流程的有效性
- 确认核心状态:通过
kubectl get pod明确 Pod 当前运行状态与所处生命周期阶段; - 优先排查事件:借助
kubectl describe pod输出的 Events 事件,快速获取原始错误信息; - 精准定位根因:结合事件中的 Reason 与报错描述,区分镜像、资源、存储、网络、健康探针等问题类型;
- 落地修复验证:依据故障根因匹配对应解决方案,完成修复后核验服务恢复状态。










暂无评论内容