kubernetes之容器健康狀態檢測


簡介

此文講述如何配置容器的livenessreadinessstartup探針。

kubelet 使用liveness探測器來知道什么時候要重啟容器。例如,liveness探測器可以捕捉到死鎖(應用程序在運行,但是無法繼續執行后面的步驟)。這樣的情況下重啟容器有助於讓應用程序在有問題的情況下更可用。

kubelet 使用readiness探測器可以知道容器什么時候准備好了並可以開始接受請求流量, 當一個 Pod 內的所有容器都准備好了,才能把這個 Pod 看作就緒了。這種信號的一個用途就是控制哪個 Pod 作為 Service 的后端。在 Pod 還沒有准備好的時候,會從 Service 的負載均衡器中被剔除的。

kubelet 使用startup探測器可以知道應用程序容器什么時候啟動了。如果配置了這類探測器,就可以控制容器在啟動成功后再進行livenessreadiness檢查,確保這些存活、就緒探測器不會影響應用程序的啟動。這可以用於對慢啟動容器進行存活性檢測,避免它們在啟動運行之前就被殺掉。

備注:此文檔參考官方文檔,並加以自己的理解。如有誤導性的內容,請批評指正。

定義一個 liveness 探針

許多長時間運行的應用程序最終會過渡到斷開的狀態,除非重新啟動,否則無法恢復。Kubernetes 提供了liveness探測器來發現並補救這種情況。

創建一個 Pod,其中運行一個基於 k8s.gcr.io/busybox 鏡像的容器。配置文件如下。文件名:exec-liveness.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

在配置文件中,可以看到Pod中只有一個容器。periodSeconds字段指定了kubelet 應該每 5 秒執行一次存活檢測。initialDelaySeconds 字段告訴 kubelet 在執行第一次探針前應該等待 5 秒。kubelet 在容器中執行命令cat /tmp/healthy來進行檢測。如果命令執行成功並且返回值為0,kubelet會認為這個容器是健康存活的。如果這個命令返回非 0 值,kubelet 會殺死這個容器並重新啟動它。執行命令如下

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"

這個容器生命的前 30 秒,/tmp/healthy文件是存在的。執行命令cat /tmp/healthy會返回成功碼。30秒后,執行命令cat /tmp/healthy就回返回失敗碼。

創建Pod

# kubectl apply -f /root/k8s-example/probe/exec-liveness.yaml

在 30 秒內,查看 Pod 的事件

kubectl describe pod liveness-exec

輸出結果顯示還沒有存活探測器失敗

Events:
  Type    Reason     Age        From                 Message
  ----    ------     ----       ----                 -------
  Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/liveness-exec to k8s-node04
  Normal  Pulled     22s        kubelet, k8s-node04  Container image "k8s.gcr.io/busybox" already present on machine
  Normal  Created    22s        kubelet, k8s-node04  Created container liveness
  Normal  Started    22s        kubelet, k8s-node04  Started container liveness

30 秒之后,再來看 Pod 的事件:

kubectl describe pod liveness-exec

在輸出結果的最下面,有信息顯示存活探測器失敗了,這個容器被殺死並且被重建了。

Events:
  Type     Reason     Age               From                 Message
  ----     ------     ----              ----                 -------
  Normal   Scheduled  <unknown>         default-scheduler    Successfully assigned default/liveness-exec to k8s-node04
  Normal   Pulled     47s               kubelet, k8s-node04  Container image "k8s.gcr.io/busybox" already present on machine
  Normal   Created    47s               kubelet, k8s-node04  Created container liveness
  Normal   Started    47s               kubelet, k8s-node04  Started container liveness
  Warning  Unhealthy  5s (x3 over 15s)  kubelet, k8s-node04  Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
  Normal   Killing    5s                kubelet, k8s-node04  Container liveness failed liveness probe, will be restarted

再等另外 30 秒,檢查看這個容器被重啟了:

kubectl get pod liveness-exec
NAME            READY   STATUS    RESTARTS   AGE
liveness-exec   1/1     Running   2          3m10s

再查看Pod資源詳情:

kubectl describe pod liveness-exec

輸出結果如下,容器重啟成功。

