配置 Liveness、Readiness 和 Startup 探針

此頁面展示瞭如何為容器配置存活探針、就緒探針和啟動探針。

有關探針的更多資訊,請參閱存活探針、就緒探針和啟動探針

kubelet 使用存活探針來知道何時重啟容器。例如,存活探針可以捕獲死鎖,即應用程式正在執行,但無法取得進展。在這種狀態下重啟容器有助於使應用程式在存在 Bug 的情況下更可用。

存活探針的常見模式是使用與就緒探針相同的低成本 HTTP 端點,但具有更高的 failureThreshold。這確保了在 Pod 被硬殺死之前,它被觀察到在一段時間內處於非就緒狀態。

kubelet 使用就緒探針來知道容器何時準備好開始接收流量。此訊號的一個用途是控制哪些 Pod 用作 Service 的後端。當 Pod 的 Ready Condition 為 true 時,它被認為是就緒的。當 Pod 未就緒時,它會從 Service 負載均衡器中移除。當 Pod 的節點的 Ready Condition 不為 true 時,當 Pod 的一個 readinessGates 為 false 時,或者當其至少一個容器未就緒時,Pod 的 Ready Condition 為 false。

kubelet 使用啟動探針來知道容器應用程式何時啟動。如果配置了此類探針,則存活探針和就緒探針不會開始,直到它成功,確保這些探針不會干擾應用程式啟動。這可用於在啟動緩慢的容器上採用存活檢查,避免它們在啟動並執行之前被 kubelet 殺死。

準備工作

你需要有一個 Kubernetes 叢集,並且 kubectl 命令列工具已配置為與你的叢集通訊。建議在至少有兩個不作為控制平面主機的節點上執行本教程。如果你還沒有叢集,可以使用 minikube 建立一個,或者你可以使用這些 Kubernetes 操場之一

定義存活命令

許多長時間執行的應用程式最終會進入損壞狀態,除了重啟之外無法恢復。Kubernetes 提供了存活探針來檢測和補救此類情況。

在本練習中,你將建立一個 Pod,它執行一個基於 registry.k8s.io/busybox:1.27.2 映象的容器。以下是 Pod 的配置檔案

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox:1.27.2
    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

在配置檔案中,你可以看到 Pod 有一個 ContainerperiodSeconds 欄位指定 kubelet 應該每 5 秒執行一次存活探針。initialDelaySeconds 欄位告訴 kubelet 它應該等待 5 秒鐘,然後執行第一次探針。為了執行探針,kubelet 在目標容器中執行命令 cat /tmp/healthy。如果命令成功,它返回 0,kubelet 認為容器是存活且健康的。如果命令返回非零值,kubelet 會殺死容器並重新啟動它。

容器啟動時,它會執行此命令

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

在容器生命的前 30 秒,存在一個 /tmp/healthy 檔案。因此,在前 30 秒內,命令 cat /tmp/healthy 返回成功程式碼。30 秒後,cat /tmp/healthy 返回失敗程式碼。

建立 Pod

kubectl apply -f https://k8s.io/examples/pods/probe/exec-liveness.yaml

在 30 秒內,檢視 Pod 事件

kubectl describe pod liveness-exec

輸出表明尚未有存活探針失敗

Type    Reason     Age   From               Message
----    ------     ----  ----               -------
Normal  Scheduled  11s   default-scheduler  Successfully assigned default/liveness-exec to node01
Normal  Pulling    9s    kubelet, node01    Pulling image "registry.k8s.io/busybox:1.27.2"
Normal  Pulled     7s    kubelet, node01    Successfully pulled image "registry.k8s.io/busybox:1.27.2"
Normal  Created    7s    kubelet, node01    Created container liveness
Normal  Started    7s    kubelet, node01    Started container liveness

35 秒後,再次檢視 Pod 事件

kubectl describe pod liveness-exec

在輸出底部,有訊息指示存活探針已失敗,並且失敗的容器已被殺死並重新建立。

