K8S 三種探針 ReadinessProbe、LivenessProbe和StartupProbe 之探索

我是 LEE,老李,一個在 IT 行業摸爬滾打 16 年的技術老兵。

事件背景

因為 k8s 中采用大量的異步機制、以及多種對象關系設計上的解耦,當應用實例數 增加/刪除、或者應用版本發生變化觸發滾動升級時,系統并不能保證應用相關的 service、ingress 配置總是及時能完成刷新。在一些情況下,往往只是新的 Pod 完成自身初始化,系統尚未完成 Endpoint、負載均衡器等外部可達的訪問信息刷新,老得 Pod 就立即被刪除,最終造成服務短暫的額不可用,這對于生產來說是不可接受的,所以 k8s 就加入了一些存活性探針:StartupProbe、LivenessProbe、ReadinessProbe。

技術探索

POD 狀態

Pod 常見的狀態

  • Pending:掛起,我們在請求創建 pod 時,條件不滿足,調度沒有完成,沒有任何一個節點能滿足調度條件。已經創建了但是沒有適合它運行的節點叫做掛起,這其中也包含集群為容器創建網絡,或者下載鏡像的過程。
  • Running:Pod 內所有的容器都已經被創建,且至少一個容器正在處于運行狀態、正在啟動狀態或者重啟狀態。
  • Succeeded:Pod 中所以容器都執行成功后退出,并且沒有處于重啟的容器。
  • Failed:Pod 中所以容器都已退出,但是至少還有一個容器退出時為失敗狀態。
  • Unknown:未知狀態,所謂 pod 是什么狀態是 apiserver 和運行在 pod 節點的 kubelet 進行通信獲取狀態信息的,如果節點之上的 kubelet 本身出故障,那么 apiserver 就連不上 kubelet,得不到信息了,就會看 Unknown
Pod狀態輪

Pod 重啟策略

  • Always: 只要容器失效退出就重新啟動容器。
  • OnFailure: 當容器以非正常(異常)退出后才自動重新啟動容器。
  • Never: 無論容器狀態如何,都不重新啟動容器。

Pod 常見狀態轉換場景

Pod狀態轉換

探針簡介

K8S 提供了 3 種探針:

  • ReadinessProbe
  • LivenessProbe
  • StartupProbe(這個 1.16 版本增加的)

探針存在的目的

在 Kubernetes 中 Pod 是最小的計算單元,而一個 Pod 又由多個容器組成,相當于每個容器就是一個應用,應用在運行期間,可能因為某也意外情況致使程序掛掉。

那么如何監控這些容器狀態穩定性,保證服務在運行期間不會發生問題,發生問題后進行重啟等機制,就成為了重中之重的事情,考慮到這點 kubernetes 推出了活性探針機制。

有了存活性探針能保證程序在運行中如果掛掉能夠自動重啟,但是還有個經常遇到的問題,比如說,在 Kubernetes 中啟動 Pod,顯示明明 Pod 已經啟動成功,且能訪問里面的端口,但是卻返回錯誤信息。還有就是在執行滾動更新時候,總會出現一段時間,Pod 對外提供網絡訪問,但是訪問卻發生 404,這兩個原因,都是因為 Pod 已經成功啟動,但是 Pod 的的容器中應用程序還在啟動中導致,考慮到這點 Kubernetes 推出了就緒性探針機制。

  1. LivenessProbe
    存活性探針,用于判斷容器是不是健康,如果不滿足健康條件,那么 Kubelet 將根據 Pod 中設置的 restartPolicy (重啟策略)來判斷,Pod 是否要進行重啟操作。LivenessProbe 按照配置去探測 ( 進程、或者端口、或者命令執行后是否成功等等),來判斷容器是不是正常。如果探測不到,代表容器不健康(可以配置連續多少次失敗才記為不健康),則 kubelet 會殺掉該容器,并根據容器的重啟策略做相應的處理。如果未配置存活探針,則默認容器啟動為通過(Success)狀態。即探針返回的值永遠是 Success。即 Success 后 pod 狀態是 RUNING

  2. ReadinessProbe
    就緒性探針,用于判斷容器內的程序是否存活(或者說是否健康),只有程序(服務)正常, 容器開始對外提供網絡訪問(啟動完成并就緒)。容器啟動后按照 ReadinessProbe 配置進行探測,無問題后結果為成功即狀態為 Success。pod 的 READY 狀態為 true,從 0/1 變為 1/1。如果失敗繼續為 0/1,狀態為 false。若未配置就緒探針,則默認狀態容器啟動后為 Success。對于此 pod、此 pod 關聯的 Service 資源、EndPoint 的關系也將基于 Pod 的 Ready 狀態進行設置,如果 Pod 運行過程中 Ready 狀態變為 false,則系統自動從 Service 資源 關聯的 EndPoint 列表中去除此 pod,屆時 service 資源接收到 GET 請求后,kube-proxy 將一定不會把流量引入此 pod 中,通過這種機制就能防止將流量轉發到不可用的 Pod 上。如果 Pod 恢復為 Ready 狀態。將再會被加回 Endpoint 列表。kube-proxy 也將有概率通過負載機制會引入流量到此 pod 中。

  3. StartupProbe:
    StartupProbe 探針,主要解決在復雜的程序中 ReadinessProbe、LivenessProbe 探針無法更好的判斷程序是否啟動、是否存活。進而引入 StartupProbe 探針為 ReadinessProbe、LivenessProbe 探針服務。