Events:
  Type     Reason     Age                 From                 Message
  ----     ------     ----                ----                 -------
  Normal   Scheduled  <unknown>           default-scheduler    Successfully assigned default/liveness-exec to k8s-node04
  Warning  Unhealthy  35s (x6 over 2m)    kubelet, k8s-node04  Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
  Normal   Killing    35s (x2 over 110s)  kubelet, k8s-node04  Container liveness failed liveness probe, will be restarted
  Normal   Pulled     5s (x3 over 2m32s)  kubelet, k8s-node04  Container image "k8s.gcr.io/busybox" already present on machine
  Normal   Created    5s (x3 over 2m32s)  kubelet, k8s-node04  Created container liveness
  Normal   Started    5s (x3 over 2m32s)  kubelet, k8s-node04  Started container liveness

定義一個存活態 HTTP 請求接口

另外一種類型的liveness探測方式是使用 HTTP GET 請求。下面是一個 Pod 的配置文件,其中運行一個基於 k8s.gcr.io/liveness 鏡像的容器。

創建Pod。文件名:``

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: X-Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

配置文件中,Pod中只有一個容器。periodSeconds字段指定了kubelet每隔 3 秒執行一次檢測。initialDelaySeconds字段告訴kubelet在執行第一次探測前應該等待 3 秒。kubelet 會向容器內運行的服務(服務會監聽 8080 端口)發送一個 HTTP GET 請求來執行探測。如果服務上/healthz路徑下的處理程序返回成功碼。則kubelet認為容器是健康存活的。如果處理程序返回失敗碼,則kubelet會殺死這個容器並且重新啟動它。

任何大於或等於 200 並且小於 400 的返回碼標示成功,其它返回碼都標示失敗。

可以在這里看到服務的源碼server.go

容器存活的最開始 10 秒中,/healthz 處理程序返回一個 200 的狀態碼。之后處理程序返回 500 的狀態碼。

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

kubelet 在容器啟動之后 3 秒開始執行健康檢測。所以前幾次健康檢查都是成功的。但是 10 秒之后,健康檢查會失敗,並且 kubelet 會殺死容器再重新啟動容器。

# kubectl apply -f /root/k8s-example/probe/http-liveness.yaml

10 秒之后,通過看 Pod 事件來檢測存活探測器已經失敗了並且容器被重新啟動了。

Events:
  Type     Reason     Age              From                 Message
  ----     ------     ----             ----                 -------
  Normal   Scheduled  <unknown>        default-scheduler    Successfully assigned default/liveness-http to k8s-node01
  Normal   Pulled     17s              kubelet, k8s-node01  Container image "k8s.gcr.io/liveness" already present on machine
  Normal   Created    17s              kubelet, k8s-node01  Created container liveness
  Normal   Started    16s              kubelet, k8s-node01  Started container liveness
  Warning  Unhealthy  1s (x2 over 4s)  kubelet, k8s-node01  Liveness probe failed: HTTP probe failed with statuscode: 500

定義 TCP 的存活探測

第三種類型的liveness探測是使用 TCP 套接字。通過配置,kubelet 會嘗試在指定端口和容器建立套接字鏈接。如果能建立鏈接,這個容器就被看作是健康的,如果不能則這個容器就被看作是有問題的。

創建一個Pod。文件名:tcp-liveness-readiness.yaml

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

TCP 檢測的配置和 HTTP 檢測非常相似。下面這個例子同時使用就緒和存活探測器。kubelet 會在容器啟動 5 秒后發送第一個就緒探測。這會嘗試連接 goproxy 容器的 8080 端口。如果探測成功,這個 Pod 會被標記為就緒狀態,kubelet 將繼續每隔 10 秒運行一次檢測。

除了readiness探測,這個配置包括了一個liveness探測。kubelet 會在容器啟動 15 秒后進行第一次liveness探測。就像readiness探測一樣,會嘗試連接 goproxy 容器的 8080 端口。如果存活探測失敗,這個容器會被重新啟動。

# kubectl apply -f /root/k8s-example/probe/tcp-liveness-readiness.yaml

15 秒之后,通過看 Pod 事件來檢測存活探測器:

# kubectl describe pod goproxy

使用命名端口

對於 HTTP 或者 TCP 存活檢測可以使用命名的容器端口。

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port