Type     Reason     Age                From               Message
----     ------     ----               ----               -------
Normal   Scheduled  57s                default-scheduler  Successfully assigned default/liveness-exec to node01
Normal   Pulling    55s                kubelet, node01    Pulling image "registry.k8s.io/busybox:1.27.2"
Normal   Pulled     53s                kubelet, node01    Successfully pulled image "registry.k8s.io/busybox:1.27.2"
Normal   Created    53s                kubelet, node01    Created container liveness
Normal   Started    53s                kubelet, node01    Started container liveness
Warning  Unhealthy  10s (x3 over 20s)  kubelet, node01    Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal   Killing    10s                kubelet, node01    Container liveness failed liveness probe, will be restarted

再等待 30 秒,並驗證容器已重新啟動

kubectl get pod liveness-exec

輸出顯示 RESTARTS 已增加。請注意,RESTARTS 計數器在失敗的容器返回到執行狀態後立即增加

NAME            READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m

定義存活 HTTP 請求

另一種存活探針使用 HTTP GET 請求。以下是一個 Pod 的配置檔案,該 Pod 執行一個基於 registry.k8s.io/e2e-test-images/agnhost 映象的容器。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    args:
    - liveness
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: 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 將殺死並重新啟動容器。

要嘗試 HTTP 存活檢查,請建立一個 Pod

kubectl apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml

10 秒後,檢視 Pod 事件以驗證存活探針已失敗且容器已重新啟動

kubectl describe pod liveness-http

在 v1.13 之後的版本中,本地 HTTP 代理環境變數設定不會影響 HTTP 存活探針。

定義 TCP 存活探針

第三種類型的存活探針使用 TCP 套接字。透過此配置,kubelet 將嘗試在指定埠上開啟與容器的套接字。如果能夠建立連線,則容器被認為是健康的;如果不能,則被認為是失敗的。

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: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10

如你所見,TCP 檢查的配置與 HTTP 檢查非常相似。此示例同時使用了就緒探針和存活探針。kubelet 將在容器啟動 15 秒後執行第一個存活探針。它將嘗試連線到 goproxy 容器的 8080 埠。如果存活探針失敗,容器將重新啟動。kubelet 將每 10 秒繼續執行此檢查。

除了存活探針,此配置還包括一個就緒探針。kubelet 將在容器啟動 15 秒後執行第一個就緒探針。與存活探針類似,它將嘗試連線到 goproxy 容器的 8080 埠。如果探針成功,Pod 將被標記為就緒並接收來自服務的流量。如果就緒探針失敗,Pod 將被標記為未就緒並且不會接收來自任何服務的流量。

要嘗試 TCP 存活檢查,請建立一個 Pod

kubectl apply -f https://k8s.io/examples/pods/probe/tcp-liveness-readiness.yaml

15 秒後,檢視 Pod 事件以驗證存活探針

kubectl describe pod goproxy

定義 gRPC 存活探針

特性狀態: Kubernetes v1.27 [穩定]

如果你的應用程式實現了 gRPC 健康檢查協議,此示例展示瞭如何配置 Kubernetes 以將其用於應用程式存活檢查。同樣,你可以配置就緒探針和啟動探針。

這是一個示例清單

apiVersion: v1
kind: Pod
metadata:
  name: etcd-with-grpc
