K8s CrashLoopBackOff 如何排障?
整理 CrashLoopBackOff 排故相關(guān)筆記分享給小伙伴。
博文內(nèi)容涉及:
什么是 CrashLoopBackOff ?
如何對 CrashLoopBackOff 排故?
理解不足小伙伴幫忙指正
「 中秋明月,豪門有,貧家也有。極慰人心。——烽火戲諸侯《劍來》」
什么是 CrashLoopBackOff
CrashLoopBackOff 是在 k8s 中較常見的一種 Pod 異常狀態(tài),最直接的表述,集群中的 Pod 在不斷的重啟掛掉,一直循環(huán),往往 Pod 運行幾秒鐘 因為程序異常會直接死掉,沒有常駐進程,但是 容器運行時 會根據(jù) Pod 的重啟策略(默認為:always)一直的重啟它,所以會 CrashLoopBackOff
pod的重啟策略 restartpolicy:pod在遇到故障之后重啟的動作:
always:當容器退出時,總是重啟容器,默認策略
onfailure:當容器異常退出(退出狀態(tài)碼非0)時,重啟容器
nerver:當容器退出時,從不重啟容器
復(fù)現(xiàn)很容易,可以簡單的啟動一個 busybox 容器,sleep 一會,exit 指定異常退出狀態(tài)碼
apiVersion: v1
kind: Pod
metadata:
name: crashlookbackoff-pod
spec:
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep 5;exit 3
安裝了 Istio ,所以自動注入了初始化容器istio-init 和容器代理 istio-proxy
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl apply -f crashlookbackoff_pod.yaml
pod/crashlookbackoff-pod created
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl get pods crashlookbackoff-pod -w
NAME READY STATUS RESTARTS AGE
crashlookbackoff-pod 0/2 Init:0/1 0 10s
crashlookbackoff-pod 0/2 PodInitializing 0 12s
crashlookbackoff-pod 1/2 Running 0 30s
crashlookbackoff-pod 0/2 Error 0 38s
crashlookbackoff-pod 1/2 Error 0 39s
crashlookbackoff-pod 2/2 Running 1 (21s ago) 55s
crashlookbackoff-pod 1/2 Error 1 (27s ago) 61s
crashlookbackoff-pod 1/2 CrashLoopBackOff 1 (11s ago) 71s
crashlookbackoff-pod 2/2 Running 2 (13s ago) 73s
crashlookbackoff-pod 1/2 Error 2 (18s ago) 78s
crashlookbackoff-pod 1/2 CrashLoopBackOff 2 (17s ago) 94s
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$
當然,這是 會觸發(fā) CrashLoopBackOff 的其中一種原因,實際生產(chǎn)中還有很多其他場景
Pod 內(nèi)容器化應(yīng)用程序異常退出
在最上面的問題復(fù)現(xiàn)即是這種情況,容器中的進程因為內(nèi)部原因掛掉,對于這種情況,需要根據(jù) Pod 日志信息來排查問題,對代碼中的問題進行處理。也可以通過 kubectl describe pods 來定位問題的原因
程序本身有 Bug,導(dǎo)致常駐進程掛掉
需要加載的配置信息,需要獲取的其他信息沒有加載到,或者是設(shè)置錯誤 ,程序報錯掛掉,獲取無法正常啟動
容器需要的 pod 內(nèi)部資源被占用 比如端口,文件系統(tǒng)等 ,文件系統(tǒng)只讀,一個 pod 的兩個容器同時監(jiān)聽一個端口
網(wǎng)絡(luò)方面,無法連接或者訪問某個服務(wù),程序超時掛掉,或者在微服務(wù)中依賴的相關(guān)服務(wù)沒有起來或者掛掉。
健康檢查配置異常
Kubernetes 對 Pod 的健康狀態(tài)可以通過兩類探針來檢查: LivenessProbe和 ReadinessProbe,檢測方式都有三種(exec/TCP/HTTP) 這里我們只考慮第一種LivenessProbe,LivenessProbe 用于檢查服務(wù)是否存活 在返回失敗后 kubelet 會殺掉容器,根據(jù)重啟策略重啟啟動一個,ReadinessProbe用于檢測 pod 對應(yīng)的服務(wù)是否會提供能力,當返回失敗的會把對應(yīng)的 endpoint 從 Service 負載列表中移除。
在 LivenessProbe 中如果配置錯誤,那么探針返回的永遠都是失敗,那么 kubelet 會認為 pod 已經(jīng)死掉,會殺掉當前進程,重啟一個新的 pod,以此循環(huán),觸發(fā) CrashLoopBackOff。
資源不足
這種情況在我的本地實驗環(huán)境中遇到很多幾次,實驗環(huán)境為物理機上的虛機環(huán)境,資源不足導(dǎo)致容器無法加載,往往是在使用 HELM 或者其他工具安裝部署大量 API 對象,涉及大量工作負載時會發(fā)生,比如 ELK 或者 普羅米修斯,其他的一些很重的客戶端或者觀測工具相關(guān)部署時,部分 Pod 狀態(tài)會變成這樣.
大部分原因是因為默認的 LivenessProbe 配置的時間內(nèi), Pod 因為資源的問題無法正常運行。健康檢查返回 失敗,kubelet 會殺掉 pod 重新啟動。以此循環(huán)。還有一種情況是內(nèi)存不夠觸發(fā) OOM killer,pod 容器進程被干掉導(dǎo)致。
對于這種情況,可以調(diào)整工作負載相關(guān)控制器對應(yīng)的副本數(shù),會嘗試調(diào)小一點,如果沒辦法調(diào)整,一般不去理會,等幾個小時自己就好了 :) ,建議嘗試調(diào)整相關(guān)資源限制,pod 的 resources(考慮Qos) 或者 考慮 LimitRange,Resource Quotas(沒有實際調(diào)整過,感覺沒什么用,應(yīng)該只涉及到準入檢查)
如何對 CrashLoopBackOff 排故?
對于 CrashLoopBackOff 的問題定位,建議通過下面的方式進行。
檢查事件
運行 kubectl describe pod [name]。查看 事件 信息,通過事件可以查看到 健康檢查失敗 等相關(guān)信息,比如下面的一些事件
Back-off restarting failed container.
Liveness probe failed: XXXX
可能是資源不夠用,導(dǎo)致 pod 沒有啟動成功,健康檢查判斷失敗,任務(wù) pod 已經(jīng)掛掉,所以 kubelet 會反復(fù)殺掉當前 pod 重啟一個。也可能是 健康檢查配置問題,配置的檢測條件是一個持續(xù)失敗的條件??匆粋€ demo .
apiVersion: v1
kind: Pod
metadata:
name: crashlookbackoff-pod
spec:
containers:
- name: busybox
image: busybox
args:
- /bin/sh
- -c
- sleep infinity
livenessProbe:
exec:
command:
- cat
- /tmp/liruilong
initialDelaySeconds: 5 #容器啟動的5s內(nèi)不監(jiān)測
periodSeconds: 5 #每5s鐘檢測一次
上面的是一個配置健康檢查的 pod,檢查方式為 exec ,這是一個永遠返回失敗的條件,所以正常會觸發(fā) CrashLoopBackOff,下面為通過 kubectl describe pods crashlookbackoff-pod 查看事件信息。
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl describe pods crashlookbackoff-pod | grep -A 20 -i event
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 17m default-scheduler Successfully assigned default/crashlookbackoff-pod to vms106.liruilongs.github.io
Normal Pulled 17m kubelet Container image "docker.io/istio/proxyv2:1.16.2" already present on machine
Normal Created 17m kubelet Created container istio-init
Normal Started 17m kubelet Started container istio-init
Normal Pulled 17m kubelet Successfully pulled image "busybox" in 15.691344116s
Normal Pulled 17m kubelet Container image "docker.io/istio/proxyv2:1.16.2" already present on machine
Normal Created 17m kubelet Created container istio-proxy
Normal Started 17m kubelet Started container istio-proxy
Warning Unhealthy 17m (x2 over 17m) kubelet Readiness probe failed: Get "http://10.244.31.80:15021/healthz/ready": dial tcp 10.244.31.80:15021: connect: connection refused
Normal Pulled 16m kubelet Successfully pulled image "busybox" in 15.599021058s
Normal Created 16m (x2 over 17m) kubelet Created container busybox
Normal Started 16m (x2 over 17m) kubelet Started container busybox
Warning Unhealthy 16m (x6 over 17m) kubelet Liveness probe failed: cat: can't open '/tmp/liruilong': No such file or directory
Normal Killing 16m (x2 over 17m) kubelet Container busybox failed liveness probe, will be restarted
Normal Pulling 12m (x6 over 17m) kubelet Pulling image "busybox"
Warning BackOff 2m39s (x36 over 11m) kubelet Back-off restarting failed container
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl get pods crashlookbackoff-pod -w
NAME READY STATUS RESTARTS AGE
crashlookbackoff-pod 1/2 CrashLoopBackOff 7 (3m4s ago) 15m
可以看到 Liveness probe failed: ,Back-off restarting failed container 相關(guān)的事件,在生產(chǎn)環(huán)境可以通過 服務(wù)可用性檢查相關(guān)定位問題,檢查是配置錯誤,還是資源不夠用,如果是資源問題,是否需要調(diào)整 periodSeconds 或 initialDelaySeconds 給應(yīng)用程序更多的啟動時間。
檢查日志
如果上一步?jīng)]有提供任何細節(jié)或無法識別,下一步我們可以通過日志信息獲取相關(guān)信息。
類似在最上面的 Demo,模擬程序 bug 的 sleep 5;exit 3,我們查看事件信息,基本上得不到有用的信息(見下面的代碼), Back-off restarting failed container 只是告訴我們?nèi)萜髦貑⑹ ?br>
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl describe pods crashlookbackoff-pod | grep -A 20 Events:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 9m52s default-scheduler Successfully assigned default/crashlookbackoff-pod to vms106.liruilongs.github.io
Normal Pulled 9m50s kubelet Container image "docker.io/istio/proxyv2:1.16.2" already present on machine
Normal Created 9m50s kubelet Created container istio-init
Normal Started 9m50s kubelet Started container istio-init
Normal Pulled 9m49s kubelet Successfully pulled image "busybox" in 1.007350697s
Normal Created 9m48s kubelet Created container istio-proxy
Normal Pulled 9m48s kubelet Container image "docker.io/istio/proxyv2:1.16.2" already present on machine
Normal Started 9m47s kubelet Started container istio-proxy
Normal Pulled 9m26s kubelet Successfully pulled image "busybox" in 15.685743099s
Normal Pulled 8m53s kubelet Successfully pulled image "busybox" in 16.040759951s
Normal Pulling 8m22s (x4 over 9m50s) kubelet Pulling image "busybox"
Normal Created 8m6s (x4 over 9m48s) kubelet Created container busybox
Normal Started 8m6s (x4 over 9m48s) kubelet Started container busybox
Normal Pulled 8m6s kubelet Successfully pulled image "busybox" in 15.878975739s
Warning BackOff 4m50s (x15 over 9m20s) kubelet Back-off restarting failed container
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl get pods crashlookbackoff-pod
NAME READY STATUS RESTARTS AGE
crashlookbackoff-pod 1/2 CrashLoopBackOff 6 (2m27s ago) 10m
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$
這里可以通過日志信息定位問題
檢查 pod 日志
通過 kubectl logs [podname] -c [containername] 獲取pod日志信息,如果只有一個 容器,則不需要 -c 參數(shù)。這里看一個我前段時間遇到 CrashLoopBackOff 的問題, 在安裝阿里開源的一個 k8s 工作負載增強工具 Kruise 的時候,DS 資源一直在重啟
下面為通過 Helm 部署后的資源查看
┌──[root@vms100.liruilongs.github.io]-[~/ansible/openkruise]
└─$kubectl get all -n kruise-system
NAME READY STATUS RESTARTS AGE
pod/kruise-controller-manager-7dc584559b-j8j78 1/1 Running 0 2m31s
pod/kruise-controller-manager-7dc584559b-r954q 1/1 Running 0 2m32s
pod/kruise-daemon-24fgt 0/1 CrashLoopBackOff 4 (9s ago) 2m33s
pod/kruise-daemon-7t5q6 0/1 CrashLoopBackOff 4 (11s ago) 2m32s
pod/kruise-daemon-fbt8m 0/1 CrashLoopBackOff 4 (16s ago) 2m33s
pod/kruise-daemon-fc8xr 0/1 CrashLoopBackOff 4 (11s ago) 2m32s
pod/kruise-daemon-kjjfd 0/1 CrashLoopBackOff 4 (15s ago) 2m32s
pod/kruise-daemon-krs9s 0/1 CrashLoopBackOff 4 (17s ago) 2m33s
pod/kruise-daemon-lb5nq 0/1 CrashLoopBackOff 4 (15s ago) 2m32s
pod/kruise-daemon-zpfzg 0/1 CrashLoopBackOff 3 (32s ago) 2m32s
通過對 DS 的日志查看發(fā)現(xiàn),它通過默認的 CRI 接口實現(xiàn)找不到對應(yīng)的 runtime 容器運行時,
W0228 07:29:31.671667 1 mutation_detector.go:53] Mutation detector is enabled, this will result in memory leakage.
E0228 07:29:31.671746 1 factory.go:224] /hostvarrun/docker.sock exists, but not found /hostvarrun/dockershim.sock
W0228 07:29:31.767342 1 factory.go:113] Failed to new image service for containerd (, unix:///hostvarrun/containerd/containerd.sock): failed to fetch cri-containerd status: rpc error: code = Unimplemented desc = unknown service runtime.v1alpha2.RuntimeService
W0228 07:29:31.767721 1 mutation_detector.go:53] Mutation detector is enabled, this will result in memory leakage.
panic: runtime error: invalid memory address or nil pointer dereference
重點在這一句:/hostvarrun/docker. sock exists, but not found /hostvarrun/dockershim.sock | Failed to new image service for containerd... , docker. sock 存在,但是沒有找到 dockershim.sock,創(chuàng)建新的鏡像服務(wù)失敗.
無法識別 CRI 接口實現(xiàn),k8s 在 1.24 版本測底移除了dockershim 的CRI接口實現(xiàn),當前版本為 1.25,所以找不到對應(yīng)的 CRI實現(xiàn),高版本容器運行時還是用 docker 的話,需要安裝一個 cri-docker 的CRI接口實現(xiàn), 所以這里需要告訴這些 DS, CRI 的接口實現(xiàn)是cri-docker不是默認的dockershim。
最后提了 issue ,有大佬指出可以在部署時指定 ,daemon.socketLocation=/var/run/,daemon.socketFile=cri-dockerd.sock。指定之后工具可以順利安裝。
檢查 deploy 日志
運行以下命令以檢索 kubectl 部署日志
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$kubectl logs -f deployments/release-name-grafana
Found 2 pods, using pod/release-name-grafana-76f4b7b77d-bbvws
[2023-03-26 03:14:28] Starting collector
................
這也可能提供有關(guān)應(yīng)用程序級別問題的線索。例如,您可以在下面看到一個顯示./data can’t be mounted,可能是因為它已被使用并被其他容器鎖定。
資源限制
由于內(nèi)存資源不足,觸發(fā) OOM killer,可能會遇到 CrashLoopBackOff 錯誤??梢酝ㄟ^更改容器資源清單中的 resources:limits來增加內(nèi)存限制。或者考慮重 Qos 設(shè)置,提高 Qos 等級等。
這里為什么只有內(nèi)存,計算資源有很多,CUP,GPU之類,這因為 CPU 是可壓縮資源。內(nèi)存是不可壓縮資源,比如對于一個 Pod 來講。
當CUP不夠時, cgroups 會對 Pod 中的容器的 CPU 使用進行限流(Throttled)
當內(nèi)存不夠時,如果 Pod 使用的內(nèi)存量超過了它的 Requests 的配置,那么這個 Pod 有可能被 Kubernetes 殺掉,如果 Pod 使用的內(nèi)存量超過了它的 Limits 設(shè)置,那么操作系統(tǒng)內(nèi)核會殺掉 Pod 所有容器的所有進程中使用內(nèi)存最多的一個,直到內(nèi)存不超過 Limits 為止。
鏡像問題
如果仍然存在問題,另一個原因可能是您使用的docker鏡像可能無法正常工作,您需要確保單獨運行時它可以正常工作。如果這對 Kubernetes 有效但失敗了,您可能需要提前找到正在發(fā)生的事情,嘗試以下操作,
確定 entrypoint 和 cmd
需要確定 entrypoint和 cmd 以獲得對容器的訪問權(quán)限以進行調(diào)試。請執(zhí)行下列操作:
運行docker pull [image-id] 拉取鏡像。
運行docker history 6600fae04efd --no-trunc 并找到容器的啟動命令,或者可以使用docker inspect [image-id]。
┌──[root@vms100.liruilongs.github.io]-[~/ansible/crashlookbackoff_demo]
└─$docker history 6600fae04efd --no-trunc
................
... /bin/sh -c #(nop) CMD ["haproxy" "-f" "/usr/local/etc/haproxy/haproxy.cfg"]
... /bin/sh -c #(nop) ENTRYPOINT ["/docker-entrypoint.sh"]
.................
覆蓋 entrypoint
由于容器崩潰無法啟動,需要暫時將Dockerfile 的 entrypoint 更改為 tail -f /dev/null
Spec:
containers:
- command: ['tail','-f','/dev/null']
檢查原因
使用命令行 kubectl exec 執(zhí)行進入問題容器。
┌──[root@vms100.liruilongs.github.io]-[/etc/kubernetes/manifests]
└─$kubectl exec -it release-name-grafana-76f4b7b77d-ddr7k -- sh
/app $ ls
helpers.py resources.py sidecar.py
/app $
單獨啟動進程,查看日志,確認配置信息。存在問題修復(fù)。
社區(qū)提問,請教大佬
如果問題還是沒有解決,可以在相對活躍的社區(qū)提問,請教大佬,比如 StackOverflow 或者 Slack。
博文部分內(nèi)容參考
? 文中涉及參考鏈接內(nèi)容版權(quán)歸原作者所有,如有侵權(quán)請告知
https://foxutech.medium.com/kubernetes-crashloopbackoff-how-to-troubleshoot-940dbb16bc84
作者:山河已無恙
歡迎關(guān)注微信公眾號 :山河已無恙