使用startup探測器保護慢啟動容器

有時候,會有一些現有的應用程序在啟動時需要較多的初始化時間。要不影響對引起探測死鎖的快速響應,這種情況下,設置liveness探測參數是要技巧的。技巧就是使用一個命令來設置startup探測,針對HTTP 或者 TCP 檢測,可以通過設置 failureThreshold * periodSeconds 參數來保證有足夠長的時間應對糟糕情況下的啟動時間。

所以,前面的例子就變成了:

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

幸虧有startup探測,應用程序將會有最多 5 分鍾(30 * 10 = 300s) 的時間來完成它的啟動。 一旦startup探測成功一次,存活探測任務就會接管對容器的探測,對容器死鎖可以快速響應。 如果startup探測一直沒有成功,容器會在 300 秒后被殺死,並且根據 restartPolicy 來設置 Pod 狀態。

定義readliness探測器

有時候,應用程序會暫時性的不能提供通信服務。例如,應用程序在啟動時可能需要加載很大的數據或配置文件,或是啟動后要依賴等待外部服務。在這種情況下,既不想殺死應用程序,也不想給它發送請求。Kubernetes 提供了就緒探測器來發現並緩解這些情況。容器所在 Pod 上報還未就緒的信息,並且不接受通過 Kubernetes Service 的流量。

注意:就緒探測器在容器的整個生命周期中保持運行狀態。

就緒探測器的配置和存活探測器的配置相似。唯一區別就是要使用 readinessProbe 字段,而不是 livenessProbe 字段。

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

HTTP 和 TCP 的readliness探測器配置也和liveness探測器的配置一樣的。

readlinessliveness探測可以在同一個容器上並行使用。兩者都用可以確保流量不會發給還沒有准備好的容器,並且容器會在它們失敗的時候被重新啟動。

配置探測器

探測器有很多配置字段,可以使用這些字段精確的控制存活和就緒檢測的行為:

  • initialDelaySeconds:容器啟動后要等待多少秒后存活和就緒探測器才被初始化,默認是 0 秒,最小值是 0。
  • periodSeconds:執行探測的時間間隔(單位是秒)。默認是 10 秒。最小值是 1。
  • timeoutSeconds:探測的超時后等待多少秒。默認值是 1 秒。最小值是 1。
  • successThreshold:探測器在失敗后,被視為成功的最小連續成功數。默認值是 1。存活探測的這個值必須是 1。最小值是 1。
  • failureThreshold:當 Pod 啟動了並且探測到失敗,Kubernetes 的重試次數。存活探測情況下的放棄就意味着重新啟動容器。就緒探測情況下的放棄 Pod 會被打上未就緒的標簽。默認值是 3。最小值是 1。

HTTP 探測器可以在 httpGet 上配置額外的字段:

  • host:連接使用的主機名,默認是 Pod 的 IP。也可以在 HTTP 頭中設置 “Host” 來代替。
  • scheme:用於設置連接主機的方式(HTTP 還是 HTTPS)。默認是 HTTP。
  • path:訪問 HTTP 服務的路徑。
  • httpHeaders:請求中自定義的 HTTP 頭。HTTP 頭字段允許重復。
  • port:訪問容器的端口號或者端口名。如果數字必須在 1 ~ 65535 之間。

對於 HTTP 探測,kubelet 發送一個 HTTP 請求到指定的路徑和端口來執行檢測。除非 httpGet 中的 host 字段設置了,否則 kubelet 默認是給 Pod 的 IP 地址發送探測。如果 scheme 字段設置為了 HTTPS,kubelet 會跳過證書驗證發送 HTTPS 請求。大多數情況下,不需要設置host字段。這里有個需要設置 host 字段的場景,假設容器監聽 127.0.0.1,並且 Pod 的 hostNetwork 字段設置為了 true。那么 httpGet 中的 host 字段應該設置為 127.0.0.1。可能更常見的情況是如果 Pod 依賴虛擬主機,你不應該設置 host 字段,而是應該在 httpHeaders 中設置 Host。

對於一次探測,kubelet 在節點上(不是在 Pod 里面)建立探測連接,這意味着你不能在 host 參數上配置 service name,因為 kubelet 不能解析 service name


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM