天天看點

終于對探針下手了

1. kubernetes 健康檢查

打造一個完全不出事的容器是不可能的,是以需要對容器進行健康檢查,以 check 容器是否處于可用狀态。

健康檢查可分為程序級健康檢查和業務級健康檢查兩種:

  • 程序級健康檢查即檢查容器程序是否存活,kubelet 定期通過 docker daemon 擷取所有 docker 程序的運作情況,如果發現某個容器未正常運作,則重新開機該容器。
  • 業務級健康檢查是更細粒度的健康檢查,試想容器程序處于死鎖狀态,此時容器是不可用的,但是容器程序是運作的,kubelet 不會重新開機該容器。針對這種情況 kubernetes 設計了 probe 探針來檢查容器是否可用。

probe 又分兩種:活性探針(liveness probe)和業務探針(readiness probe)。

1.1 liveness probe

以 livenss probe 為例,在啟動容器時執行一段死鎖代碼如下:

#coding:utf8
import threading
import time

num = 0
lock = threading.Lock()

def func(n):
    lock.acquire()
    print n
    if(n == 5):
        print "deadLock"
        raise Exception('deadLock')
    lock.release()

if __name__ == "__main__":
    t4 = threading.Thread(target=func, args=(5,))
    t1 = threading.Thread(target=func, args=(8,))
    t2 = threading.Thread(target=func, args=(4,))
    t3 = threading.Thread(target=func, args=(2,))

    t4.start()
    t1.start()
    t2.start()
    t3.start()
           

啟動包含該死鎖代碼的容器:

$ docker run -it centos_python2:v1 python2 /home/deadLock.py
5
deadLock here
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/threading.py", line 805, in __bootstrap_inner
    self.run()
  File "/usr/lib64/python2.7/threading.py", line 758, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/deadLock.py", line 13, in func
    raise Exception('Error: deadLock ')
Exception: Error: deadLock
           

此時容器停留在死鎖狀态,這時候容器是無法正常工作的!

kubernetes 的 liveness probe 通過三種方式對應用進行健康檢查:

  1. HTTP GET
  2. Container Exec
  3. TCP Socket

這裡僅介紹第二種方式 Container Exec(詳細資訊看 這裡),它通過在容器中檢查執行指令的退出碼來檢查容器應用是否正常,如退出碼為 0 則認為容器應用工作正常。如下所示:

[root@chunqiu ~ ]# kubectl exec -it httpd /bin/bash -n ci
bash-5.0$ /usr/bin/genapik8s --is-alive
bash-5.0$ echo $?
0

bash-5.0$ /usr/bin/genapik8s --is-alive2
unknown option: --is-alive2
bash-5.0$ echo $?
3
           

這裡展示了一個容器應用正常工作的容器,使用 genapik8s 檢查容器是否可用。對應的 helm chat 如下:

livenessProbe:
    exec:
      command:
      - /usr/bin/genapik8s
      - --is-alive
    initialDelaySeconds: 15
    periodSeconds: 5
    successThreshold: 1
    failureThreshold: 3
           

其中,initialDelaySeconds 表示容器啟動到執行健康檢查的延遲時間,延遲時間的設計是為了容器程序有時間完成必要的初始化工作,而不是在還未啟動好之前就被 kubelet 重新開機了。

同理,将 livenessProbe 應用在前面死鎖的容器,容器中執行指令的退出碼不為 0,使得 kubelet 發現到該容器無法正常工作,進而重新開機該容器。

1.2 readiness probe

如果某些容器應用隻是暫時性出了問題,而不想 kubelet 對其重新開機該怎麼辦呢?kubernetes 提供了 readiness probe 來解決這類問題。類似于 liveness probe,readiness probe 會在容器中執行檢查操作,如果檢查失敗 kubelet 不會重新開機容器,而是将容器所屬的 pod 從 endpoint 清單中删除,這樣通路 pod 的請求就會被路由到其它 pod 上。

根據這一特性,可以構造“相同”副本的 pod,一個為 active,一個為 standby。它們都通過 service 建立連接配接,service 會将請求路由到 active 的 pod,而不會路由到 standby 的 pod。

這裡僅以單一副本舉例,不深入讨論 active 和 standby pod 的情況。構造帶 readiness probe 的 pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: readinesstester
spec:
  replicas: 1
  selector:
    matchLabels:
      app: readinessprobe
  template:
    metadata:
      labels:
        app: readinessprobe
    spec:
      containers:
      - name: httpd
        image: docker-httpd:0.2.4
        readinessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 5
          periodSeconds: 5
           

container 内包含一個 readinessProbe 探針,它會執行指令 cat /tmp/healthy 檢查容器是否健康。容器中 healthy 是不存在的,執行會失敗。建立 service 如下:

apiVersion: v1
kind: Service
metadata:
  name: readinessservice
spec:
  selector:
    app: readinessprobe
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80
           

檢查 pod 狀态:

