運維人的日常之一次 K8s 磁盤故障的驚魂夜
事件起因
23年3月的某個該死的一天,而且是該死的凌晨兩點,值班人員忽然來電話,公司主力產品的某大型在線對戰游戲部分玩家在拍賣行交易時,界面一直停在“提交中”,頁面卡死,整個流程卡住了。”好!知道了~“,迷迷糊糊中拿起手機,眼皮還沒完全睜開,就看到滿屏的告警,Prometheus那邊已經炸了——trade-service的錯誤率飆到了85%以上。沒辦法拿人錢財與人消災,操練起來吧。
廢話少說直接來干貨!先聲明這個K8s集群的版本是v1.24.8。
排查過程
先從集群角度看看都什么情況!
kubectl get nodes -o wide 顯示 worker-node-7 狀態為 DiskPressure該節點運行著交易服務的核心事務處理的Pod。
執行驅逐檢查:
kubectl describe node worker-node-7 | grep -i evicted # 發現12個Pod因磁盤壓力被驅逐
Elastic Stack 日志分析發現連續錯誤:[FATAL] Transaction commit failed: Input/output error
再從問題節點上看看什么情況。
SSH登錄worker-node-7隨手就是一個技能:
df -hT /var/lib/kubelet # 顯示使用率100%
嚯!~接下來必須看看是哪個或者哪些大文件導致的?:
ncdu -x /var/lib/docker/overlay2 # 發現某容器日志文件占用52GB
那我刪?別忙,別動!老運維的直覺,感覺有埋伏.那我必須再一個技能
sudo smartctl -a /dev/nvme0n1 | grep -e 'Media_Wearout_Indicator' -e 'Reallocated_Sector_Ct'
Reallocated_Sector_Ct 0x0033 086 086 000 Pre-fail Always FAILING_NOW 142
我勒個去.迷糊不?子母雷呀!磁盤有142個壞道。
這時候不能處理奧!這時候就想起了IT運維技術圈的博主老楊對我的諄諄教誨:“不要看到一個毛病就下手,一定要盡量的查,全查明白了,然后再反饋,然后再按照領導的決定去執行”記得前一陣子剛升級了k8s集群版本,咋就這么巧出了問題呢?我再仔細摸一下集群的配置.那邊總監已經開始在群里各種哀嚎了.不過一定要穩住,穩住!
我再確認一下集群的配置:
ps aux | grep kubelet | grep -o 'eviction-hard=.*' # 輸出'imagefs.available<10%,nodefs.available<5%'
第三顆雷被我找到了.查了一下K8s v1.24.8的新特性,默認是禁用SMART的
journalctl -u kubelet | grep 'eviction_manager'
顯示日志輪轉失效警告
到這可以做階段總結了:
- 硬件老化失效:NVMe SSD出現嚴重壞道,I/O故障直接影響服務。
- K8s存儲感知缺失:集群未啟用本地存儲容量隔離特性,無法及時預警磁盤異常。
- 資源限額與日志治理缺陷:Pod未設定存儲限額,日志文件無輪轉,導致磁盤空間被單一容器異常占用。
行了,該總監上場了,把自己看到的和認為合理的處理辦法告訴它,得到了“汪汪“的回復,并且給出了“汪汪”的建議,以及“汪汪”的指導后,開始操作。
一頓反搖拳
干掉worker-node-7節點,但一定要優雅。
標記節點不可調度:
kubectl taint nodes worker-node-7 diskfailure=true:NoSchedule
驅逐Pod:
kubectl drain worker-node-7 --ignore-daemonsets --grace-period=300
臨時用badblocks和e2fsck標記壞塊,強行讓文件系統跳過這些坑bash badblocks -sv /dev/nvme0n1 # 標記壞塊 e2fsck -c /dev/nvme0n1 # 強制文件系統跳過壞道。
日志全清空:
truncate -s 0 /var/lib/docker/containers/*/*-json.log
直接用ArgoCD把trade-service重新部署了一遍。
使用 ArgoCD 觸發自動重部署:
argocd app actions run trade-service --kind Rollout --action restart
數據庫那邊也沒敢大意,特地跑了個一致性校驗:
kubectl exec trade-db-0 -- pg_checkconsistency -v
等到凌晨四點半,監控大屏上的交易成功率終于回升,玩家投訴也慢慢消停了。第二天直接休了一天!~
復盤
復盤會上沒什么好撕的,根因就上面那些!做了下面的幾個方向的改進。
寫了個磁盤檢查腳本(羅列一下大概意思,不喜勿噴)disk_health_monitor.sh:
#!/bin/bash
# 設備自動發現(兼容NVMe/SATA)
DEVICES=$(lsblk -d -o NAME,TYPE | grep disk | awk '{print "/dev/"$1}')
ALERT_FLAG=0
for DEV in$DEVICES; do
# SMART健康檢查(帶重試機制)
for i in {1..3}; do
SMART_REPORT=$(smartctl -H $DEV 2>/dev/null)
[ $? -eq 0 ] && break || sleep 5
done
# 關鍵參數解析
REALLOC=$(smartctl -A $DEV | grep 'Reallocated_Sector_Ct' | awk '{print $10}')
WEAR_LEVEL=$(smartctl -A $DEV | grep 'Wear_Leveling_Count' | awk '{print $4}' | tr -d '%')
# 多維度健康評估
ifecho$SMART_REPORT | grep -q "FAILED"; then
logger "[DISK CRITICAL] $DEV SMART failed!"
ALERT_FLAG=1
elif [ $REALLOC -gt 50 ]; then
logger "[DISK WARNING] $DEV Realloc sectors: $REALLOC"
ALERT_FLAG=1
elif [ $WEAR_LEVEL -lt 10 ]; then
logger "[DISK WARNING] $DEV Wear level: $WEAR_LEVEL%"
ALERT_FLAG=1
fi
done
# 聯動K8s節點標記
if [ $ALERT_FLAG -eq 1 ]; then
NODE_NAME=$(hostname)
kubectl label nodes $NODE_NAME disk-status=critical --overwrite
curl -X POST -H "Content-Type: application/json" -d '{"text":"磁盤健康告警!"}'$WEBHOOK_URL
fi
(1) 修改k8s集群配置
啟用特性門控(/etc/kubernetes/kubelet.conf):
featureGates:
LocalStorageCapacityIsolation:true
部署存儲限額策略:
apiVersion:scheduling.k8s.io/v1
kind:PriorityClass
metadata:
name:high-storage
value:1000000
ephemeral-storage-limit:5Gi # 每個Pod限制5GB臨時存儲
(2) 日志生態系統重構
部署 Loki+Promtail 替代Docker原生日志:
helm upgrade --install loki grafana/loki-stack --set promtail.enabled=true
添加日志自動清理CronJob:
apiVersion:batch/v1
kind:CronJob
spec:
schedule:"0 */4 * * *"
jobTemplate:
spec:
containers:
-name:log-cleaner
image:alpine:3.14
command: ["find", "/var/log/containers", "-size +500M", "-delete"]
最終狀態
后續研發通過 Redis事務補償機制 自動修復2,317筆中斷交易。叉會腰,這次可給我牛批壞了!