(★) ReadinessProbe 與 LivenessProbe 的區別

  • ReadinessProbe 當檢測失敗后,將 Pod 的 IP:Port 從對應的 EndPoint 列表中刪除。
  • LivenessProbe 當檢測失敗后,將殺死容器并根據 Pod 的重啟策略來決定作出對應的措施。

(★) StartupProbe 與 ReadinessProbe、LivenessProbe 的區別

如果三個探針同時存在,先執行 StartupProbe 探針,其他兩個探針將會被暫時禁用,直到 pod 滿足 StartupProbe 探針配置的條件,其他 2 個探針啟動,如果不滿足按照規則重啟容器。

另外兩種探針在容器啟動后,會按照配置,直到容器消亡才停止探測,而 StartupProbe 探針只是在容器啟動后按照配置滿足一次后,不在進行后續的探測。

正確的 ReadinessProbe 與 LivenessProbe 使用方式

LivenessProbe 和 ReadinessProbe 兩種探針都支持下面三種探測方法:

  • ExecAction:在容器中執行指定的命令,如果執行成功,退出碼為 0 則探測成功。
  • HTTPGetAction:通過容器的 IP 地址、端口號及路徑調用 HTTP Get 方法,如果響應的狀態碼大于等于 - 200 且小于 400,則認為容器 健康。
  • TCPSocketAction:通過容器的 IP 地址和端口號執行 TCP 檢 查,如果能夠建立 TCP 連接,則表明容器健康。

探針探測結果有以下值:

  • Success:表示通過檢測。
  • Failure:表示未通過檢測。
  • Unknown:表示檢測沒有正常進行。

LivenessProbe 和 ReadinessProbe 兩種探針的相關屬性
探針(Probe)有許多可選字段,可以用來更加精確的控制 Liveness 和 Readiness 兩種探針的行為(Probe):

  • initialDelaySeconds:容器啟動后要等待多少秒后就探針開始工作,單位“秒”,默認是 0 秒,最小值是 0
  • periodSeconds:執行探測的時間間隔(單位是秒),默認為 10s,單位“秒”,最小值是 1
  • timeoutSeconds:探針執行檢測請求后,等待響應的超時時間,默認為 1s,單位“秒”,最小值是 1
  • successThreshold:探針檢測失敗后認為成功的最小連接成功次數,默認為 1s,在 Liveness 探針中必須為 1s,最小值為 1s。
  • failureThreshold:探測失敗的重試次數,重試一定次數后將認為失敗,在 readiness 探針中,Pod 會被標記為未就緒,默認為 3s,最小值為 1s

Tips:initialDelaySeconds 在 ReadinessProbe 其實可以不用配置,不配置默認 pod 剛啟動,開始進行 ReadinessProbe 探測,但那有怎么樣,除了 StartupProbe,ReadinessProbe、LivenessProbe 運行在 pod 的整個生命周期,剛啟動的時候 ReadinessProbe 檢測失敗了,只不過顯示 READY 狀態一直是 0/1,ReadinessProbe 失敗并不會導致重啟 pod,只有 StartupProbe、LivenessProbe 失敗才會重啟 pod。而等到多少 s 后,真正服務啟動后,檢查 success 成功后,READY 狀態自然正常

正確的 StartupProbe 使用方式

StartupProbe 探針支持下面三種探測方法:

  • ExecAction:在容器中執行指定的命令,如果執行成功,退出碼為 0 則探測成功。
  • HTTPGetAction:通過容器的 IP 地址、端口號及路徑調用 HTTP Get 方法,如果響應的狀態碼大于等于 200 且小于 400,則認為容器 健康。
  • TCPSocketAction:通過容器的 IP 地址和端口號執行 TCP 檢 查,如果能夠建立 TCP 連接,則表明容器健康。