[root@chunqiu readinessProbe ]# kubectl get pods -o wide
NAME                               READY   STATUS             RESTARTS   AGE     IP              NODE
readinesstester-85bbbd7947-dzsm6   0/1     Running            0          3h34m   10.10.44.173    chunqiu-node-2

[root@chunqiu readinessProbe ]# kubectl describe pods readinesstester-85bbbd7947-dzsm6
...
Status:               Running
Controlled By:  ReplicaSet/readinesstester-85bbbd7947
Containers:
  httpd:
    ...
    State:          Running
      Started:      Sun, 30 May 2021 11:57:21 +0800
    Ready:          False
    Restart Count:  0
    Readiness:      exec [cat /tmp/healthy] delay=5s timeout=1s period=5s #success=1 #failure=3
Events:
  Type     Reason     Age                       From     Message
  ----     ------     ----                      ----     -------
  Warning  Unhealthy  3m13s (x2460 over 3h28m)  kubelet  Readiness probe failed: cat: /tmp/healthy: No such file or directory
           

這裡僅列出需要重點關注的資訊。從 pod 狀态來看, pod 并未 ready,不過 pod 并未被重新開機,且 pod 是 running 的。進一步檢視 service 對應的 pod endpoint 是否存在:

[root@chunqiu readinessProbe ]# kubectl describe service readinessservice
Name:              readinessservice
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=readinessprobe
Type:              ClusterIP
IP:                10.254.194.117
Port:              <unset>  8080/TCP
TargetPort:        80/TCP
Endpoints:
Session Affinity:  None
Events:            <none>
           

可以看到未 ready 的 pod endpoints 并未放到 service 中,雖然 pod 有自己的 ip。

2. Deployment 和 replicaSet

這一節我們這麼部署 pod 和 service 看看會發生什麼。

首先建立不帶 readinessProbe 的 pod:

[root@chunqiu readinessProbe ]# kubectl get pods -o wide
NAME                              READY   STATUS     RESTARTS   AGE     IP              NODE
readinesstester-cd586b86d-b4n7t   1/1     Running    0          26s     10.10.44.179    chunqiu-node-2

[root@chunqiu readinessProbe ]# kubectl describe service readinessservice
Name:              readinessservice
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=readinessprobe
Type:              ClusterIP
IP:                10.254.194.117
Port:              <unset>  8080/TCP
TargetPort:        80/TCP
Endpoints:         10.10.44.179:80
Session Affinity:  None
Events:            <none>
           

很正常,接着将 readinessProbe 加到 pod 内,重新配置 pod,檢視 pod 狀态:

[root@chunqiu readinessProbe ]# kubectl get pods -o wide
NAME                               READY   STATUS             RESTARTS   AGE     IP              NODE
readinesstester-85bbbd7947-xj9gv   0/1     Running            0          29s     10.10.44.161    chunqiu-node-2
readinesstester-cd586b86d-b4n7t    1/1     Running            0          3m51s   10.10.44.179    chunqiu-node-2
           

發現有兩個名為 readinesstester... 的 pod 存在,且狀态還不一緻,這是為什麼呢?

自上而下通過 Deployment 和 replicatSet 檢視 pod 幹了什麼:

[root@chunqiu readinessProbe ]# kubectl get deployments.apps
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
readinesstester   1/1     1            1           5m39s

[root@chunqiu readinessProbe ]# kubectl describe deployments.apps readinesstester
...
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  5m48s  deployment-controller  Scaled up replica set readinesstester-cd586b86d to 1
  Normal  ScalingReplicaSet  2m26s  deployment-controller  Scaled up replica set readinesstester-85bbbd7947 to 1
           

根據 Events 可以看到 deployment-controller scale up 一個 replicaSet readinesstester-85bbbd7947,由這個 replicaSet 接管新的帶 readinessProbe pod 的建立:

[root@chunqiu readinessProbe ]# kubectl get replicasets.apps
NAME                         DESIRED   CURRENT   READY   AGE
readinesstester-85bbbd7947   1         1         0       9m13s
readinesstester-cd586b86d    1         1         1       12m

[root@chunqiu readinessProbe ]# kubectl describe replicasets.apps readinesstester-85bbbd7947
Name:           readinesstester-85bbbd7947
Replicas:       1 current / 1 desired
Pods Status:    1 Running / 0 Waiting / 0 Succeeded / 0 Failed
Events:
  Type    Reason            Age    From                   Message
  ----    ------            ----   ----                   -------
  Normal  SuccessfulCreate  9m24s  replicaset-controller  Created pod: readinesstester-85bbbd7947-xj9gv
...
           

scale up 的 replicaSet 建立出 Pod,該 pod 的狀态并未 ready,導緻 deployment 的滾動更新政策一直卡在等待新 replicaSet 建立完成這裡,是以會出現兩個并行的 pod。從這個示例也可以看出 Deployment 是為了應用的更新而設計的。