
無論是在微服務(wù)體系還是云原生體系的開發(fā)迭代過程中,通常都會以Kubernetes進(jìn)行容器化部署,但是這也往往帶來了很多意外的場景和情況。例如,雖然我們已經(jīng)將JVM堆內(nèi)存設(shè)置為小于Docker容器中內(nèi)存及K8S的Pod的內(nèi)存,但是還是會被K8s給無情的殺掉(Kill -9 / Kill -15)Killed。當(dāng)發(fā)生了Killed的時候,我們該如何分析和判斷呢?在此我們介紹一下K8s的Killed的Exit Code編碼。
K8s中的探針用來對pod中容器的狀態(tài)進(jìn)行檢測,有3種探針,存活探針、就緒探針、啟動探針。
Kubernetes中的健康檢查主要使用就緒性探針(readinessProbes)和 存活性探針(livenessProbes)來實現(xiàn),service即為負(fù)載均衡,k8s保證service后面的pod都可用,是k8s中自愈能力的主要手段,主要基于這兩種探測機制,可以實現(xiàn)如下需求:
(資料圖片)
kubelet使用 存活探針來確定什么時候要重啟容器。 例如,存活探針可以探測到應(yīng)用死鎖(應(yīng)用程序在運行,但是無法繼續(xù)執(zhí)行后面的步驟)情況,重啟這種狀態(tài)下的容器有助于提高應(yīng)用的可用性,即使其中存在缺陷。
kubelet使用就緒探針可以知道容器何時準(zhǔn)備好接受請求流量,當(dāng)一個 Pod 內(nèi)的所有容器都就緒時,才能認(rèn)為該 Pod 就緒。 該指針用來指示容器是否準(zhǔn)備好為請求提供服務(wù)。如果就緒態(tài)探測失敗,kubelet將該Pod提供的所有服務(wù)的endpoint列表中刪除該Pod的 IP地址。
當(dāng)容器未通過檢查準(zhǔn)備,則不會被終止或重新啟動。存活探針通過殺死異常的容器并用新的容器去替代他們的工作,而就緒探針確保只有準(zhǔn)備好處理請求的pod才能在服務(wù)集群中。
kubelet使用啟動探針來了解應(yīng)用容器何時啟動。 如果配置了這類探針,你就可以控制容器在啟動成功后再進(jìn)行存活性和就緒態(tài)檢查, 確保這些存活、就緒探針不會影響應(yīng)用的啟動。
啟動探針可以用于對慢啟動容器進(jìn)行存活性檢測,避免它們在啟動運行之前就被殺掉,如:使用了啟動探針,則所有其他探針都會被禁用,直到此探針成功為止。如果啟動探測失敗,kubelet將殺死容器,而容器依其重啟策略進(jìn)行重啟。
每種探測機制支持三種健康檢查方法,分別是命令行exec,httpGet和tcpSocket,其中exec通用性最強,適用與大部分場景,tcpSocket適用于TCP業(yè)務(wù),httpGet適用于web業(yè)務(wù)。
HTTP GEt:該類型的探針通過容器的IP地址、端口號及路徑調(diào)用 HTTP Get請求,如果響應(yīng)的狀態(tài)碼大于等于200且小于400,則認(rèn)為容器 健康。TcpSocket:該類型的探針嘗試與容器指定端口建立TCP連接,如果端口打開,則診斷被認(rèn)為是成功的。Exec(自定義健康檢查):該類型的探針在容器內(nèi)執(zhí)行任意的命令,如果命令退出時返回碼為0,則認(rèn)為診斷成功。通過在yaml文件中pod的spec部分的containers里面添加一個字段livenessProbe來添加存活指針:
apiVersion: v1kind: Podmetadata: labels: test: liveness name: liveness-httpspec: containers: - name: liveness image: registry.k8s.io/liveness args: - /server livenessProbe: httpGet: path: /healthz port: 8080 httpHeaders: - name: Custom-Header value: Awesome initialDelaySeconds: 3 periodSeconds: 3
apiVersion: v1kind: Podmetadata: labels: test: liveness name: liveness-execspec: containers: - name: liveness image: registry.k8s.io/busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600 livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 5 periodSeconds: 5
apiVersion: v1kind: Podmetadata: name: goproxy labels: app: goproxyspec: containers: - name: goproxy image: registry.k8s.io/goproxy:0.1 ports: - containerPort: 8080 readinessProbe: tcpSocket: port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 15 periodSeconds: 20
每次探測都將獲得以下三種結(jié)果之一:
Success(成功):表示容器通過了診斷。Failure(失敗):表示容器未通過診斷。Unknown(未知):表示沒有正常進(jìn)行且診斷失敗,因此不會采取任何行動。Exit Codes的取值范圍必須在0-255之間。可以參考:??https://tldp.org/LDP/abs/html/exitcodes.html,如下圖所示。??
0:表示正常退出1-128:一般程序自身原因?qū)е碌漠惓M顺鰻顟B(tài)區(qū)間在 1-128 (這只是一般約定,程序如果一定要用129-255的狀態(tài)碼也是可以的)129-255:外界中斷將程序退出的時候狀態(tài)碼區(qū)間在129-255,(操作系統(tǒng)給程序發(fā)送中斷信號,比如 kill -9 是 SIGKILL)$ kubectl describe pods ${pod-name}
如下圖所示:
退出代碼0表示特定容器沒有附加前臺進(jìn)程,該退出代碼是所有其他后續(xù)退出代碼的例外,這不一定意味著發(fā)生了不好的事情。如果開發(fā)人員想要在容器完成其工作后自動停止其容器,則使用此退出代碼。比如:kubernetes job在執(zhí)行完任務(wù)后正常退出碼為 0
程序錯誤,或者Dockerfile中引用不存在的文件,如 entrypoint中引用了錯誤的包程序錯誤可以很簡單,例如 “除以0”,也可以很復(fù)雜,比如空引用或者其他程序 crash
Exit Code 139: Indicates failure as container received SIGSEGV
表明容器收到了 SIGSEGV 信號,無效的內(nèi)存引用,對應(yīng)kill -11,一般是代碼有問題,或者 docker 的基礎(chǔ)鏡像有問題
Exit Code 143: Indicates failure as container received SIGTERM
表明容器收到了 SIGTERM 信號,終端關(guān)閉,對應(yīng)kill -15,一般對應(yīng) docker stop 命令,有時docker stop也會導(dǎo)致Exit Code 137,發(fā)生在與代碼無法處理SIGTERM的情況下,docker進(jìn)程等待十秒鐘然后發(fā)出 SIGKILL 強制退出。
Exit Code 137: Indicates failure as container received SIGKILL
表明容器收到了 SIGKILL 信號,進(jìn)程被殺掉,對應(yīng)kill -9,引發(fā)SIGKILL的是docker kill。這可以由用戶或由docker守護(hù)程序來發(fā)起,手動執(zhí)行:docker kill(Manual intervention or ‘oom-killer’ [OUT-OF-MEMORY]) 被手動干預(yù)殺死進(jìn)程,或者違反系統(tǒng)限制被殺
137 比較常見,如果 pod 中的limit 資源設(shè)置較小,會運行內(nèi)存不足導(dǎo)致 OOMKilled,此時state 中的 ”O(jiān)OMKilled” 值為true,你可以在系統(tǒng)的 dmesg -T 中看到 oom 日志
此狀態(tài)碼一般是因為 pod 中容器內(nèi)存達(dá)到了它的資源限制(resources.limits),一般是內(nèi)存溢出(OOM),CPU達(dá)到限制只需要不分時間片給程序就可以。因為限制資源是通過 linux 的 cgroup 實現(xiàn)的,所以 cgroup 會將此容器強制殺掉,類似于kill -9,此時在describe pod中可以看到 Reason 是OOMKilled
還可能是宿主機本身資源不夠用了(OOM),內(nèi)核會選取一些進(jìn)程殺掉來釋放內(nèi)存,不管是 cgroup 限制殺掉進(jìn)程還是因為節(jié)點機器本身資源不夠?qū)е逻M(jìn)程死掉,都可以從系統(tǒng)日志中找到記錄:
ubuntu 的系統(tǒng)日志在/var/log/syslog,centos的系統(tǒng)日志在/var/log/messages,都可以用journalctl -k來查看系統(tǒng)日志。
標(biāo)簽: 系統(tǒng)日志 退出狀態(tài)