探針探測結果有以下值:

  • Success:表示通過檢測。
  • Failure:表示未通過檢測。
  • Unknown:表示檢測沒有正常進行。

StartupProbe 探針屬性

  • initialDelaySeconds:容器啟動后要等待多少秒后就探針開始工作,單位“秒”,默認是 0 秒,最小值是 0
  • periodSeconds:執行探測的時間間隔(單位是秒),默認為 10s,單位“秒”,最小值是 1
  • timeoutSeconds:探針執行檢測請求后,等待響應的超時時間,默認為 1s,單位“秒”,最小值是 1
  • successThreshold:探針檢測失敗后認為成功的最小連接成功次數,默認為 1s,在 Liveness 探針中必須為 1s,最小值為 1s。
  • failureThreshold:探測失敗的重試次數,重試一定次數后將認為失敗,在 readiness 探針中,Pod 會被標記為未就緒,默認為 3s,最小值為 1s

Tips:在 StartupProbe 執行完之后,其他 2 種探針的所有配置才全部啟動,相當于容器剛啟動的時候,所以其他 2 種探針如果配置了 initialDelaySeconds,建議不要給太長。

使用舉例

LivenessProbe 探針使用示例

1. 通過 exec 方式做健康探測

[root@localhost ~]# vim liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
    name: liveness-exec
    labels:
        app: liveness
spec:
    containers:
        - name: liveness
          image: busybox
          args: #創建測試探針探測的文件
              - /bin/sh
              - -c
              - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
          LivenessProbe:
              initialDelaySeconds: 10 #延遲檢測時間
              periodSeconds: 5 #檢測時間間隔
              exec: #使用命令檢查
                  command: #指令,類似于運行命令sh
                      - cat #sh 后的第一個內容,直到需要輸入空格,變成下一行
                      - /tmp/healthy #由于不能輸入空格,需要另外聲明,結果為sh cat"空格"/tmp/healthy

思路整理:

