背景
在 Kubernetes 日常運(yùn)維中,Pod 起不來(lái)是最常見(jiàn)的故障形態(tài)之一。很多運(yùn)維工程師看到 Pod 狀態(tài)不是Running時(shí),第一反應(yīng)是盯著kubectl get pod的STATUS列反復(fù)查看,或者反復(fù)執(zhí)行kubectl describe pod期待找到答案。殊不知,Pod 的非 Running 狀態(tài)有幾十種,每種狀態(tài)的根因和處理方法完全不同。
本文以 Kubernetes 1.32(2026 年主流版本)為基礎(chǔ),系統(tǒng)講解 Pod 啟動(dòng)失敗的各種場(chǎng)景及其排障方法。內(nèi)容覆蓋 Pod 生命周期的完整解析、容器鏡像相關(guān)問(wèn)題、資源不足導(dǎo)致的調(diào)度失敗、存儲(chǔ)掛載異常、網(wǎng)絡(luò)配置問(wèn)題、以及使用 kubectl 和 crictl 工具進(jìn)行深度排障的技巧。
前置知識(shí)要求:了解 Kubernetes 基本概念(Pod、Deployment、Service、Namespace)、熟悉 kubectl 基礎(chǔ)命令、了解 Docker/容器基礎(chǔ)知識(shí)。
1. Pod 生命周期與狀態(tài)解析
1.1 Pod 生命周期階段
Pod 的生命周期分為多個(gè)階段(phase),通過(guò)status.phase字段表示:
# kubectl get pod -o yaml 中的 Pod 狀態(tài) status: phase:Running# Pending | Running | Succeeded | Failed | Unknown conditions: -type:Initialized # 初始化容器是否完成 status:"True" -type:Ready # Pod 是否可以接收流量 status:"True" -type:ContainersReady # 所有容器是否就緒 status:"True" -type:PodScheduled # 是否已調(diào)度到節(jié)點(diǎn) status:"True"
Phase 與 Conditions 的關(guān)系:
Pending+PodScheduled=False→ 調(diào)度失敗
Pending+Initialized=False→ 初始化容器失敗
Pending+ContainersReady=False→ 容器啟動(dòng)失敗
Running+Ready=False→ 存活探針失敗
Failed→ 容器進(jìn)程退出且未配置 restartPolicy
1.2 Pod 狀態(tài)快速診斷
#!/bin/bash
# k8s_pod_status_diag.sh
# Pod 狀態(tài)快速診斷腳本
POD_NAME="${1:-}"
NAMESPACE="${2:-default}"
if[ -z"$POD_NAME"];then
echo"用法:$0 [命名空間]"
exit1
fi
echo"========================================"
echo"Pod 狀態(tài)診斷"
echo"Pod:$POD_NAME"
echo"命名空間:$NAMESPACE"
echo"時(shí)間:$(date '+%Y-%m-%d %H:%M:%S')"
echo"========================================"
echo""
# 獲取 Pod 概要信息
echo"【Pod 概要】"
kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o wide
echo""
# 獲取 Pod 詳細(xì)信息
echo"【Pod 詳細(xì)狀態(tài)】"
kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o yaml | grep -A 20"status:"
echo""
# 獲取最近事件
echo"【相關(guān)事件】"
kubectl get events -n"$NAMESPACE"
--field-selector involvedObject.name="$POD_NAME"
--sort-by='.lastTimestamp'| tail -20
echo""
# 獲取容器狀態(tài)
echo"【容器狀態(tài)】"
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""
# 檢查容器重啟次數(shù)
echo"【容器重啟統(tǒng)計(jì)】"
kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o jsonpath='{.status.containerStatuses[*].restartCount}'
echo""
1.3 常見(jiàn) Pod 狀態(tài)速查
| STATUS | 含義 | 常見(jiàn)根因 |
|---|---|---|
| Pending | Pod 已創(chuàng)建但未調(diào)度到節(jié)點(diǎn) | 資源不足、節(jié)點(diǎn)選擇器不匹配、調(diào)度器故障 |
| ContainerCreating | 容器正在創(chuàng)建中 | 鏡像拉取中、存儲(chǔ)掛載中 |
| Running | 容器已啟動(dòng) | 但需檢查Ready狀態(tài)和探針 |
| CrashLoopBackOff | 容器反復(fù)崩潰重啟 | 應(yīng)用啟動(dòng)失敗、配置錯(cuò)誤、資源不足 |
| ImagePullBackOff | 鏡像拉取失敗 | 鏡像不存在、認(rèn)證失敗、網(wǎng)絡(luò)不通 |
| ErrImagePull | 鏡像拉取錯(cuò)誤 | 同上,但處于早期階段 |
| Evicted | Pod 被驅(qū)逐 | 資源壓力、節(jié)點(diǎn) drain |
| Terminating | 正在刪除 | finalizers 未完成、force kill 失敗 |
| Unknown | 無(wú)法獲取 Pod 狀態(tài) | 節(jié)點(diǎn)網(wǎng)絡(luò)問(wèn)題、API Server 連接問(wèn)題 |
2. 鏡像相關(guān)問(wèn)題
2.1 鏡像拉取失敗的常見(jiàn)原因
鏡像相關(guān)問(wèn)題是 Pod 啟動(dòng)失敗最常見(jiàn)的原因之一。ImagePullBackOff和ErrImagePull是兩個(gè)典型的鏡像拉取失敗狀態(tài)。
# 查看 Pod 事件中的鏡像相關(guān)錯(cuò)誤 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 # 查看詳細(xì)的拉取錯(cuò)誤 kubectl describe pod myapp-abc123 -n mynamespace | grep -A 10"Warning"
鏡像名稱拼寫錯(cuò)誤:
# 錯(cuò)誤示例:鏡像名拼寫錯(cuò)誤 # kubectl run myapp --image=myap:v1 # 拼寫錯(cuò)誤,應(yīng)該是 myapp # 正確做法:使用完整的鏡像路徑 kubectl run myapp --image=registry.example.com/myorg/myapp:v1.0.0
私有鏡像認(rèn)證問(wèn)題:
# 創(chuàng)建 Secret 保存?zhèn)}庫(kù)認(rèn)證信息
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 中關(guān)聯(lián)(影響該 SA 下所有 Pod)
kubectl patch serviceaccount default
? ? -p?'{"imagePullSecrets":[{"name":"myregistry-secret"}]}'?
? ? -n mynamespace
鏡像 tag 指向錯(cuò)誤:
# 使用 latest tag 的風(fēng)險(xiǎn) # latest 指向的鏡像可能隨時(shí)變化,導(dǎo)致部署不確定性 # 正確做法:使用不可變的 tag(版本號(hào)、commit hash、時(shí)間戳) # good: myapp:v1.2.3 # good: myappabc123... # good: myapp:2026-04-10 # bad: myapp:latest
2.2 鏡像預(yù)熱與拉取策略
#!/bin/bash
# preload_images.sh
# 節(jié)點(diǎn)鏡像預(yù)熱腳本(減少 Pod 啟動(dòng)時(shí)間)
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"
}
forimagein"${IMAGES[@]}";do
log"預(yù)熱鏡像:$image"
docker pull"$image"
done
log"鏡像預(yù)熱完成"
imagePullPolicy 配置:
# 鏡像拉取策略 spec: containers: -name:myapp image:myapp:v1.0 imagePullPolicy:Always# Always | IfNotPresent | Never # 策略說(shuō)明: # Always: 每次啟動(dòng)都拉取鏡像(默認(rèn)用于 :latest tag) # IfNotPresent: 本地存在則使用本地,不存在則拉?。J(rèn)用于指定 tag) # Never: 從不拉取,僅使用本地鏡像
2.3 鏡像健康檢查腳本
#!/bin/bash
# check_images.sh
# 檢查集群中所有節(jié)點(diǎn)使用的鏡像
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}{"
"}{end}'| sort | uniq
else
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"
"}{end}'| sort | uniq
fi
}
log"===== 所有 Pod 使用的鏡像 ====="
get_pod_images
log"===== 節(jié)點(diǎn)信息 ====="
kubectl get nodes -o wide
log"===== 鏡像 Pod 分布 ====="
forimagein$(get_pod_images);do
count=$(kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"
"}{end}'| grep -c"$image"||echo"0")
log"$image:$count個(gè) Pod"
done
3. 資源不足與調(diào)度失敗
3.1 調(diào)度失敗的表現(xiàn)
Pod 處于Pending狀態(tài)且PodScheduled=False,通常是調(diào)度失敗或資源不足。
# 查看調(diào)度失敗的原因 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. # 查看節(jié)點(diǎn)資源狀態(tài) 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 資源請(qǐng)求與限制
# 查看 Pod 的資源請(qǐng)求和限制
kubectl get pod myapp-abc123 -n mynamespace -o jsonpath='{.spec.containers[*].resources}'
# {"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"250m","memory":"128Mi"}}
資源請(qǐng)求(requests)vs 資源限制(limits):
requests:調(diào)度時(shí)使用的資源量,節(jié)點(diǎn)必須滿足 requests 才能調(diào)度
limits:容器運(yùn)行時(shí)的資源上限,超出 limits 會(huì)被限制(CPU)或 OOM Kill(內(nèi)存)
# 典型的資源配置 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 內(nèi)存 limits: cpu:"1000m" # 最多 1 核 CPU memory:"512Mi" # 最多 512 MB 內(nèi)存
3.3 調(diào)度失敗排查腳本
#!/bin/bash
# k8s_scheduler_diag.sh
# Kubernetes 調(diào)度失敗診斷腳本
set-euo pipefail
NAMESPACE="${1:-default}"
log() {
echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}
echo"===== 調(diào)度失敗的 Pod ====="
kubectl get pods -n"$NAMESPACE"--field-selector=status.phase=Pending -o wide
echo""
echo"===== 節(jié)點(diǎn)資源概覽 ====="
kubectl top nodes 2>/dev/null ||echo"metrics-server 未安裝或不可用"
echo""
echo"===== 各節(jié)點(diǎn)已分配資源 ====="
kubectl describe nodes | grep -A 10"Allocated resources"
echo""
echo"===== 存在資源不足的 Pod 事件 ====="
forpodin$(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"===== 節(jié)點(diǎn)污點(diǎn)情況 ====="
kubectl get nodes -o custom-columns=NODE:.metadata.name,TAINTS:.spec.taints
echo""
echo"===== 未調(diào)度的 Pod 及原因 ====="
kubectl get pods -n"$NAMESPACE"--field-selector=status.phase=Pending -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.status.conditions[?(@.type=="PodScheduled")].reason}{"
"}{end}'
3.4 資源不足的解決方案
# 方案1: 擴(kuò)容節(jié)點(diǎn)
kubectl scale deployment myapp --replicas=3
# 方案2: 降低 Pod 資源請(qǐng)求
kubectl patch deployment myapp -p'{
"spec": {
"template": {
"spec": {
"containers":[{
"name": "myapp",
"resources": {
"requests": {
"cpu": "100m",
"memory": "64Mi"
}
}
}]
}
}
}
}'
# 方案3: 驅(qū)逐低優(yōu)先級(jí) Pod(為高優(yōu)先級(jí) Pod 騰出空間)
kubectl get pods --sort-by='.spec.priority'-n"$NAMESPACE"
# 方案4: 添加新節(jié)點(diǎn)(配合集群自動(dòng)擴(kuò)縮容)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Node
metadata:
? name: new-node
spec:
? providerID: aws:///i-xxxxx
EOF
# 方案5: 調(diào)整 Pod 優(yōu)先級(jí)
kubectl patch deployment myapp -p?'{
? ? "spec": {
? ? ? ? "template": {
? ? ? ? ? ? "spec": {
? ? ? ? ? ? ? ? "priorityClassName": "high-priority"
? ? ? ? ? ? }
? ? ? ? }
? ? }
}'
3.5 污點(diǎn)與容忍診斷
#!/bin/bash
# k8s_taint_diag.sh
# 污點(diǎn)與容忍診斷腳本
set-euo pipefail
log() {
echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}
log"===== 節(jié)點(diǎn)污點(diǎn) ====="
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"
"}{range .spec.taints[*]} - {.key}={.effect} (Added by {.addedBy}){"
"}{end}{"
"}{end}'
echo""
log"===== Pod 容忍 ====="
kubectl get pods -A -o custom-columns=
NAMESPACE:.metadata.namespace,
NAME:.metadata.name,
TOLERATIONS:.spec.tolerations
echo""
log"===== 無(wú)法調(diào)度的 Pod(污點(diǎn)原因)====="
forpodin$(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}')
ifecho"$msg"| grep -qi"taint";then
echo"$pod:$reason-$msg"
fi
done
4. 存儲(chǔ)掛載異常
4.1 存儲(chǔ)相關(guān)問(wèn)題
Pod 啟動(dòng)時(shí)需要掛載 PersistentVolume(PV)或配置映射(ConfigMap)、密鑰(Secret)。存儲(chǔ)問(wèn)題會(huì)導(dǎo)致 Pod 停留在ContainerCreating狀態(tài)。
# 查看存儲(chǔ)掛載相關(guān)錯(cuò)誤 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:" # 常見(jiàn)錯(cuò)誤: # "MountVolume.SetUp failed" - 卷掛載失敗 # "Unable to attach or mount volumes" - 無(wú)法掛載卷 # "multi-attach error" - 卷被多個(gè) Pod 同時(shí)掛載
4.2 PVC 狀態(tài)診斷
#!/bin/bash
# k8s_pvc_diag.sh
# PVC 狀態(tài)診斷腳本
set-euo pipefail
NAMESPACE="${1:-default}"
log() {
echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}
log"===== PVC 狀態(tài) ====="
kubectl get pvc -n"$NAMESPACE"
echo""
log"===== PVC 詳細(xì)信息 ====="
kubectl describe pvc -n"$NAMESPACE"
echo""
log"===== PVC 綁定狀態(tài)為 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}
{"
"}{end}'| grep -A 3"Status: Pending"
echo""
log"===== StorageClass 信息 ====="
kubectl get storageclass
kubectl describe storageclass
echo""
log"===== 檢查 PV 狀態(tài) ====="
kubectl get pv
kubectl describe pv
4.3 ConfigMap 和 Secret 問(wèn)題
# 查看 ConfigMap 相關(guān)錯(cuò)誤
kubectl describe pod myapp-abc123 | grep -A 5"ConfigMap"
# 查看 Secret 相關(guān)錯(cuò)誤
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 更新后強(qiáng)制 Pod 重新加載(通常需要重啟 Pod)
kubectl rollout restart deployment myapp -n mynamespace
# 或手動(dòng)刪除 Pod 觸發(fā)重建
kubectl delete pod myapp-abc123 -n mynamespace
4.4 HostPath 問(wèn)題
# HostPath 卷問(wèn)題排查
kubectl describe pod myapp-abc123 | grep -A 10"HostPath"
# 檢查宿主機(jī)路徑是否存在
kubectl get pod myapp-abc123 -o jsonpath='{.spec.volumes[*].hostPath.path}'
# 例如:/data/logs
# 在節(jié)點(diǎn)上檢查
# kubectl exec -it myapp-abc123 -n mynamespace -- sh
# 在節(jié)點(diǎn)上檢查:
# ls -la /data/logs
# 常見(jiàn) HostPath 問(wèn)題:
# 1. 路徑不存在
# 2. 路徑存在但權(quán)限不足
# 3. 路徑是文件而非目錄
5. 網(wǎng)絡(luò)配置問(wèn)題
5.1 網(wǎng)絡(luò)相關(guān)問(wèn)題表現(xiàn)
Pod 處于ContainerCreating狀態(tài)但不是網(wǎng)絡(luò)插件( CNI)問(wèn)題,就是 Service/NetworkPolicy 配置問(wèn)題。
# 查看 CNI 相關(guān)錯(cuò)誤
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 問(wèn)題排查
DNS 是 Kubernetes 網(wǎng)絡(luò)中最容易出問(wèn)題的環(huán)節(jié)之一。
#!/bin/bash
# k8s_dns_diag.sh
# DNS 問(wèn)題診斷腳本
set-euo pipefail
POD_NAME="${1:-}"
NAMESPACE="${2:-default}"
if[ -z"$POD_NAME"];then
echo"用法:$0 [命名空間]"
exit1
fi
log() {
echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}
log"===== 檢查 Pod DNS 配置 ====="
kubectlexec-it"$POD_NAME"-n"$NAMESPACE"-- cat /etc/resolv.conf
echo""
log"===== 測(cè)試 DNS 解析 ====="
kubectlexec-it"$POD_NAME"-n"$NAMESPACE"-- nslookup kubernetes.default 2>&1 | head -5
kubectlexec-it"$POD_NAME"-n"$NAMESPACE"-- nslookup google.com 2>&1 | head -5
echo""
log"===== 測(cè)試網(wǎng)絡(luò)連通性 ====="
kubectlexec-it"$POD_NAME"-n"$NAMESPACE"-- ping -c 3 8.8.8.8
echo""
log"===== 查看 CoreDNS Pod 狀態(tài) ====="
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 問(wèn)題
# 查看 Service 關(guān)聯(lián)的 Endpoints kubectl get endpoints myapp-service -n mynamespace # 如果 Endpoints 為空,說(shuō)明沒(méi)有 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 # 端到端連通性測(cè)試 kubectlexec-ittest-pod -n mynamespace -- wget -qO- http://myapp-service.mynamespace.svc.cluster.local:8080/health
5.4 網(wǎng)絡(luò)策略問(wèn)題
# 查看 Pod 的網(wǎng)絡(luò)策略 kubectl get networkpolicy -A # 檢查特定 Pod 是否被 NetworkPolicy 限制 kubectl describe pod myapp-abc123 -n mynamespace | grep -i"policy" # 測(cè)試 Pod 間網(wǎng)絡(luò)(從測(cè)試 Pod 訪問(wèn)目標(biāo) Pod) kubectl runtest-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 高級(jí)用法
Kubernetes 1.20+ 提供了kubectl debug命令,可以在不修改 Pod 的情況下進(jìn)行調(diào)試。
# 在 Pod 所在節(jié)點(diǎn)上啟動(dòng)調(diào)試容器 kubectl debug myapp-abc123 -n mynamespace -it --image=busybox:1.36 --share-processes --copy-to=myapp-debug # 查看調(diào)試容器的 Shell kubectlexec-it myapp-debug -n mynamespace -- sh # 復(fù)制 Pod 的網(wǎng)絡(luò)命名空間進(jìn)行調(diào)試 kubectl debug myapp-abc123 -n mynamespace --image=busybox:1.36 -it --container=myapp --copy-to=myapp-netdebug # 檢查節(jié)點(diǎn)級(jí)別問(wèn)題(節(jié)點(diǎn)調(diào)試) kubectl debug node/my-node -it --image=busybox:1.36 # 在節(jié)點(diǎn) shell 中執(zhí)行: # ls /var/log/pods/ # crictl ps # crictl logs
6.2 crictl 工具
crictl 是 container runtime interface(CRI)的 CLI 工具,用于直接與 containerd 或 CRI-O 交互。當(dāng) kubectl 無(wú)法訪問(wèn)時(shí)(如節(jié)點(diǎn)網(wǎng)絡(luò)故障),crictl 是最后一道排障手段。
# 查看容器列表 crictl ps -a # 查看鏡像列表 crictl images # 查看容器日志 crictl logs# 查看容器詳細(xì)信息 crictl inspect # 進(jìn)入容器(如果支持 exec) crictlexec-it sh # 查看容器掛載 crictl inspect | grep -A 20"mounts" # 重啟容器(相當(dāng)于 kubelet 重建) crictl stop crictl rm # 然后刪除 Pod,kubelet 會(huì)重新創(chuàng)建 # 查看沙箱/pause 容器 crictl pods # 資源統(tǒng)計(jì) crictl stats
6.3 kubelet 日志分析
# 查看 kubelet 日志(systemd 環(huán)境) journalctl -u kubelet -n 100 --no-pager # 查看特定 Pod 的 kubelet 日志(按 pod UID 過(guò)濾) journalctl --no-pager | grep -E"podUID|myapp-abc123" # kubelet 日志中的常見(jiàn)信息 # "Container runtime network not ready" - CNI 未就緒 # "Image garbage collection failed" - 鏡像垃圾回收失敗 # "Failed to start container" - 容器啟動(dòng)失敗 # "Failed to kill pod" - 刪除 Pod 失敗 # 檢查 kubelet 配置 kubectl get cm kubelet-config -n kube-system -o yaml # 查看 kubelet 服務(wù)狀態(tài) 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 [命名空間]"
exit1
fi
RED='?33[0;31m'
GREEN='?33[0;32m'
YELLOW='?33[1;33m'
NC='?33[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. 基本狀態(tài)
echo""
echo"【1. Pod 基本狀態(tài)】"
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)
ifecho"$EVENTS"| grep -qi"error|fail|backoff";then
log_error"發(fā)現(xiàn)錯(cuò)誤/失敗事件"
fi
echo"$EVENTS"
# 3. 容器狀態(tài)
echo""
echo"【3. 容器狀態(tài)】"
CONTAINERS=$(kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o jsonpath='{range .status.containerStatuses[*]}
名稱: {.name}
狀態(tài): {.state}
重啟: {.restartCount}
{"
"}{end}')
echo"$CONTAINERS"
# 4. 鏡像檢查
echo""
echo"【4. 鏡像狀態(tài)】"
IMAGES=$(kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o jsonpath='{range .spec.containers[*]}Container: {.name}, Image: {.image}{"
"}{end}')
echo"$IMAGES"
# 5. 資源配置
echo""
echo"【5. 資源請(qǐng)求與限制】"
kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o jsonpath='{.spec.containers[*].resources}'| python3 -m json.tool
# 6. 節(jié)點(diǎn)狀態(tài)
echo""
echo"【6. 調(diào)度的節(jié)點(diǎn)】"
NODE=$(kubectl get pod"$POD_NAME"-n"$NAMESPACE"-o jsonpath='{.spec.nodeName}')
if[ -n"$NODE"];then
log_ok"節(jié)點(diǎn):$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 未調(diào)度到任何節(jié)點(diǎn)"
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}{"
"}{end}{end}')
if[ -n"$PVS"];then
echo"$PVS"
forpvcin$(echo"$PVS"| grep PVC | awk'{print $2}');do
kubectl get pvc"$pvc"-n"$NAMESPACE"
done
else
log_ok"無(wú) 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}{"
"}{end}'
fi
# 9. 網(wǎng)絡(luò)狀態(tài)
echo""
echo"【9. 網(wǎng)絡(luò)狀態(tài)】"
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. 常見(jiàn)故障場(chǎng)景與處理
7.1 CrashLoopBackOff
CrashLoopBackOff 表示容器啟動(dòng)后立即崩潰,kubelet 反復(fù)嘗試重啟。
# 查看容器退出原因
kubectl logs myapp-abc123 -n mynamespace --previous
# 查看容器退出碼
kubectl get pod myapp-abc123 -n mynamespace -o jsonpath='{.status.containerStatuses[*].lastState.terminated.exitCode}'
# 常見(jiàn)退出碼:
# 0: 正常退出(可能是 main 進(jìn)程提前退出)
# 1: 一般錯(cuò)誤(應(yīng)用崩潰)
# 137: 被 SIGKILL(內(nèi)存不足或 OOM)
# 139: 段錯(cuò)誤(SIGSEGV)
# 143: 優(yōu)雅退出(SIGTERM)
# 典型原因分析
# 1. 應(yīng)用啟動(dòng)失?。ㄅ渲缅e(cuò)誤、依賴不可用)
# 2. 內(nèi)存不足被 OOM Kill
# 3. 健康檢查失敗
# 4. 權(quán)限問(wèn)題
OOM Kill 處理:
# 檢查節(jié)點(diǎn)是否存在 OOM
dmesg | grep -i"oom"| tail -20
# 檢查 Pod 的內(nèi)存限制
kubectl get pod myapp-abc123 -o jsonpath='{.spec.containers[*].resources.limits.memory}'
# 增加內(nèi)存限制
kubectl patch deployment myapp -n mynamespace -p'{
"spec": {
"template": {
"spec": {
"containers":[{
"name": "myapp",
"resources": {
"limits": {"memory": "1Gi"}
}
}]
}
}
}
}'
7.2 Init 容器失敗
# Init 容器失敗會(huì)導(dǎo)致主容器永遠(yuǎn)無(wú)法啟動(dòng)
kubectl get pod myapp-abc123 -o jsonpath='{.status.initContainerStatuses}'
# 查看 Init 容器日志
kubectl logs myapp-abc123 -n mynamespace -c my-init-container --previous
# 常見(jiàn) Init 容器問(wèn)題:
# 1. Init 容器鏡像拉取失敗
# 2. Init 容器執(zhí)行失?。╡xit code != 0)
# 3. Init 容器超時(shí)
# 解決方案
# 1. 檢查 Init 容器配置
kubectl get pod myapp-abc123 -o yaml | grep -A 10"initContainers"
# 2. 延長(zhǎng) Init 容器超時(shí)時(shí)間
kubectl patch deployment myapp -n mynamespace -p'{
"spec": {
"template": {
"spec": {
"initContainers":[{
"name": "my-init",
"resources": {},
"imagePullPolicy": "IfNotPresent"
}]
}
}
}
}'
7.3 存活探針失敗
# 存活探針失敗會(huì)導(dǎo)致 Pod 被重啟 kubectl describe pod myapp-abc123 | grep -A 5"Liveness" # 臨時(shí)禁用探針進(jìn)行測(cè)試 kubectl run myapp-test --image=myapp:v1 --restart=Never -- /bin/sh -c"sleep 3600" # 常見(jiàn)探針問(wèn)題: # 1. 探針路徑錯(cuò)誤(應(yīng)用未提供 /health 端點(diǎn)) # 2. 探針端口錯(cuò)誤 # 3. 應(yīng)用啟動(dòng)過(guò)慢(需要 initialDelaySeconds) # 4. 探針超時(shí)時(shí)間過(guò)短 # 調(diào)整探針配置 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(被驅(qū)逐)
Pod 被驅(qū)逐通常是因?yàn)楣?jié)點(diǎn)資源壓力或運(yùn)維操作。
# 查看被驅(qū)逐的 Pod kubectl get pods -n mynamespace --field-selector=status.phase=Failed | grep Evicted # 驅(qū)逐原因 kubectl describe pod myapp-abc123 | grep -A 3"Reason: Evicted" # 常見(jiàn)驅(qū)逐原因: # 1. 節(jié)點(diǎn)內(nèi)存壓力 (MemoryPressure) # 2. 節(jié)點(diǎn)磁盤壓力 (DiskPressure) # 3. 節(jié)點(diǎn) PID 壓力 (PIDPressure) # 4. 運(yùn)維主動(dòng)驅(qū)逐 (kubectl drain) # 處理:刪除被驅(qū)逐的 Pod,Deployment 會(huì)創(chuàng)建新的 kubectl delete pod myapp-abc123 -n mynamespace --grace-period=0 --force # 檢查節(jié)點(diǎn)資源狀態(tài) kubectl describe node | grep -E"MemoryPressure|DiskPressure|PIDPressure|Conditions"
7.5 Terminating 狀態(tài)卡住
Pod 刪除后一直處于Terminating狀態(tài)。
# 查看 Terminating 原因
kubectl describe pod myapp-abc123 | grep -A 10"Conditions"
# 常見(jiàn)原因:
# 1. Finalizers 未完成
# 2. 存儲(chǔ)卷未卸載
# 3. 網(wǎng)絡(luò)插件問(wèn)題
# 4. 容器未響應(yīng) SIGTERM
# 強(qiáng)制刪除(謹(jǐn)慎使用)
kubectl delete pod myapp-abc123 -n mynamespace --grace-period=0 --force
# 檢查 finalizers
kubectl get pod myapp-abc123 -o jsonpath='{.spec.finalizers}'
# 如果是 finalizers 問(wèn)題,移除 finalizers
kubectl patch pod myapp-abc123 -n mynamespace -p'{"metadata":{"finalizers":[]}}'--type=merge
# 檢查 NFS 等存儲(chǔ)掛載是否卡住
mount | grep nfs
8. 排障流程圖與總結(jié)
8.1 Pod 排障決策樹
Pod 狀態(tài)不是 Running? │ ├── Pending │ ├── 檢查 kubectl describe pod Events │ │ ├──"FailedScheduling"→ 資源不足/調(diào)度失敗 → 參見(jiàn)調(diào)度診斷 │ │ ├──"Unschedulable"→ 資源不足 → 增加節(jié)點(diǎn)或減少請(qǐng)求 │ │ └──"didn't have free ports"→ 端口沖突 │ └── kubectl get events --field-selector involvedObject.name=│ ├── ContainerCreating │ ├── 檢查 kubectl describe pod │ │ ├──"ImagePullBackOff"→ 鏡像拉取失敗 → 檢查鏡像地址/認(rèn)證 │ │ ├──"ErrImagePull"→ 同上,早期階段 │ │ ├──"MountVolume.SetUp failed"→ 存儲(chǔ)問(wèn)題 → 檢查 PVC/PV │ │ └──"NetworkPlugin"→ CNI 問(wèn)題 → 檢查網(wǎng)絡(luò)插件 │ └── crictl ps -a 查看容器狀態(tài) │ ├── CrashLoopBackOff │ ├── kubectl logs --previous 查看崩潰日志 │ ├── 檢查退出碼 │ │ ├── 137 → 內(nèi)存不足 OOM │ │ ├── 1 → 應(yīng)用錯(cuò)誤 │ │ └── 0 → 進(jìn)程正常退出但不應(yīng)該退出 │ └── 檢查資源限制和實(shí)際使用 │ ├── ImagePullBackOff │ ├── 檢查鏡像名稱是否正確 │ ├── 檢查 imagePullSecrets 是否配置 │ ├── 手動(dòng)測(cè)試 docker pull 鏡像 │ └── 檢查私有倉(cāng)庫(kù)認(rèn)證 │ ├── Running 但 Ready=False │ ├── 檢查存活探針和就緒探針 │ ├── kubectl logs 查看探針路徑 │ └── 檢查應(yīng)用 /health 端點(diǎn) │ ├── Evicted │ ├── 檢查節(jié)點(diǎn)資源狀態(tài) │ └── 重新調(diào)度 Pod │ └── Terminating 卡住 ├── 檢查 finalizers ├── 檢查存儲(chǔ)掛載 └── 強(qiáng)制刪除
8.2 核心排障命令速查
# Pod 基本信息 kubectl get pod-o wide kubectl describe pod kubectl get pod -o yaml # 日志 kubectl logs --tail=100 kubectl logs --previous # 上一次運(yùn)行的日志 # 事件 kubectl get events -n --field-selector involvedObject.name= --sort-by='.lastTimestamp' # 資源使用 kubectl top pod kubectl top node # 調(diào)度 kubectl get pods -n --field-selector=status.phase=Pending # 存儲(chǔ) kubectl get pvc -n kubectl describe pvc # 網(wǎng)絡(luò) kubectl get svc -n kubectl get endpoints -n # 節(jié)點(diǎn)級(jí)調(diào)試 kubectl debug -it --image=busybox -- sh crictl ps -a crictl logs journalctl -u kubelet -n 50 --no-pager
8.3 預(yù)防措施
資源配置合理:requests 和 limits 根據(jù)實(shí)際負(fù)載設(shè)置,避免資源耗盡和 OOM
鏡像預(yù)熱:在 Pod 調(diào)度前預(yù)先拉取鏡像,減少啟動(dòng)時(shí)間
健康檢查合理:探針路徑、端口、超時(shí)時(shí)間需與應(yīng)用實(shí)際情況匹配
監(jiān)控告警:對(duì) Pod 非 Running 狀態(tài)設(shè)置告警,及時(shí)發(fā)現(xiàn)問(wèn)題
定期演練:模擬各類故障,驗(yàn)證排障流程的有效性
9. 總結(jié)
Pod 啟動(dòng)失敗是 Kubernetes 運(yùn)維中最常見(jiàn)的故障形態(tài),但并非所有非 Running 狀態(tài)都需要緊急處理。運(yùn)維工程師需要建立系統(tǒng)的排障思路:
首先確認(rèn)狀態(tài):通過(guò)kubectl get pod確認(rèn) Pod 的實(shí)際狀態(tài)和所屬階段
其次查看事件:通過(guò)kubectl describe pod的 Events 部分獲取第一手錯(cuò)誤信息
然后定位根因:根據(jù)事件中的 Reason 和 Message 判斷問(wèn)題類別(鏡像、資源、存儲(chǔ)、網(wǎng)絡(luò)、探針)
最后實(shí)施修復(fù):根據(jù)根因選擇對(duì)應(yīng)的修復(fù)方案,并驗(yàn)證結(jié)果
本文覆蓋了從基礎(chǔ)狀態(tài)診斷到深度節(jié)點(diǎn)級(jí)排障的完整工具鏈。在實(shí)際工作中,建議運(yùn)維團(tuán)隊(duì)將這些腳本整理成工具庫(kù),配合監(jiān)控告警形成完整的故障響應(yīng)體系。
本文基于 Kubernetes 1.32、containerd 2.0、crictl 1.32 環(huán)境編寫,測(cè)試于 Ubuntu 24.04 LTS 和 CentOS Stream 9。
-
容器
+關(guān)注
關(guān)注
0文章
536瀏覽量
23033 -
Docker
+關(guān)注
關(guān)注
0文章
537瀏覽量
14416 -
kubernetes
+關(guān)注
關(guān)注
0文章
275瀏覽量
9538
原文標(biāo)題:Pod 一直起不來(lái),運(yùn)維排障別只會(huì)盯著 Running
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Kubernetes的Device Plugin設(shè)計(jì)解讀
阿里云容器Kubernetes監(jiān)控(二) - 使用Grafana展現(xiàn)Pod監(jiān)控?cái)?shù)據(jù)
從零開始入門 K8s| 詳解 Pod 及容器設(shè)計(jì)模式
電動(dòng)機(jī)故障排障方法
iPod排障秘技!
Kubernetes組件pod核心原理
Kubernetes中的Pod簡(jiǎn)易理解
Kubernetes Pod如何獨(dú)立工作
Kubernetes Pod如何獲取IP地址呢?
Kubernetes Pod啟動(dòng)失敗的各種場(chǎng)景及其排障方法
評(píng)論