Kubernetes故障排查手冊
一、概述
1.1 背景介紹
K8s集群出故障是常態。Pod起不來、Service訪問不通、節點NotReady、證書過期、etcd磁盤滿——每一個問題都可能導致業務中斷。和傳統運維不同,K8s的故障鏈路更長:一個請求從Ingress進來,經過Service、Endpoint、kube-proxy的iptables/IPVS規則,到達Pod,中間任何一環出問題都會導致故障。
這篇手冊整理了生產環境中最常見的K8s故障場景和排查方法,按照"現象→定位→解決"的思路組織。不講理論,直接給排查命令和解決方案。
1.2 技術特點
分層排查思路:K8s故障排查遵循"節點層→控制平面層→工作負載層→網絡層"的分層模型,從底層往上排查效率最高
聲明式系統的排查特點:K8s是聲明式系統,故障往往表現為"期望狀態和實際狀態不一致"。排查的核心是找到哪個控制器沒有正常工作
事件驅動排查:kubectl describe和kubectl get events是最有價值的排查入口,90%的問題能從Events中找到線索
1.3 適用場景
Pod狀態異常:CrashLoopBackOff、ImagePullBackOff、Pending、OOMKilled、Evicted
網絡故障:Service無法訪問、Pod間通信不通、DNS解析失敗、Ingress 502/504
節點故障:NotReady、磁盤壓力、內存壓力、PID壓力
控制平面故障:API Server無響應、etcd故障、調度器異常、證書過期
存儲故障:PVC Pending、掛載失敗、存儲性能問題
1.4 環境要求
| 組件 | 版本要求 | 說明 |
|---|---|---|
| kubectl | 與集群版本匹配 | 排查的核心工具,版本差異不超過1個小版本 |
| crictl | 與容器運行時匹配 | 節點級容器排查工具,替代docker命令 |
| nsenter | 系統自帶 | 進入容器網絡命名空間排查網絡問題 |
| tcpdump | 需要安裝 | 抓包分析網絡故障 |
| etcdctl | 與etcd版本匹配 | etcd故障排查和數據恢復 |
二、詳細步驟
2.1 準備工作
2.1.1 排查工具準備
# 確認kubectl版本和集群連接 kubectl version --short kubectl cluster-info # 安裝kubectl插件(可選但推薦) # krew是kubectl插件管理器 kubectl krew install ctx # 快速切換context kubectl krew install ns # 快速切換namespace kubectl krew install neat # 清理kubectl輸出中的冗余字段 kubectl krew install tree # 以樹形展示資源關系 kubectl krew install node-shell# SSH到節點 # 準備debug鏡像(用于網絡排查) kubectl run debug-pod --image=nicolaka/netshoot --restart=Never -- sleep 3600
2.1.2 快速定位故障范圍
# 第一步:集群整體健康檢查 kubectl get nodes kubectl get cs # 控制平面組件狀態(1.19+已廢棄,用下面的命令) kubectl get --raw='/readyz?verbose' # 第二步:檢查所有namespace中異常的Pod kubectl get pods -A --field-selector status.phase!=Running,status.phase!=Succeeded | head -30 # 第三步:檢查最近的集群事件 kubectl get events -A --sort-by='.lastTimestamp'| tail -30 # 第四步:檢查節點資源壓力 kubectl describe nodes | grep -A 5"Conditions:"
2.2 核心配置
2.2.1 Pod故障排查
場景一:Pod狀態 CrashLoopBackOff
Pod啟動后立即退出,kubelet不斷重啟,重啟間隔指數增長(10s→20s→40s→...→5min)。
# 查看Pod狀態和重啟次數 kubectl get pod-o wide # 查看Pod事件 kubectl describe pod # 關注Events部分和容器的Last State # 查看容器退出日志(最關鍵的一步) kubectl logs --previous # --previous 查看上一次崩潰的日志,不加只能看到當前(可能還沒輸出就崩了) # 如果容器啟動太快來不及看日志,用debug容器 kubectl debug -it --copy-to=debug-pod --container=app -- sh
常見原因和解決方案:
| 退出碼 | 含義 | 常見原因 | 解決方案 |
|---|---|---|---|
| 0 | 正常退出 | 應用執行完就退出了,不是常駐進程 | 檢查Dockerfile的CMD/ENTRYPOINT |
| 1 | 應用錯誤 | 配置文件錯誤、依賴服務不可用 | 查看--previous日志定位具體錯誤 |
| 137 | SIGKILL(OOMKilled) | 內存超過limits被殺 | 調大memory limits或優化應用內存使用 |
| 139 | SIGSEGV | 段錯誤,程序bug | 檢查應用代碼,特別是C/C++/CGO |
| 143 | SIGTERM | 被正常終止 | 檢查是否被liveness probe殺掉 |
# 確認是否OOMKilled kubectl get pod-o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}' # 如果輸出OOMKilled,查看內存使用 kubectl top pod
場景二:Pod狀態 ImagePullBackOff
# 查看具體拉取錯誤 kubectl describe pod| grep -A 10"Events:" # 常見錯誤: # ErrImagePull: 鏡像不存在或tag錯誤 # ImagePullBackOff: 拉取失敗后的退避狀態 # unauthorized: 需要認證但沒配置imagePullSecrets # 檢查鏡像是否存在 crictl pull # 檢查imagePullSecrets配置 kubectl get pod -o jsonpath='{.spec.imagePullSecrets}' kubectl get secret -o jsonpath='{.data..dockerconfigjson}'| base64 -d
解決方案:
鏡像名或tag寫錯 → 修正鏡像地址
私有倉庫未配置認證 → 創建docker-registry類型的Secret并關聯到Pod
網絡不通無法拉取 → 檢查節點到鏡像倉庫的網絡連通性
鏡像倉庫限流(Docker Hub免費賬戶100次/6小時)→ 配置鏡像代理或使用私有倉庫
# 創建鏡像拉取Secret kubectl create secret docker-registry regcred --docker-server=your-registry.com --docker-username=user --docker-password=pass -n
場景三:Pod狀態 Pending
# Pending說明Pod還沒被調度到節點上 kubectl describe pod# 關注Events中的調度失敗原因 # 常見調度失敗原因: # 0/3 nodes are available: 3 Insufficient cpu # → 所有節點CPU不夠 # 0/3 nodes are available: 3 node(s) had taint {key: NoSchedule} # → 所有節點有污點,Pod沒配置容忍 # 0/3 nodes are available: 1 node(s) didn't match Pod's node affinity # → 節點親和性不匹配
# 檢查集群可用資源 kubectl describe nodes | grep -A 8"Allocated resources:" # 檢查節點污點 kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints # 檢查PVC是否綁定(如果Pod掛載了PVC) kubectl get pvc -n# Pending的PVC會導致Pod也Pending
2.2.2 網絡故障排查
場景一:Service無法訪問
# 第一步:確認Service和Endpoint存在 kubectl get svc-n kubectl get endpoints -n # 如果Endpoints為空( ),說明沒有Pod匹配Service的selector # 第二步:確認Service selector和Pod label匹配 kubectl get svc -o jsonpath='{.spec.selector}' kubectl get pods -l = -n # 第三步:確認Pod端口和Service targetPort一致 kubectl get svc -o jsonpath='{.spec.ports}' kubectl get pod -o jsonpath='{.spec.containers[0].ports}' # 第四步:從集群內部測試連通性 kubectl runtest-curl --rm -it --image=curlimages/curl -- curl -v http:// . .svc:port/path
場景二:DNS解析失敗
# 測試DNS解析 kubectl run dns-test --rm -it --image=busybox:1.36 -- nslookup kubernetes.default kubectl run dns-test --rm -it --image=busybox:1.36 -- nslookup. .svc.cluster.local # 檢查CoreDNS是否正常運行 kubectl get pods -n kube-system -l k8s-app=kube-dns kubectl logs -n kube-system -l k8s-app=kube-dns --tail=30 # 檢查CoreDNS的ConfigMap kubectl get configmap coredns -n kube-system -o yaml # 檢查Pod的DNS配置 kubectlexec -- cat /etc/resolv.conf # nameserver應該指向CoreDNS的ClusterIP(通常是10.96.0.10)
常見DNS問題:
CoreDNS Pod掛了 → 重啟CoreDNS:kubectl rollout restart deployment coredns -n kube-system
節點上的resolv.conf有問題導致CoreDNS上游解析失敗 → 檢查CoreDNS的forward配置
ndots配置導致解析慢 → Pod的resolv.conf默認ndots:5,短域名會嘗試5次搜索域拼接后才查外部DNS。可以在Pod spec中設置dnsConfig減小ndots
場景三:Pod間通信不通
# 從源Pod ping目標Pod IP kubectlexec-- ping -c 3 # 如果ping不通,檢查CNI插件狀態 kubectl get pods -n kube-system | grep -E"calico|flannel|cilium|weave" # 檢查節點間網絡 # 在源Pod所在節點上抓包 kubectl debug node/ -it --image=nicolaka/netshoot -- tcpdump -i any host -c 10 # 檢查NetworkPolicy是否阻止了通信 kubectl get networkpolicy -n kubectl describe networkpolicy -n
場景四:Ingress返回502/504
# 502通常是后端Pod不健康或不存在 # 504通常是后端Pod響應超時 # 檢查Ingress配置 kubectl describe ingress-n # 檢查Ingress Controller日志 kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=50 # 檢查后端Service的Endpoints kubectl get endpoints -n # 檢查后端Pod是否Ready kubectl get pods -l -n # 如果Pod Running但不Ready,說明readinessProbe失敗 kubectl describe pod | grep -A 5"Readiness"
2.2.3 節點故障排查
場景一:節點狀態NotReady
# 查看節點狀態和條件 kubectl describe node| grep -A 20"Conditions:" # 常見Condition: # Ready=False: kubelet停止上報心跳 # MemoryPressure=True: 內存不足 # DiskPressure=True: 磁盤不足 # PIDPressure=True: PID耗盡 # NetworkUnavailable=True: 網絡插件未就緒 # SSH到節點檢查kubelet狀態 ssh systemctl status kubelet journalctl -u kubelet --since"10 minutes ago"| tail -50 # 檢查節點資源 free -h df -h ps aux | wc -l
常見原因和解決方案:
| 原因 | 排查命令 | 解決方案 |
|---|---|---|
| kubelet進程掛了 | systemctl status kubelet | systemctl restart kubelet |
| 證書過期 | openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates | kubeadm certs renew all |
| 磁盤滿 | df -h | 清理docker/containerd鏡像緩存:crictl rmi --prune |
| 內存不足 | free -h | 驅逐低優先級Pod或擴容節點 |
| 容器運行時掛了 | systemctl status containerd | systemctl restart containerd |
場景二:節點磁盤壓力(DiskPressure)
# 檢查磁盤使用 df -h /var/lib/kubelet df -h /var/lib/containerd # 查看占用空間最大的目錄 du -sh /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/* | sort -rh | head -10 # 清理未使用的容器鏡像 crictl rmi --prune # 清理已退出的容器 crictl rm $(crictl ps -a --state exited -q) # 清理kubelet日志 journalctl --vacuum-size=500M
2.2.4 控制平面故障排查
場景一:API Server無響應
# 檢查API Server Pod狀態 kubectl get pods -n kube-system -l component=kube-apiserver # 如果kubectl完全無法連接,直接到Master節點操作 sshcrictl ps | grep kube-apiserver crictl logs --tail 50 # 檢查API Server端口是否監聽 ss -tlnp | grep 6443 # 檢查etcd連接 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint health
場景二:證書過期
# 檢查所有證書過期時間 kubeadm certs check-expiration # 輸出示例: # CERTIFICATE EXPIRES RESIDUAL TIME # admin.conf Feb 08, 2027 00:00 UTC 364d # apiserver Feb 08, 2027 00:00 UTC 364d # apiserver-etcd-client Feb 08, 2027 00:00 UTC 364d # ... # 續期所有證書 kubeadm certs renew all # 重啟控制平面組件使新證書生效 # kubeadm部署的集群,靜態Pod會自動重啟 # 手動檢查: crictl ps | grep -E"kube-apiserver|kube-controller|kube-scheduler"
注意:證書續期后需要更新kubeconfig文件。kubeadm certs renew all會自動更新/etc/kubernetes/*.conf,但如果你把kubeconfig復制到了其他地方(比如~/.kube/config),需要手動更新。
2.3 啟動和驗證
2.3.1 故障恢復后驗證清單
# 1. 集群整體狀態
kubectl get nodes
kubectl get pods -A --field-selector status.phase!=Running,status.phase!=Succeeded
# 2. 控制平面組件
kubectl get pods -n kube-system
# 3. 核心服務可用性
kubectl run verify --rm -it --image=busybox:1.36 -- wget -qO- http://kubernetes.default.svc/healthz
# 4. DNS解析
kubectl run verify --rm -it --image=busybox:1.36 -- nslookup kubernetes.default
# 5. 業務服務健康檢查
kubectl get deployments -A | awk'$3 != $4 {print $0}'
# 輸出READY數不等于期望數的Deployment
2.3.2 驗證網絡連通性
# 創建測試Pod驗證跨節點通信
kubectl run net-test-1 --image=nicolaka/netshoot --restart=Never
--overrides='{"spec":{"nodeName":"node-1"}}'-- sleep 3600
kubectl run net-test-2 --image=nicolaka/netshoot --restart=Never
--overrides='{"spec":{"nodeName":"node-2"}}'-- sleep 3600
# 獲取Pod IP
POD1_IP=$(kubectl get pod net-test-1 -o jsonpath='{.status.podIP}')
POD2_IP=$(kubectl get pod net-test-2 -o jsonpath='{.status.podIP}')
# 跨節點ping測試
kubectlexecnet-test-1 -- ping -c 3$POD2_IP
kubectlexecnet-test-2 -- ping -c 3$POD1_IP
# 清理
kubectl delete pod net-test-1 net-test-2
三、示例代碼和配置
3.1 完整配置示例
3.1.1 一鍵集群健康檢查腳本
#!/bin/bash
# 文件名:k8s-health-check.sh
# 功能:全面檢查K8s集群健康狀態,輸出診斷報告
# 用法:./k8s-health-check.sh
RED='?33[0;31m'
YELLOW='?33[1;33m'
GREEN='?33[0;32m'
NC='?33[0m'
echo"============================================"
echo" Kubernetes集群健康檢查報告"
echo" 時間:$(date '+%Y-%m-%d %H:%M:%S')"
echo"============================================"
echo""
# 1. 節點狀態
echo"=== 1. 節點狀態 ==="
NOT_READY=$(kubectl get nodes --no-headers | grep -v" Ready "| wc -l)
TOTAL_NODES=$(kubectl get nodes --no-headers | wc -l)
if["$NOT_READY"-gt 0 ];then
echo-e"${RED}[異常]$NOT_READY/$TOTAL_NODES個節點不健康${NC}"
kubectl get nodes | grep -v" Ready "
else
echo-e"${GREEN}[正常]$TOTAL_NODES/$TOTAL_NODES個節點健康${NC}"
fi
echo""
# 2. 節點資源壓力
echo"=== 2. 節點資源壓力 ==="
kubectl get nodes -o json | jq -r'
.items[] |
.metadata.name as $name |
.status.conditions[] |
select(.type != "Ready" and .status == "True") |
"($name): (.type) = (.status) - (.message)"
'|whileread-r line;do
echo-e"${RED}[壓力]$line${NC}"
done
echo""
# 3. 異常Pod
echo"=== 3. 異常Pod ==="
ABNORMAL=$(kubectl get pods -A --no-headers --field-selector status.phase!=Running,status.phase!=Succeeded 2>/dev/null | wc -l)
if["$ABNORMAL"-gt 0 ];then
echo-e"${YELLOW}[警告]$ABNORMAL個異常Pod${NC}"
kubectl get pods -A --field-selector status.phase!=Running,status.phase!=Succeeded 2>/dev/null | head -20
else
echo-e"${GREEN}[正常] 無異常Pod${NC}"
fi
echo""
# 4. CrashLoopBackOff的Pod
echo"=== 4. CrashLoopBackOff Pod ==="
kubectl get pods -A -o json | jq -r'
.items[] |
select(.status.containerStatuses[]? | select(.state.waiting.reason == "CrashLoopBackOff")) |
[.metadata.namespace, .metadata.name,
(.status.containerStatuses[0].restartCount | tostring)] | @tsv
'|whileIFS=$' 'read-r ns name restarts;do
echo-e"${RED}[崩潰]$ns/$name(重啟${restarts}次)${NC}"
done
echo""
# 5. 高重啟次數Pod(>10次)
echo"=== 5. 高重啟次數Pod(>10次)==="
kubectl get pods -A -o json | jq -r'
.items[] |
select(.status.containerStatuses[]? | select(.restartCount > 10)) |
[.metadata.namespace, .metadata.name,
(.status.containerStatuses[0].restartCount | tostring)] | @tsv
'| sort -t$' '-k3 -rn | head -10 |whileIFS=$' 'read-r ns name restarts;do
echo-e"${YELLOW}[警告]$ns/$name:${restarts}次重啟${NC}"
done
echo""
# 6. Deployment副本數不匹配
echo"=== 6. Deployment副本數異常 ==="
kubectl get deployments -A -o json | jq -r'
.items[] |
select(.status.readyReplicas != .spec.replicas) |
[.metadata.namespace, .metadata.name,
(.status.readyReplicas // 0 | tostring),
(.spec.replicas | tostring)] | @tsv
'|whileIFS=$' 'read-r ns name ready desired;do
echo-e"${YELLOW}[異常]$ns/$name: Ready$ready/$desired${NC}"
done
echo""
# 7. PVC狀態
echo"=== 7. 未綁定的PVC ==="
kubectl get pvc -A --no-headers | grep -v"Bound"|whileread-r line;do
echo-e"${YELLOW}[未綁定]$line${NC}"
done
echo""
# 8. 最近的Warning事件
echo"=== 8. 最近Warning事件(最近30分鐘)==="
kubectl get events -A --field-selectortype=Warning
--sort-by='.lastTimestamp'2>/dev/null | tail -10
echo""
echo"============================================"
echo" 檢查完成"
echo"============================================"
3.1.2 etcd健康檢查和備份腳本
#!/bin/bash # 文件名:etcd-check-backup.sh # 功能:檢查etcd健康狀態并執行快照備份 ETCD_ENDPOINTS="https://127.0.0.1:2379" ETCD_CACERT="/etc/kubernetes/pki/etcd/ca.crt" ETCD_CERT="/etc/kubernetes/pki/etcd/server.crt" ETCD_KEY="/etc/kubernetes/pki/etcd/server.key" BACKUP_DIR="/opt/etcd-backup" ETCDCTL="etcdctl --endpoints=$ETCD_ENDPOINTS --cacert=$ETCD_CACERT--cert=$ETCD_CERT--key=$ETCD_KEY" echo"=== etcd健康檢查 ===" # 端點健康狀態 $ETCDCTLendpoint health echo"" # 端點狀態(包含數據庫大小) $ETCDCTLendpoint status --write-out=table echo"" # 成員列表 $ETCDCTLmember list --write-out=table echo"" # 檢查數據庫大小(超過4GB需要告警) DB_SIZE=$($ETCDCTLendpoint status --write-out=json | jq'.[0].Status.dbSize') DB_SIZE_MB=$((DB_SIZE / 1024 / 1024)) echo"數據庫大小:${DB_SIZE_MB}MB" if["$DB_SIZE_MB"-gt 4000 ];then echo"[警告] etcd數據庫超過4GB,需要壓縮和碎片整理" # 壓縮歷史版本 LATEST_REV=$($ETCDCTLendpoint status --write-out=json | jq'.[0].Status.header.revision') $ETCDCTLcompact"$LATEST_REV" # 碎片整理 $ETCDCTLdefrag fi # 執行快照備份 mkdir -p"$BACKUP_DIR" SNAPSHOT_FILE="$BACKUP_DIR/etcd-snapshot-$(date +%Y%m%d-%H%M%S).db" $ETCDCTLsnapshot save"$SNAPSHOT_FILE" $ETCDCTLsnapshot status"$SNAPSHOT_FILE"--write-out=table echo"備份完成:$SNAPSHOT_FILE"
3.2 輔助腳本
3.2.1 Pod故障快速診斷腳本
#!/bin/bash # 文件名:pod-diagnose.sh # 功能:快速診斷指定Pod的問題 # 用法:./pod-diagnose.shNS=${1:?"用法: $0 "} POD=${2:?"請指定Pod名稱"} echo"========== Pod診斷:$NS/$POD==========" # 基本信息 echo"--- 基本狀態 ---" kubectl get pod"$POD"-n"$NS"-o wide echo"" # 容器狀態 echo"--- 容器狀態 ---" kubectl get pod"$POD"-n"$NS"-o json | jq -r' .status.containerStatuses[]? | "容器: (.name) | Ready: (.ready) | 重啟: (.restartCount) | 狀態: (.state | keys[0])" ' echo"" # 事件 echo"--- 相關事件 ---" kubectl get events -n"$NS"--field-selector"involvedObject.name=$POD" --sort-by='.lastTimestamp'| tail -10 echo"" # 如果有上次崩潰日志 RESTART_COUNT=$(kubectl get pod"$POD"-n"$NS"-o jsonpath='{.status.containerStatuses[0].restartCount}') if["$RESTART_COUNT"-gt 0 ];then echo"--- 上次崩潰日志(最后30行)---" kubectl logs"$POD"-n"$NS"--previous --tail=30 2>/dev/null ||echo"無法獲取上次日志" echo"" fi # 資源使用 echo"--- 資源使用 ---" kubectl top pod"$POD"-n"$NS"2>/dev/null ||echo"Metrics不可用" echo"" # 資源配置 echo"--- 資源配置 ---" kubectl get pod"$POD"-n"$NS"-o json | jq'.spec.containers[] | {name, resources}' echo"" echo"========== 診斷完成 =========="
3.2.2 節點資源排查腳本
#!/bin/bash
# 文件名:node-resource-check.sh
# 功能:檢查節點資源分配和使用情況
# 用法:./node-resource-check.sh [node-name]
NODE=${1:-""}
if[ -z"$NODE"];then
NODES=$(kubectl get nodes -o jsonpath='{.items[*].metadata.name}')
else
NODES="$NODE"
fi
fornin$NODES;do
echo"========== 節點:$n=========="
# 節點狀態
STATUS=$(kubectl get node"$n"-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')
echo"狀態: Ready=$STATUS"
# 資源容量和已分配
echo""
echo"--- 資源分配 ---"
kubectl describe node"$n"| sed -n'/Allocated resources/,/Events/p'| head -15
# 該節點上的Pod數量
POD_COUNT=$(kubectl get pods -A --field-selector"spec.nodeName=$n"--no-headers | wc -l)
MAX_PODS=$(kubectl get node"$n"-o jsonpath='{.status.capacity.pods}')
echo""
echo"Pod數量:$POD_COUNT/$MAX_PODS"
# 該節點上資源使用Top 5的Pod
echo""
echo"--- CPU使用Top 5 ---"
kubectl top pods -A --sort-by=cpu --no-headers |
whileread-r ns name cpu mem;do
pod_node=$(kubectl get pod"$name"-n"$ns"-o jsonpath='{.spec.nodeName}'2>/dev/null)
if["$pod_node"="$n"];then
echo" $ns/$name: CPU=$cpuMEM=$mem"
fi
done| head -5
echo""
done
3.3 實際應用案例
案例一:生產環境大規模Pod驅逐事件排查
場景描述:周一早上收到告警,production namespace中30%的Pod被驅逐(Evicted),業務出現大面積503。
排查過程:
# 1. 確認驅逐規模 kubectl get pods -n production --field-selector status.phase=Failed | grep Evicted | wc -l # 輸出:47 # 2. 查看驅逐原因 kubectl get pods -n production --field-selector status.phase=Failed -o json | jq -r'.items[] | select(.status.reason=="Evicted") | .status.message'| sort | uniq -c # 輸出:47 The node was low on resource: ephemeral-storage. # 3. 定位問題節點 kubectl get pods -n production --field-selector status.phase=Failed -o json | jq -r'.items[] | select(.status.reason=="Evicted") | .spec.nodeName'| sort | uniq -c # 輸出:47 node-3 # 4. 檢查node-3的磁盤 ssh node-3 df -h # /dev/sda1 100G 97G 3G 97% / # 5. 找到磁盤占用元兇 ssh node-3 du -sh /var/log/* | sort -rh | head -5 # 78G /var/log/pods/production_data-processor-xxx/app/0.log
根因:data-processor應用沒有配置日志輪轉,單個容器日志文件漲到78G,撐滿了節點磁盤。kubelet檢測到ephemeral-storage壓力,開始驅逐該節點上的Pod。
解決方案:
# 1. 清理Evicted Pod kubectl delete pods -n production --field-selector status.phase=Failed # 2. 配置kubelet日志輪轉(在每個節點上) # /var/lib/kubelet/config.yaml 中添加: # containerLogMaxSize: "100Mi" # containerLogMaxFiles: 5 # 3. 給應用Pod配置ephemeral-storage限制 # 防止單個Pod占滿節點磁盤
resources: requests: ephemeral-storage:1Gi limits: ephemeral-storage:5Gi
案例二:間歇性DNS解析失敗排查
場景描述:業務反饋部分請求報"Name or service not known"錯誤,但不是每次都失敗,大約5%的請求會DNS解析失敗。
排查過程:
# 1. 確認CoreDNS運行狀態 kubectl get pods -n kube-system -l k8s-app=kube-dns # 2個Pod都Running # 2. 檢查CoreDNS日志 kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100 | grep -i"error|fail" # 沒有明顯錯誤 # 3. 檢查CoreDNS的資源使用 kubectl top pods -n kube-system -l k8s-app=kube-dns # NAME CPU MEMORY # coredns-xxx-aaa 980m 178Mi # coredns-xxx-bbb 950m 175Mi # CPU接近limits(1核),說明CoreDNS過載 # 4. 檢查DNS查詢量 kubectlexec-n kube-system coredns-xxx-aaa -- wget -qO- http://localhost:9153/metrics | grep coredns_dns_requests_total # 每秒超過5000次查詢
根因:集群規模擴大后DNS查詢量超過CoreDNS的處理能力,2個Pod各1核CPU已經跑滿。
解決方案:
# 1. 擴容CoreDNS kubectl scale deployment coredns -n kube-system --replicas=5 # 2. 調大CoreDNS資源 kubectl edit deployment coredns -n kube-system # 將CPU limits從1調到2 # 3. 啟用NodeLocal DNSCache減少CoreDNS壓力 kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml # NodeLocal DNSCache在每個節點運行DNS緩存,大部分查詢在本地命中,不需要走CoreDNS
四、最佳實踐和注意事項
4.1 最佳實踐
4.1.1 性能優化
配置合理的資源requests和limits:requests設太低會導致Pod被調度到資源不足的節點,運行時性能差;limits設太低會導致CPU被throttle或內存OOMKill。生產環境建議requests設為實際使用量的80%,limits設為requests的1.5-2倍。
# 查看Pod實際資源使用,作為設置requests的參考 kubectl top pods -n production --sort-by=cpu kubectl top pods -n production --sort-by=memory
配置PodDisruptionBudget:防止節點維護或集群升級時同時驅逐太多Pod導致服務中斷。
apiVersion:policy/v1 kind:PodDisruptionBudget metadata: name:web-app-pdb namespace:production spec: minAvailable:"60%" selector: matchLabels: app:web-app
配置優雅終止時間:默認terminationGracePeriodSeconds是30秒。Java應用可能需要更長時間完成請求處理和資源釋放,建議設為60-120秒。應用代碼中要正確處理SIGTERM信號。
4.1.2 安全加固
定期檢查證書過期時間:kubeadm簽發的證書默認1年有效期,過期后API Server直接不可用。配置定時任務每周檢查一次。
# 加入crontab,每周一檢查證書 0 9 * * 1 kubeadm certs check-expiration | mail -s"K8s證書檢查"sre@company.com
etcd定期備份:etcd存儲了集群所有狀態數據,丟了就全完了。每天至少備份一次,保留最近7天的快照。
# crontab每天凌晨2點備份 0 2 * * * /opt/scripts/etcd-check-backup.sh >> /var/log/etcd-backup.log 2>&1 # 清理7天前的備份 0 3 * * * find /opt/etcd-backup -name"*.db"-mtime +7 -delete
限制kubectl權限:不要給所有人cluster-admin權限。按團隊和職責分配RBAC角色,開發只能查看自己namespace的資源,SRE有更高權限但也要審計。
4.1.3 高可用配置
控制平面多節點:生產環境至少3個Master節點,API Server、etcd、Controller Manager、Scheduler都是多副本。單Master掛了整個集群不可管理。
跨可用區部署:Worker節點分布在至少2個可用區,配合Pod Topology Spread Constraints確保Pod分散在不同AZ。
關鍵組件監控:etcd磁盤延遲、API Server請求延遲、Controller Manager隊列深度——這三個指標異常通常是集群故障的前兆。
4.2 注意事項
4.2.1 配置注意事項
警告:以下操作在生產環境中執行前務必確認影響范圍。
不要隨意刪除kube-system中的Pod:kube-proxy、CoreDNS、CNI插件等核心組件在kube-system中,誤刪會導致集群網絡中斷。
不要在高峰期做節點維護:kubectl drain會驅逐節點上所有Pod,高峰期驅逐可能導致其他節點資源不足,引發連鎖故障。
kubectl delete pod和kubectl delete deployment的區別:delete pod只刪一個Pod(Deployment會自動重建),delete deployment會刪除所有Pod且不會重建。緊急情況下重啟服務用kubectl rollout restart deployment,不要delete。
4.2.2 常見錯誤
| 錯誤現象 | 原因分析 | 解決方案 |
|---|---|---|
| kubectl超時無響應 | API Server過載或網絡不通 | 檢查API Server Pod狀態和節點網絡 |
| Pod一直Terminating | finalizer阻塞或容器進程不響應SIGTERM | kubectl delete pod --force --grace-period=0 強制刪除 |
| 節點NotReady后Pod不遷移 | 默認等待5分鐘(pod-eviction-timeout)才開始遷移 | 檢查kube-controller-manager的配置 |
| HPA不工作 | Metrics Server未安裝或Pod沒設requests | 安裝Metrics Server,配置resources.requests |
| PVC一直Pending | StorageClass不存在或存儲后端故障 | kubectl describe pvc 查看事件 |
| Service ClusterIP不通 | kube-proxy未運行或iptables規則異常 | 檢查kube-proxy Pod和iptables規則 |
4.2.3 兼容性問題
kubectl版本:kubectl和集群版本差異不能超過1個小版本。用1.25的kubectl管理1.28的集群可能出現API不兼容。
容器運行時:K8s 1.24+移除了dockershim,必須用containerd或CRI-O。升級到1.24前確認運行時已切換。
API廢棄:每個K8s版本都會廢棄一些API(如extensions/v1beta1 Ingress)。升級前用kubectl convert或pluto工具檢查廢棄API。
五、故障排查和監控
5.1 故障排查
5.1.1 日志查看
# kubelet日志(節點級別) journalctl -u kubelet --since"30 minutes ago"| tail -100 # API Server日志 kubectl logs -n kube-system -l component=kube-apiserver --tail=50 # Controller Manager日志 kubectl logs -n kube-system -l component=kube-controller-manager --tail=50 # Scheduler日志 kubectl logs -n kube-system -l component=kube-scheduler --tail=50 # etcd日志 kubectl logs -n kube-system -l component=etcd --tail=50 # CoreDNS日志 kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50 # kube-proxy日志 kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=50
5.1.2 常見問題排查
問題一:API Server響應慢,kubectl操作延遲高
# 檢查API Server請求延遲 kubectl get --raw /metrics | grep apiserver_request_duration_seconds # 檢查etcd延遲(API Server依賴etcd) kubectlexec-n kube-system etcd-master01 -- etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint status --write-out=table # 檢查API Server審計日志中的慢請求 grep"AUDIT"/var/log/kubernetes/audit.log | jq'select(.stageTimestamp > .requestReceivedTimestamp + "5s")'
解決方案:
etcd磁盤IO慢 → 將etcd數據目錄遷移到SSD
API Server過載 → 檢查是否有大量LIST請求(通常是監控組件配置不當)
大量Webhook調用 → 檢查MutatingWebhookConfiguration和ValidatingWebhookConfiguration
問題二:Pod被OOMKilled反復重啟
# 確認OOMKill kubectl get pod-o jsonpath='{.status.containerStatuses[0].lastState.terminated}' # 查看Pod內存使用趨勢 kubectl top pod --containers # 查看節點上的OOM事件 ssh dmesg | grep -i"oom|killed"| tail -10
解決方案:
調大memory limits(臨時方案)
排查內存泄漏(根本方案):Java應用用jmap dump堆內存分析,Go應用用pprof
如果是JVM應用,確認-Xmx設置小于容器memory limits的80%,留20%給非堆內存
問題三:Service的Endpoints頻繁變化導致流量抖動
癥狀:服務間調用間歇性失敗,Endpoints列表頻繁增減
排查:
# 監控Endpoints變化 kubectl get endpoints-w # 檢查Pod的readinessProbe是否頻繁失敗 kubectl describe pod | grep -A 10"Readiness" kubectl get events --field-selector reason=Unhealthy -n
解決:調整readinessProbe的參數,增大failureThreshold和periodSeconds,避免短暫的響應慢就被摘除
5.1.3 調試模式
# 使用ephemeral container調試運行中的Pod(不重啟Pod) kubectl debug-it --image=nicolaka/netshoot --target= # 復制Pod進行調試(不影響原Pod) kubectl debug -it --copy-to=debug-copy --container=app -- sh # 調試節點(在節點上啟動特權Pod) kubectl debug node/ -it --image=ubuntu # 查看Pod的完整YAML(包含status) kubectl get pod -o yaml # 查看資源的所有事件 kubectl get events --field-selector involvedObject.name= --sort-by='.lastTimestamp'
5.2 性能監控
5.2.1 關鍵指標監控
# 集群整體資源使用率 kubectl top nodes # 各namespace資源使用匯總 kubectl top pods -A --sort-by=cpu | head -20 kubectl top pods -A --sort-by=memory | head -20 # API Server請求延遲 kubectl get --raw /metrics | grep apiserver_request_duration_seconds_bucket # etcd數據庫大小 kubectlexec-n kube-system etcd-master01 -- etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint status --write-out=json | jq'.[0].Status.dbSize / 1024 / 1024'
5.2.2 監控指標說明
| 指標名稱 | 正常范圍 | 告警閾值 | 說明 |
|---|---|---|---|
| 節點CPU使用率 | < 70% | > 85% 持續5分鐘 | 過高會導致Pod被throttle |
| 節點內存使用率 | < 80% | > 90% | 接近100%會觸發OOM Killer |
| etcd數據庫大小 | < 2GB | > 4GB | 超過8GB etcd會拒絕寫入 |
| etcd磁盤fsync延遲 | < 10ms | > 25ms | 延遲過高影響集群所有寫操作 |
| API Server請求延遲P99 | < 1s | > 5s | 延遲過高kubectl操作會超時 |
| Pod重啟次數 | 0 | > 5次/小時 | 頻繁重啟說明應用有問題 |
| CoreDNS請求延遲 | < 5ms | > 50ms | DNS慢會影響所有服務間調用 |
5.2.3 監控告警配置
# Prometheus告警規則:k8s-cluster-alerts.yaml
apiVersion:monitoring.coreos.com/v1
kind:PrometheusRule
metadata:
name:k8s-cluster-alerts
namespace:monitoring
spec:
groups:
-name:k8s-node.rules
rules:
-alert:NodeNotReady
expr:kube_node_status_condition{condition="Ready",status="true"}==0
for:5m
labels:
severity:critical
annotations:
summary:"節點{{ $labels.node }}NotReady超過5分鐘"
-alert:NodeHighCPU
expr:|
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for:5m
labels:
severity:warning
annotations:
summary:"節點{{ $labels.instance }}CPU使用率超過85%"
-alert:NodeHighMemory
expr:|
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
for:5m
labels:
severity:critical
annotations:
summary:"節點{{ $labels.instance }}內存使用率超過90%"
-alert:NodeDiskPressure
expr:|
(1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 > 85
for:5m
labels:
severity:warning
annotations:
summary:"節點{{ $labels.instance }}磁盤使用率超過85%"
-name:k8s-pod.rules
rules:
-alert:PodCrashLooping
expr:|
rate(kube_pod_container_status_restarts_total[15m]) * 60 * 15 > 0
for:5m
labels:
severity:critical
annotations:
summary:"Pod{{ $labels.namespace }}/{{ $labels.pod }}頻繁重啟"
-alert:PodOOMKilled
expr:|
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1
for:0m
labels:
severity:warning
annotations:
summary:"Pod{{ $labels.namespace }}/{{ $labels.pod }}被OOMKill"
-name:k8s-etcd.rules
rules:
-alert:EtcdHighDiskLatency
expr:|
histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])) > 0.025
for:5m
labels:
severity:critical
annotations:
summary:"etcd磁盤fsync P99延遲超過25ms,集群寫入性能受影響"
-alert:EtcdDatabaseSizeLarge
expr:etcd_mvcc_db_total_size_in_bytes>4294967296
for:5m
labels:
severity:warning
annotations:
summary:"etcd數據庫大小超過4GB,需要壓縮和碎片整理"
5.3 備份與恢復
5.3.1 備份策略
#!/bin/bash
# 文件名:etcd-daily-backup.sh
# 功能:etcd每日快照備份,保留7天
BACKUP_DIR="/opt/etcd-backup"
ETCD_ENDPOINTS="https://127.0.0.1:2379"
ETCD_CACERT="/etc/kubernetes/pki/etcd/ca.crt"
ETCD_CERT="/etc/kubernetes/pki/etcd/server.crt"
ETCD_KEY="/etc/kubernetes/pki/etcd/server.key"
mkdir -p"$BACKUP_DIR"
SNAPSHOT="$BACKUP_DIR/etcd-$(date +%Y%m%d-%H%M%S).db"
etcdctl snapshot save"$SNAPSHOT"
--endpoints="$ETCD_ENDPOINTS"
--cacert="$ETCD_CACERT"
--cert="$ETCD_CERT"
--key="$ETCD_KEY"
if[ $? -eq 0 ];then
echo"備份成功:$SNAPSHOT($(du -sh "$SNAPSHOT" | awk '{print $1}'))"
# 清理7天前的備份
find"$BACKUP_DIR"-name"etcd-*.db"-mtime +7 -delete
else
echo"備份失敗!">&2
exit1
fi
5.3.2 恢復流程
# etcd從快照恢復(集群完全不可用時的最后手段) # 警告:恢復會丟失快照之后的所有變更 # 1. 停止所有Master節點的API Server # 移走靜態Pod manifest mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ # 2. 停止etcd mv /etc/kubernetes/manifests/etcd.yaml /tmp/ # 3. 備份當前etcd數據目錄 mv /var/lib/etcd /var/lib/etcd.bak # 4. 從快照恢復 etcdctl snapshot restore /opt/etcd-backup/etcd-20260208.db --data-dir=/var/lib/etcd --name=master01 --initial-cluster=master01=https://10.0.0.1:2380 --initial-advertise-peer-urls=https://10.0.0.1:2380 # 5. 恢復etcd和API Server mv /tmp/etcd.yaml /etc/kubernetes/manifests/ mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/ # 6. 等待集群恢復 sleep 30 kubectl get nodes kubectl get pods -A
六、總結
6.1 技術要點回顧
排查思路分層:節點層(kubelet、磁盤、內存)→ 控制平面層(API Server、etcd、Scheduler)→ 工作負載層(Pod狀態、容器日志)→ 網絡層(Service、DNS、CNI),從底層往上排查效率最高
kubectl describe是第一步:90%的問題能從Events中找到線索。kubectl describe pod/node/svc是排查的起點
--previous查看崩潰日志:CrashLoopBackOff的Pod用kubectl logs --previous查看上一次崩潰的日志,不加這個參數看到的可能是空的
etcd是集群的命脈:etcd數據丟失等于集群報廢。每天備份,定期驗證恢復流程
6.2 進階學習方向
kubectl debug深入使用:Ephemeral Container可以在不重啟Pod的情況下注入調試工具,K8s 1.25+ GA
文檔:https://kubernetes.io/docs/tasks/debug/debug-application/debug-running-pod/
實踐建議:準備一個包含常用排查工具的debug鏡像(netshoot、busybox等)
混沌工程:用Chaos Mesh或Litmus在測試環境主動注入故障,提前發現系統弱點
項目地址:https://chaos-mesh.org/
實踐建議:從簡單的Pod Kill開始,逐步增加網絡延遲、磁盤故障等場景
可觀測性體系建設:Prometheus + Grafana + Loki + Tempo,構建指標、日志、鏈路追蹤三位一體的可觀測性平臺
實踐建議:先搞定指標監控和告警,再補充日志和鏈路追蹤
6.3 參考資料
Kubernetes官方排查文檔- 官方故障排查指南
kubectl Cheat Sheet- kubectl命令速查
learnk8s故障排查流程圖- 可視化排查決策樹
etcd運維文檔- etcd備份恢復和調優
附錄
A. 命令速查表
# Pod排查 kubectl get pods -A --field-selector status.phase!=Running,status.phase!=Succeeded # 異常Pod kubectl describe pod# Pod詳情和事件 kubectl logs --previous --tail=50 # 上次崩潰日志 kubectl logs -c # 指定容器日志 kubectl top pod --containers # Pod資源使用 kubectl debug -it --image=netshoot # 調試Pod kubectl delete pod --force --grace-period=0 # 強制刪除卡住的Pod # 節點排查 kubectl describe node # 節點詳情 kubectl top nodes # 節點資源使用 kubectl get nodes -o wide # 節點IP和版本 kubectl cordon # 標記節點不可調度 kubectl drain --ignore-daemonsets # 驅逐節點上的Pod kubectl uncordon # 恢復節點調度 # 網絡排查 kubectl get svc,endpoints -n # Service和Endpoint kubectl runtest--rm -it --image=busybox -- nslookup # DNS測試 kubectl runtest--rm -it --image=curlimages/curl -- curl # HTTP測試 # 事件和日志 kubectl get events -A --sort-by='.lastTimestamp'| tail -20 # 最近事件 kubectl get events --field-selectortype=Warning -A # Warning事件 journalctl -u kubelet --since"30m ago" # kubelet日志 # etcd etcdctl endpoint health # 健康檢查 etcdctl endpoint status --write-out=table # 狀態詳情 etcdctl snapshot save # 快照備份 etcdctl snapshot restore # 快照恢復
B. 配置參數詳解
Pod狀態含義:
| 狀態 | 含義 | 排查方向 |
|---|---|---|
| Pending | 等待調度 | 資源不足、污點、親和性、PVC未綁定 |
| ContainerCreating | 容器創建中 | 鏡像拉取、Volume掛載、Init容器 |
| Running | 運行中 | 正常狀態,但可能不Ready |
| CrashLoopBackOff | 反復崩潰 | 查看--previous日志,檢查退出碼 |
| ImagePullBackOff | 鏡像拉取失敗 | 鏡像地址、認證、網絡 |
| OOMKilled | 內存超限被殺 | 調大limits或排查內存泄漏 |
| Evicted | 被驅逐 | 節點資源壓力(磁盤、內存) |
| Terminating | 終止中 | finalizer阻塞或進程不響應SIGTERM |
| Unknown | 狀態未知 | 節點失聯,kubelet無法上報 |
容器退出碼含義:
| 退出碼 | 信號 | 含義 |
|---|---|---|
| 0 | - | 正常退出 |
| 1 | - | 應用錯誤 |
| 126 | - | 命令無法執行(權限問題) |
| 127 | - | 命令未找到 |
| 128+N | Signal N | 被信號N終止 |
| 137 | SIGKILL(9) | 被強制殺死(OOMKill或kill -9) |
| 139 | SIGSEGV(11) | 段錯誤 |
| 143 | SIGTERM(15) | 被正常終止 |
C. 術語表
| 術語 | 英文 | 解釋 |
|---|---|---|
| 控制平面 | Control Plane | K8s集群的管理層,包含API Server、etcd、Scheduler、Controller Manager |
| 數據平面 | Data Plane | K8s集群的工作層,即Worker節點上運行的kubelet、kube-proxy和業務Pod |
| 驅逐 | Eviction | kubelet在節點資源壓力下主動終止Pod的行為 |
| 污點 | Taint | 節點上的標記,阻止不容忍該污點的Pod被調度到該節點 |
| 容忍 | Toleration | Pod上的配置,允許Pod被調度到有特定污點的節點 |
| 親和性 | Affinity | Pod調度時對節點或其他Pod的偏好或要求 |
| 探針 | Probe | kubelet用于檢測容器健康狀態的機制:liveness、readiness、startup |
| 臨時容器 | Ephemeral Container | 用于調試的臨時容器,可以注入到運行中的Pod而不重啟 |
-
磁盤
+關注
關注
1文章
398瀏覽量
26470 -
kubernetes
+關注
關注
0文章
263瀏覽量
9494
原文標題:線上 K8s 又炸了?這份故障排查手冊請收好
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
Kubernetes故障排查手冊
評論