容器在初始化后,執行(/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600")首先創建一個 /tmp/healthy 文件,然后執行睡眠命令,睡眠 30 秒,到時間后執行刪除 /tmp/healthy 文件命令。

而設置的存活探針檢檢測方式為執行 shell 命令,用 cat 命令輸出 healthy 文件的內容,如果能成功執行這條命令一次(默認 successThreshold:1),存活探針就認為探測成功,由于沒有配置(failureThreshold、timeoutSeconds),所以執行(cat /tmp/healthy)并只等待 1s,如果 1s 內執行后返回失敗,探測失敗。

在前 30 秒內,由于文件存在,所以存活探針探測時執行 cat /tmp/healthy 命令成功執行。30 秒后 healthy 文件被刪除,所以執行命令失敗,Kubernetes 會根據 Pod 設置的重啟策略來判斷,是否重啟 Pod。

2. 通過 HTTP 方式做健康探測

[root@localhost ~]# vi liveness-http.yaml
apiVersion: v1
kind: Pod
metadata:
    name: liveness-http
    labels:
        test: liveness
spec:
    containers:
        - name: liveness
          image: test.com/test-http-prober:v0.0.1
          LivenessProbe:
              failureThreshold: 5 #檢測失敗5次表示未就緒
              initialDelaySeconds: 20 #延遲加載時間
              periodSeconds: 10 #重試時間間隔
              timeoutSeconds: 5 #超時時間設置
              successThreshold: 2 #檢查成功為2次表示就緒
              httpGet:
                  scheme: HTTP
                  port: 8081
                  path: /ping

思路整理:

在 pod 啟動后,初始化等待 20s 后,LivenessProbe 開始工作,去請求 http://Pod_IP:8081/ping 接口,類似于 curl -I http://Pod_IP:8081/ping 接口,考慮到請求會有延遲(curl -I 后一直出現假死狀態),所以給這次請求操作一直持續 5s,如果 5s 內訪問返回數值在>=200 且<=400 代表第一次檢測 success,如果是其他的數值,或者 5s 后還是假死狀態,執行類似(ctrl+c)中斷,并反回 failure 失敗。

等待 10s 后,再一次的去請求 http://Pod_IP:8081/ping 接口。如果有連續的 2 次都是 success,代表無問題。如果期間有連續的 5 次都是 failure,代表有問題,直接重啟 pod,此操作會伴隨 pod 的整個生命周期。

Tips

Http Get 探測方式有如下可選的控制字段:

  • scheme: 用于連接 host 的協議,默認為 HTTP。
  • host:要連接的主機名,默認為 Pod IP,可以在 Http Request headers 中設置 host 頭部。
  • port:容器上要訪問端口號或名稱。
  • path:http 服務器上的訪問 URI。
  • httpHeaders:自定義 HTTP 請求 headers,HTTP 允許重復 headers。

3. 通過 TCP 方式做健康探測

[root@localhost ~]# vi liveness-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
    name: liveness-tcp
    labels:
        app: liveness
spec:
    containers:
        - name: liveness
          image: nginx
          LivenessProbe:
              initialDelaySeconds: 15
              periodSeconds: 20
              tcpSocket:
                  port: 80

思路整理:

TCP 檢查方式和 HTTP 檢查方式非常相似,在容器啟動 initialDelaySeconds 參數設定的時間后,kubelet 將發送第一個 LivenessProbe 探針,嘗試連接容器的 80 端口,類似于 telnet 80 端口。 每隔 20 秒(periodSeconds)做探測,如果連接失敗則將殺死 Pod 重啟容器。

ReadinessProbe 探針使用示例

ReadinessProbe 探針使用方式和 LivenessProbe 探針探測方法一樣,也是支持三種,只是一個是用于探測應用的存活,一個是判斷是否對外提供流量的條件。

[root@localhost ~]# vim readiness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
    name: readiness-exec
    labels:
        app: readiness-exec
spec:
    containers:
        - name: readiness-exec
          image: busybox
          args: #創建測試探針探測的文件
              - /bin/sh
              - -c
              - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
          LivenessProbe:
              initialDelaySeconds: 10
              periodSeconds: 5
              exec:
                  command:
                      - cat
                      - /tmp/healthy
---
apiVersion: v1
kind: Pod
metadata:
    name: readiness-http
    labels:
        app: readiness-http
spec:
    containers:
        - name: readiness-http
          image: test.com/test-http-prober:v0.0.1
          ports:
              - name: server
                containerPort: 8080
              - name: management
                containerPort: 8081
          ReadinessProbe:
              initialDelaySeconds: 20
              periodSeconds: 5
              timeoutSeconds: 10
              httpGet:
                  scheme: HTTP
                  port: 8081
                  path: /ping
---
apiVersion: v1
kind: Pod
metadata:
    name: readiness-tcp
    labels:
        app: readiness-tcp
spec:
    containers:
        - name: readiness-tcp
          image: nginx
          LivenessProbe:
              initialDelaySeconds: 15
              periodSeconds: 20
              tcpSocket:
                  port: 80

這里說說 terminationGracePeriodSeconds

terminationGracePeriodSeconds 這個參數非常的重要,具體講解。請參考我的另外一篇文章《詳細解讀 Kubernetes 中 Pod 優雅退出,幫你解決大問題》, 里面有詳細的解釋,我這里說下其他的內容。

Tips: terminationGracePeriodSeconds 不能用于 ReadinessProbe,如果將它應用于 ReadinessProbe 將會被 apiserver 接口所拒絕

LivenessProbe:
    httpGet:
        path: /ping
        port: liveness-port
    failureThreshold: 1
    periodSeconds: 30
    terminationGracePeriodSeconds: 30 # 寬限時間30s

StartupProbe 探針使用示例

[root@localhost ~]# vim startup.yaml
apiVersion: v1
kind: Pod
metadata:
    name: startup
    labels:
        app: startup
spec:
    containers:
        - name: startup
          image: nginx
          StartupProbe:
              failureThreshold: 3 # 失敗閾值,連續幾次失敗才算真失敗
              initialDelaySeconds: 5 # 指定的這個秒以后才執行探測
              timeoutSeconds: 10 # 探測超時,到了超時時間探測還沒返回結果說明失敗
              periodSeconds: 5 # 每隔幾秒來運行這個
              httpGet:
                  path: /test
                  prot: 80

思路整理:

在容器啟動 initialDelaySeconds (5 秒) 參數設定的時間后,kubelet 將發送第一個 StartupProbe 探針,嘗試連接容器的 80 端口。 如果連續探測失敗沒有超過 3 次 (failureThreshold) ,且每次探測間隔為 5 秒 (periodSeconds) 和探測執行時間不超過超時時間 10 秒/每次 (timeoutSeconds),則認為探測成功,反之探測失敗,kubelet 直接殺死 Pod。

總結

通過對三種探針的探索,我們能夠得到一句話的總結:理解底層結構,能夠最大程度在可用性、安全性,持續性等方面讓 Pod 達到最佳工作狀態。 凡事沒有“銀彈”,尤其對重要的業務需要一個案例一個解決方案,希望這次的分析能提供給大家開啟一個思路之門。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容