spec:
  containers:
  - name: etcd
    image: registry.k8s.io/etcd:3.5.1-0
    command: [ "/usr/local/bin/etcd", "--data-dir",  "/var/lib/etcd", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://127.0.0.1:2379", "--log-level", "debug"]
    ports:
    - containerPort: 2379
    livenessProbe:
      grpc:
        port: 2379
      initialDelaySeconds: 10

要使用 gRPC 探針,必須配置 port。如果你想區分不同型別的探針和不同功能的探針,可以使用 service 欄位。你可以將 service 設定為 liveness,並使你的 gRPC 健康檢查端點對此請求的響應與將 service 設定為 readiness 時不同。這允許你為不同型別的容器健康檢查使用相同的端點,而不是監聽兩個不同的埠。如果你想指定自己的自定義服務名稱並同時指定探針型別,Kubernetes 專案建議你使用將這些名稱連線起來的名稱。例如:myservice-liveness(使用 - 作為分隔符)。

配置問題(例如:埠或服務不正確,未實現的健康檢查協議)被視為探針失敗,類似於 HTTP 和 TCP 探針。

要嘗試 gRPC 存活檢查,請使用以下命令建立一個 Pod。在下面的示例中,etcd Pod 配置為使用 gRPC 存活探針。

kubectl apply -f https://k8s.io/examples/pods/probe/grpc-liveness.yaml

15 秒後,檢視 Pod 事件以驗證存活檢查未失敗

kubectl describe pod etcd-with-grpc

使用 gRPC 探針時,需要注意一些技術細節

  • 探針針對 Pod IP 地址或其主機名執行。請務必將你的 gRPC 端點配置為監聽 Pod 的 IP 地址。
  • 探針不支援任何身份驗證引數(如 -tls)。
  • 內建探針沒有錯誤程式碼。所有錯誤都被視為探針失敗。
  • 如果 ExecProbeTimeout 功能門設定為 false,grpc-health-probe 將**不**遵守 timeoutSeconds 設定(預設為 1 秒),而內建探針將在超時時失敗。

使用命名埠

你可以為 HTTP 和 TCP 探針使用命名 port。gRPC 探針不支援命名埠。

例如

ports:
- name: liveness-port
  containerPort: 8080

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

使用啟動探針保護啟動緩慢的容器

有時,你必須處理在首次初始化時需要額外啟動時間的應用程式。在這種情況下,很難設定存活探針引數,而不會影響死鎖的快速響應,這是此類探針的動機。解決方案是使用相同的命令、HTTP 或 TCP 檢查設定啟動探針,其中 failureThreshold * periodSeconds 足夠長以覆蓋最壞情況的啟動時間。

因此,前面的示例將變為

ports:
- name: liveness-port
  containerPort: 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 秒後被殺死並受 Pod 的 restartPolicy 約束。

定義就緒探針

有時,應用程式暫時無法提供流量。例如,應用程式可能需要在啟動期間載入大型資料或配置檔案,或者在啟動後依賴外部服務。在這種情況下,你不想殺死應用程式,但你也不想向它傳送請求。Kubernetes 提供了就緒探針來檢測和緩解這些情況。容器報告未就緒的 Pod 不會透過 Kubernetes Services 接收流量。

就緒探針的配置類似於存活探針。唯一的區別是你使用 readinessProbe 欄位而不是 livenessProbe 欄位。

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

HTTP 和 TCP 就緒探針的配置也與存活探針相同。

就緒探針和存活探針可以同時用於同一個容器。同時使用兩者可以確保流量不會到達尚未就緒的容器,並在容器失敗時重新啟動。

配置探針

探針有許多欄位,你可以使用它們更精確地控制啟動、存活和就緒檢查的行為

  • initialDelaySeconds:容器啟動後,在啟動、存活或就緒探針啟動之前的秒數。如果定義了啟動探針,則存活和就緒探針的延遲不會開始,直到啟動探針成功。如果 periodSeconds 的值大於 initialDelaySeconds,則 initialDelaySeconds 將被忽略。預設為 0 秒。最小值為 0。
  • periodSeconds:執行探針的頻率(秒)。預設為 10 秒。最小值為 1。當容器未就緒時,ReadinessProbe 可能會在配置的 periodSeconds 間隔之外的時間執行。這是為了更快地使 Pod 就緒。
  • timeoutSeconds:探針超時前的秒數。預設為 1 秒。最小值為 1。
  • successThreshold:探針在失敗後被認為是成功的最小連續成功次數。預設為 1。對於存活探針和啟動探針,必須為 1。最小值為 1。
  • failureThreshold:探針連續失敗 failureThreshold 次後,Kubernetes 認為整體檢查已失敗:容器**未**就緒/健康/存活。預設為 3。最小值為 1。對於啟動或存活探針,如果至少有 failureThreshold 次探針失敗,Kubernetes 會將容器視為不健康並觸發該特定容器的重啟。kubelet 遵守該容器的 terminationGracePeriodSeconds 設定。對於失敗的就緒探針,kubelet 繼續執行失敗檢查的容器,並繼續執行更多探針;由於檢查失敗,kubelet 將 Pod 上的 Ready condition 設定為 false
  • terminationGracePeriodSeconds:配置一個寬限期,讓 kubelet 在觸發關閉失敗容器和強制容器執行時停止該容器之間等待。預設為繼承 Pod 級別的 terminationGracePeriodSeconds 值(如果未指定,則為 30 秒),最小值為 1。有關更多詳細資訊,請參閱探針級別的 terminationGracePeriodSeconds

HTTP 探針

HTTP 探針有可以在 httpGet 上設定的附加欄位

  • host:要連線的主機名,預設為 Pod IP。你可能希望在 httpHeaders 中設定 "Host"。
  • scheme:用於連線主機的方案(HTTP 或 HTTPS)。預設為 "HTTP"。
  • path:HTTP 伺服器上要訪問的路徑。預設為 "/"。
  • httpHeaders:要在請求中設定的自定義頭部。HTTP 允許重複頭部。
  • port:容器上要訪問的埠的名稱或編號。編號必須在 1 到 65535 的範圍內。

對於 HTTP 探針,kubelet 向指定的埠和路徑傳送 HTTP 請求以執行檢查。kubelet 將探針傳送到 Pod 的 IP 地址,除非地址被 httpGet 中可選的 host 欄位覆蓋。如果 scheme 欄位設定為 HTTPS,kubelet 會發送 HTTPS 請求,跳過證書驗證。在大多數情況下,你不需要設定 host 欄位。這裡有一個你會設定它的場景。假設容器監聽 127.0.0.1,並且 Pod 的 hostNetwork 欄位為 true。那麼 httpGet 下的 host 應該設定為 127.0.0.1。如果你的 Pod 依賴於虛擬主機(這可能是更常見的情況),則不應使用 host,而應在 httpHeaders 中設定 Host 頭部。

對於 HTTP 探針,kubelet 除了強制性的 Host 頭部之外,還會發送兩個請求頭部

  • User-Agent:預設值為 kube-probe/1.34,其中 1.34 是 kubelet 的版本。
  • Accept:預設值為 */*

你可以透過為探針定義 httpHeaders 來覆蓋預設頭部。例如

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: application/json

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: MyUserAgent

你也可以透過將這兩個頭定義為空值來移除它們。

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: ""

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: ""

TCP 探針

對於 TCP 探針,kubelet 在節點上建立探針連線,而不是在 Pod 中,這意味著你不能在 host 引數中使用服務名稱,因為 kubelet 無法解析它。

探針級別 terminationGracePeriodSeconds

特性狀態: Kubernetes v1.28 [stable]

在 1.25 及更高版本中,使用者可以在探針規範中指定探針級別的 terminationGracePeriodSeconds。當同時設定了 Pod 級別和探針級別的 terminationGracePeriodSeconds 時,kubelet 將使用探針級別的值。

設定 terminationGracePeriodSeconds 時,請注意以下事項

  • 如果 Pod 上存在探針級別的 terminationGracePeriodSeconds 欄位,kubelet 總是會遵守它。

  • 如果你有已設定 terminationGracePeriodSeconds 欄位的現有 Pod,並且你不再希望使用每個探針的終止寬限期,則必須刪除這些現有 Pod。

例如

spec:
  terminationGracePeriodSeconds: 3600  # pod-level
  containers:
  - name: test
    image: ...

    ports:
    - name: liveness-port
      containerPort: 8080

    livenessProbe:
      httpGet:
        path: /healthz
        port: liveness-port
      failureThreshold: 1
      periodSeconds: 60
      # Override pod-level terminationGracePeriodSeconds #
      terminationGracePeriodSeconds: 60

就緒探針不能設定探針級別的 terminationGracePeriodSeconds。它將被 API 伺服器拒絕。

下一步

你還可以閱讀以下 API 參考

最後修改於 2025 年 1 月 17 日下午 1:48 PST:更新清單中的映象標籤 (7b5ca4d7a5)