【kubernetes入門到精通】Kubernetes的健康監測機制以及常見ExitCode問題分析「探索篇」

kubernetes進行Killed我們服務的問題背景

無論是在微服務體系還是雲原生體系的開發迭代過程中,通常都會以Kubernetes進行容器化部署,但是這也往往帶來了很多意外的場景和情況。例如,雖然我們已經將JVM堆內存設置爲小於Docker容器中內存及K8S的Pod的內存,但是還是會被K8s給無情的殺掉(Kill -9 / Kill -15)Killed。當發生了Killed的時候,我們該如何分析和判斷呢?在此我們介紹一下K8s的Killed的Exit Code編碼。

kubernetes健康檢測體系之探針

K8s中的探針用來對pod中容器的狀態進行檢測,有3種探針,存活探針、就緒探針、啓動探針。

kubernetes如何監控和管理我們的Pod的運行狀態

Kubernetes中的健康檢查主要使用就緒性探針(readinessProbes)存活性探針(livenessProbes) 來實現,service即爲負載均衡,k8s保證service後面的pod都可用,是k8s中自愈能力的主要手段,主要基於這兩種探測機制,可以實現如下需求:

  • 異常實例自動剔除,並重啓新實例
  • 多種類型探針檢測,保證異常pod不接入流量
  • 不停機部署,更安全的滾動升級

存活探針 — livenessProbes

kubelet使用 存活探針 來確定什麼時候要重啓容器。 例如,存活探針可以探測到應用死鎖(應用程序在運行,但是無法繼續執行後面的步驟)情況,重啓這種狀態下的容器有助於提高應用的可用性,即使其中存在缺陷。

就緒探針 — readinessProbes

kubelet使用就緒探針可以知道容器何時準備好接受請求流量,當一個 Pod 內的所有容器都就緒時,才能認爲該 Pod 就緒。 該指針用來指示容器是否準備好爲請求提供服務。如果就緒態探測失敗,kubelet將該Pod提供的所有服務的endpoint列表中刪除該Pod的 IP地址。

與存活探針的區別

當容器未通過檢查準備,則不會被終止或重新啓動。存活探針通過殺死異常的容器並用新的容器去替代他們的工作,而就緒探針確保只有準備好處理請求的pod才能在服務集羣中。

啓動探針 — startupProbes( 1.17 版本新增)

kubelet使用啓動探針來了解應用容器何時啓動。 如果配置了這類探針,你就可以控制容器在啓動成功後再進行存活性和就緒態檢查, 確保這些存活、就緒探針不會影響應用的啓動。

啓動探針可以用於對慢啓動容器進行存活性檢測,避免它們在啓動運行之前就被殺掉,如:使用了啓動探針,則所有其他探針都會被禁用,直到此探針成功爲止。如果啓動探測失敗,kubelet將殺死容器,而容器依其重啓策略進行重啓。

探針的3種機制

每種探測機制支持三種健康檢查方法,分別是命令行exec,httpGet和tcpSocket,其中exec通用性最強,適用與大部分場景,tcpSocket適用於TCP業務,httpGet適用於web業務。

  • HTTP GEt:該類型的探針通過容器的IP地址、端口號及路徑調用 HTTP Get請求,如果響應的狀態碼大於等於200且小於400,則認爲容器 健康。
  • TcpSocket:該類型的探針嘗試與容器指定端口建立TCP連接,如果端口打開,則診斷被認爲是成功的。
  • Exec(自定義健康檢查):該類型的探針在容器內執行任意的命令,如果命令退出時返回碼爲0,則認爲診斷成功。
配置和設定livenessProbes探針

通過在yaml文件中pod的spec部分的containers裏面添加一個字段livenessProbe來添加存活指針:

livenessProbe執行模式執行探針控制(httpGet)
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  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

Exec執行模式執行探針控制(exec)
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  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
livenessProbe執行模式執行探針控制(tcpSocket)
apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  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

每次探測都將獲得以下三種結果之一:

  • Success(成功):表示容器通過了診斷。
  • Failure(失敗):表示容器未通過診斷。
  • Unknown(未知):表示沒有正常進行且診斷失敗,因此不會採取任何行動。

容器退出狀態碼的區間

Exit Codes的取值範圍必須在0-255之間。可以參考:https://tldp.org/LDP/abs/html/exitcodes.html,如下圖所示。

  • 0:表示正常退出
  • 1-128:一般程序自身原因導致的異常退出狀態區間在 1-128 (這只是一般約定,程序如果一定要用129-255的狀態碼也是可以的)
  • 129-255:外界中斷將程序退出的時候狀態碼區間在129-255,(操作系統給程序發送中斷信號,比如 kill -9 是 SIGKILL)

查看 Pod 退出狀態碼

$ kubectl describe pods ${pod-name}

如下圖所示:

Exit Code 0

退出代碼0表示特定容器沒有附加前臺進程,該退出代碼是所有其他後續退出代碼的例外,這不一定意味着發生了不好的事情。如果開發人員想要在容器完成其工作後自動停止其容器,則使用此退出代碼。比如:kubernetes job在執行完任務後正常退出碼爲 0

Exit Code 1

程序錯誤,或者Dockerfile中引用不存在的文件,如 entrypoint中引用了錯誤的包程序錯誤可以很簡單,例如 “除以0”,也可以很複雜,比如空引用或者其他程序 crash

Exit Code 139

Exit Code 139: Indicates failure as container received SIGSEGV

表明容器收到了 SIGSEGV 信號,無效的內存引用,對應kill -11,一般是代碼有問題,或者 docker 的基礎鏡像有問題

Exit Code 143

Exit Code 143: Indicates failure as container received SIGTERM

表明容器收到了 SIGTERM 信號,終端關閉,對應kill -15,一般對應 docker stop 命令,有時docker stop也會導致Exit Code 137,發生在與代碼無法處理SIGTERM的情況下,docker進程等待十秒鐘然後發出 SIGKILL 強制退出。

Exit Code 137

Exit Code 137: Indicates failure as container received SIGKILL

表明容器收到了 SIGKILL 信號,進程被殺掉,對應kill -9,引發SIGKILL的是docker kill。這可以由用戶或由docker守護程序來發起,手動執行:docker kill(Manual intervention or ‘oom-killer’ [OUT-OF-MEMORY]) 被手動干預殺死進程,或者違反系統限制被殺

137 比較常見,如果 pod 中的limit 資源設置較小,會運行內存不足導致 OOMKilled,此時state 中的 ”OOMKilled” 值爲true,你可以在系統的 dmesg -T 中看到 oom 日誌

內存溢出問題

此狀態碼一般是因爲 pod 中容器內存達到了它的資源限制(resources.limits),一般是內存溢出(OOM),CPU達到限制只需要不分時間片給程序就可以。因爲限制資源是通過 linux 的 cgroup 實現的,所以 cgroup 會將此容器強制殺掉,類似於 kill -9,此時在 describe pod 中可以看到 Reason 是 OOMKilled

還可能是宿主機本身資源不夠用了(OOM),內核會選取一些進程殺掉來釋放內存,不管是 cgroup 限制殺掉進程還是因爲節點機器本身資源不夠導致進程死掉,都可以從系統日誌中找到記錄:

ubuntu 的系統日誌在 /var/log/syslog,centos的系統日誌在 /var/log/messages,都可以用 journalctl -k 來查看系統日誌

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章