理論+實操:K8S的pod健康檢查——live、ready、startup

一:健康檢查概述

又被稱爲探針(probe),探針是檢查pod資源

注意:規則可以同時定義

1.1 探針策略類型

  • livenessProbe 如果檢查失敗不存活,將殺死容器,根據Pod的restartPlicy來操作

kubelet](https://kubernetes.io/zh/docs/admin/kubelet/) 使用存活探測器來知道什麼時候要重啓容器。例如,存活探測器可以捕捉到死鎖(應用程序在運行,但是無法繼續執行後面的步驟)。這樣的情況下重啓容器有助於讓應用程序在有問題的情況下更可用。

  • ReadinessProbe 如果檢查失敗沒有準備好,kubernetes會把pod從service endpoints中剔除,endpoint相當於後端的羣集,從羣集中剔除掉

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

備註:還有一個啓動探測器startup probes

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

官方文檔參考:https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

1.2 probe 探針支持三種檢查方法

  • httpGet:發送http請求,返回200-400範圍狀態碼爲成功

  • exec:執行shell命令返回狀態碼爲0代表成功

exec檢查後面所有pod資源,觸發策略就執行

  • tcpSocket:發起tcp socket建立成功 tcpsoucket 是三次握手規則

二:使用exec檢查方法

定義存活命令

許多長時間運行的應用程序最終會過渡到斷開的狀態,除非重新啓動,否則無法恢復。

Kubernetes 提供了存活探測器livenessprobe 來發現並補救這種情況。

2.1 編輯測試pod的yaml

[root@master1 ~]# vim pod4.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 300
    livenessProbe:
    #執行存活的exec探針策略
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      #容器啓動5秒後開始探測
      periodSeconds: 5
      #每5秒創建一次

在這個配置文件中,可以看到 Pod 中只有一個容器。

periodSeconds 字段指定了 kubelet 應該每 5 秒執行一次存活探測。

initialDelaySeconds 字段告訴 kubelet 在執行第一次探測前應該等待 5 秒。

kubelet 在容器內執行命令 cat /tmp/healthy 來進行探測。

如果命令執行成功並且返回值爲 0,kubelet 就會認爲這個容器是健康存活的。

如果這個命令返回非 0 值,kubelet 會殺死這個容器並重新啓動它。

2.2 啓動yaml創建pod

[root@master1 ~]# kubectl apply -f pod4.yaml 
pod/liveness-exec created
[root@master1 ~]# kubectl get pods -w
liveness-exec                       0/1     ContainerCreating   0          2s
liveness-exec   1/1   Running   0     3s

當容器啓動時,執行如下的命令:

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

這個容器生命的前 30 秒, /tmp/healthy 文件是存在的。

所以在這最開始的 30 秒內,執行命令 cat /tmp/healthy 會返回成功碼。

30 秒之後,執行命令 cat /tmp/healthy 就會返回失敗碼。

2.3 在 30 秒內,查看 Pod 的事件

^C[root@master1 ~]# kubectl describe pod liveness-exec

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

FirstSeen    LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
24s       24s     1   {default-scheduler }                    Normal      Scheduled   Successfully assigned liveness-exec to worker0
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulling     pulling image "k8s.gcr.io/busybox"
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulled      Successfully pulled image "k8s.gcr.io/busybox"
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Created     Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
23s       23s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Started     Started container with docker id 86849c15382e

2.4 35 秒之後,再來看 Pod 的事件:

[root@master1 ~]# kubectl describe pod liveness-exec

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

FirstSeen LastSeen    Count   From            SubobjectPath           Type        Reason      Message
--------- --------    -----   ----            -------------           --------    ------      -------
37s       37s     1   {default-scheduler }                    Normal      Scheduled   Successfully assigned liveness-exec to worker0
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulling     pulling image "k8s.gcr.io/busybox"
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Pulled      Successfully pulled image "k8s.gcr.io/busybox"
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Created     Created container with docker id 86849c15382e; Security:[seccomp=unconfined]
36s       36s     1   {kubelet worker0}   spec.containers{liveness}   Normal      Started     Started container with docker id 86849c15382e
2s        2s      1   {kubelet worker0}   spec.containers{liveness}   Warning     Unhealthy   Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory

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

[root@master1 ~]# kubectl get pods -w
liveness-exec                       1/1     Running     0          60s
liveness-exec   1/1   Running   1     93s
liveness-exec   1/1   Running   2     2m48s
liveness-exec   1/1   Running   3     4m6s

輸出結果顯示 RESTARTS 的值增加了

三:httpGet方式

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

第二種類型的存活探測方式是使用 HTTP GET 請求。下面是一個 Pod 的配置文件,其中運行一個基於 liveness 鏡像的容器。

3.1 編寫yaml

[root@master1 ~]# vim pod5.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: nginx
    image: nginx
   # args:
   # - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 80
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

在這個配置文件中,可以看到 Pod 也只有一個容器。

periodSeconds 字段指定了 kubelet 每隔 3 秒執行一次存活探測。

initialDelaySeconds 字段告訴 kubelet 在執行第一次探測前應該等待 3 秒。

kubelet 會向容器內運行的服務(服務會監聽 80 端口)發送一個 HTTP GET 請求來執行探測。如果服務上 /healthz 路徑下的處理程序返回成功碼,則 kubelet 認爲容器是健康存活的。如果處理程序返回失敗碼,則 kubelet 會殺死這個容器並且重新啓動它。

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

容器存活的最開始 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 會殺死容器再重新啓動容器。

3.2 創建一個 Pod 來測試 HTTP 的存活檢測:

[root@master1 ~]#kubectl apply -f pod5.yaml
pod/liveness-http created
[root@master1 ~]# kubectl get pods -w
NAME                                READY   STATUS              RESTARTS   AGE
liveness-http                       0/1     ContainerCreating   0          4s
my-tomcat-6cbc7c4d65-gpkxr          1/1     Running             0          26h
my-tomcat-6cbc7c4d65-hdmhc          1/1     Running             0          26h
nginx-6c94d899fd-xsxct              1/1     Running             1          4d22h
nginx-deployment-78cdb5b557-6z2sf   1/1     Running             1          2d22h
nginx-deployment-78cdb5b557-9pdf8   1/1     Running             1          2d22h
nginx-deployment-78cdb5b557-f2hx2   1/1     Running             1          2d22h
pod1                                1/1     Running             1          2d12h
liveness-http   1/1   Running   0     18s
liveness-http   1/1   Running   1     37s
liveness-http   1/1   Running   2     49s

3.3 通過看 Pod 事件來檢測存活探測器已經失敗了並且容器被重新啓動了。

但是原因並不是因爲之前預想的

原因是因爲在容器還未啓動的情況下,就執行探測,結果可想而知,直接就kill掉然後重建

^C[root@master1 ~]kubectl describe pod liveness-http
Events:
  Type     Reason     Age               From                      Message
  ----     ------     ----              ----                      -------
  Normal   Scheduled  55s               default-scheduler         Successfully assigned default/liveness-http to 192.168.247.143
  Normal   Pulling    8s (x3 over 54s)  kubelet, 192.168.247.143  pulling image "nginx"
  Normal   Killing    8s (x2 over 29s)  kubelet, 192.168.247.143  Killing container with id docker://nginx:Container failed liveness probe.. Container will be killed and recreated.
  Normal   Pulled     6s (x3 over 39s)  kubelet, 192.168.247.143  Successfully pulled image "nginx"
  Normal   Created    6s (x3 over 39s)  kubelet, 192.168.247.143  Created container
  Normal   Started    6s (x3 over 38s)  kubelet, 192.168.247.143  Started container
  Warning  Unhealthy  2s (x7 over 35s)  kubelet, 192.168.247.143  Liveness probe failed: HTTP probe failed with statuscode: 404
Normal   Killing    9s (x3 over 42s)   kubelet, 192.168.247.143  Killing container with id docker://nginx:Container failed liveness probe.. Container will be killed and recreated.

備註:在 1.13(包括 1.13版本)之前的版本中,如果在 Pod 運行的節點上設置了環境變量 http_proxy(或者 HTTP_PROXY),HTTP 的存活探測會使用這個代理。在 1.13 之後的版本中,設置本地的 HTTP 代理環境變量不會影響 HTTP 的存活探測

3.4 查看日誌

探針進行探測,三秒探測一次

^C[root@master1 ~]# kubectl logs liveness-http 
2020/05/13 05:00:04 [error] 6#6: *1 open() "/usr/share/nginx/html/healthz" failed (2: No such file or directory), client: 172.17.45.1, server: localhost, request: "GET /healthz HTTP/1.1", host: "172.17.45.5:80"
172.17.45.1 - - [13/May/2020:05:00:04 +0000] "GET /healthz HTTP/1.1" 404 154 "-" "kube-probe/1.12" "-"
2020/05/13 05:00:07 [error] 6#6: *2 open() "/usr/share/nginx/html/healthz" failed (2: No such file or directory), client: 172.17.45.1, server: localhost, request: "GET /healthz HTTP/1.1", host: "172.17.45.5:80"
172.17.45.1 - - [13/May/2020:05:00:07 +0000] "GET /healthz HTTP/1.1" 404 154 "-" "kube-probe/1.12" "-"
2020/05/13 05:00:10 [error] 6#6: *3 open() "/usr/share/nginx/html/healthz" failed (2: No such file or directory), client: 172.17.45.1, server: localhost, request: "GET /healthz HTTP/1.1", host: "172.17.45.5:80"
172.17.45.1 - - [13/May/2020:05:00:10 +0000] "GET /healthz HTTP/1.1" 404 154 "-" "kube-probe/1.12" "-"

四:定義 TCP 的存活探測

第三種類型的存活探測是使用 TCP 套接字。

通過配置,kubelet 會嘗試在指定端口和容器建立套接字鏈接。

如果能建立鏈接,這個容器就被看作是健康的,如果不能則這個容器就被看作是有問題的。

4.1 編輯yaml文件

[root@master1 ~]# vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcp
  labels:
    app: liveness-tcp
spec:
  containers:
  - name: liveness-tcp
    image: nginx
    ports:
    - containerPort: 80
    readinessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 15
      periodSeconds: 20

TCP 檢測的配置和 HTTP 檢測非常相似。

這個yaml同時使用就緒和存活探測器。kubelet 會在容器啓動 5 秒後發送第一個就緒探測。這會嘗試連接 goproxy 容器的 8080 端口。如果探測成功,這個 Pod 會被標記爲就緒狀態,kubelet 將繼續每隔 10 秒運行一次檢測。

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

4.2 啓動pod

[root@master1 ~]# kubectl apply -f pod6.yaml
pod/liveness-tcp created
[root@master1 ~]# kubectl get pods -w
NAME           READY   STATUS              RESTARTS   AGE
liveness-tcp   0/1     ContainerCreating   0          10s
liveness-tcp   0/1   Running   0     17s
liveness-tcp   1/1   Running   0     29s

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

一切正常

[root@master1 ~]# kubectl describe pod liveness-tcp
    Liveness:       tcp-socket :80 delay=15s timeout=1s period=20s #success=1 #failure=3
    Readiness:      tcp-socket :80 delay=5s timeout=1s period=10s #success=1 #failure=3

4.4 刪掉pod,修改yaml文件

[root@master1 ~]# kubectl delete -f pod6.yaml 
pod "liveness-tcp" deleted
[root@master1 ~]# vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcp
  labels:
    app: liveness-tcp
spec:
  containers:
  - name: liveness-tcp
    image: nginx
    ports:
    - containerPort: 80
    args:
    - /bin/sh
    - -c
    - sleep 20;killall -3 nginx
    readinessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 15
      periodSeconds: 20

4.5 重新生成pod

[root@master1 ~]# kubectl apply -f pod6.yaml
pod/liveness-tcp created
[root@master1 ~]# kubectl get pods -w
NAME           READY   STATUS              RESTARTS   AGE
liveness-tcp   0/1     ContainerCreating   0          4s
liveness-tcp   0/1   Running   0     6s
liveness-tcp   0/1   Error   0     26s
liveness-tcp   0/1   Running   1     31s
liveness-tcp   0/1   Error   1     51s
liveness-tcp   0/1   CrashLoopBackOff   1     63s
liveness-tcp   0/1   Running   2     68s
liveness-tcp   0/1   Error   2     88s
liveness-tcp   0/1   CrashLoopBackOff   2     103s
liveness-tcp   0/1   Running   3     2m
liveness-tcp   0/1   Error   3     2m20s

4.6 查看pod的describe

[root@master1 ~]# kubectl describe pod liveness-tcp
Events:
  Type     Reason     Age                   From                      Message
  ----     ------     ----                  ----                      -------
  Normal   Scheduled  2m56s                 default-scheduler         Successfully assigned default/liveness-tcp to 192.168.247.143
  Normal   Started    108s (x3 over 2m51s)  kubelet, 192.168.247.143  Started container
  Warning  Unhealthy  97s (x4 over 2m37s)   kubelet, 192.168.247.143  Readiness probe failed: dial tcp 172.17.45.2:80: connect: connection refused
  Warning  Unhealthy  93s (x2 over 2m33s)   kubelet, 192.168.247.143  Liveness probe failed: dial tcp 172.17.45.2:80: connect: connection refused
  Warning  BackOff    73s (x4 over 2m5s)    kubelet, 192.168.247.143  Back-off restarting failed container
  Normal   Pulling    61s (x4 over 2m55s)   kubelet, 192.168.247.143  pulling image "nginx"
  Normal   Pulled     56s (x4 over 2m51s)   kubelet, 192.168.247.143  Successfully pulled image "nginx"
  Normal   Created    56s (x4 over 2m51s)   kubelet, 192.168.247.143  Created container

五:總結:

後端節點是網站服務器的話,使用tcp/http進行探針訪問會導致日誌訪問流量變大

所以用exec用的多

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

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

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

六:啓動探針

備註:使用啓動探測器保護慢啓動容器

有時候,會有一些現有的應用程序在啓動時需要較多的初始化時間。

要不影響對引起探測死鎖的快速響應,這種情況下,設置存活探測參數是要技巧的。

技巧就是使用一個命令來設置啓動探測,針對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

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

七:探針參數

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

  • 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

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

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