設定存活度、就緒度和啟動探測器

本頁說明如何設定容器的存活度、就緒度和啟動探測器。

如需關於探測器的更多資訊,請參閱存活度、就緒度和啟動探測器

kubelet 使用存活度探測器來了解何時重新啟動容器。例如,存活度探測器可以捕捉到死鎖,即應用程式正在執行,但無法取得進展的情況。在這種狀態下重新啟動容器可以幫助使應用程式更可用,儘管存在錯誤。

存活度探測器的常見模式是使用與就緒度探測器相同的低成本 HTTP 端點,但具有更高的 failureThreshold。這確保了在硬性終止之前,Pod 會被觀察到一段時間處於未就緒狀態。

kubelet 使用就緒度探測器來了解容器何時準備好開始接受流量。此訊號的一個用途是控制哪些 Pod 用作服務的後端。當 Pod 的 Ready condition 為 true 時,Pod 會被視為就緒。當 Pod 未就緒時,它會從服務負載平衡器中移除。當 Pod 的節點的 Ready 條件不為 true 時,當 Pod 的其中一個 readinessGates 為 false 時,或者當其至少一個容器未就緒時,Pod 的 Ready 條件為 false。

kubelet 使用啟動探測器來了解容器應用程式何時已啟動。如果設定了這樣的探測器,則存活度和就緒度探測器會在它成功之前不會啟動,確保這些探測器不會干擾應用程式啟動。這可以用於對啟動緩慢的容器採用存活度檢查,避免它們在啟動並執行之前被 kubelet 終止。

開始之前

您需要有一個 Kubernetes 叢集,並且必須設定 kubectl 命令列工具以與您的叢集通訊。建議在至少有兩個節點且這些節點不充當控制平面主機的叢集上執行本教學課程。如果您還沒有叢集,您可以使用 minikube 建立一個,或者您可以使用以下 Kubernetes playground 之一

定義存活度命令

許多長時間執行的應用程式最終會轉換為損壞的狀態,並且除非重新啟動,否則無法恢復。Kubernetes 提供存活度探測器來偵測和補救這種情況。

在本練習中,您建立一個 Pod,該 Pod 執行基於 registry.k8s.io/busybox 映像檔的容器。以下是 Pod 的組態檔

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    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"
Normal  Pulled     7s    kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
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"
Normal   Pulled     53s                kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
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 會向容器中運行的伺服器發送 HTTP GET 請求,並監聽 8080 埠。如果伺服器的 /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 socket。透過此組態,kubelet 將嘗試在指定的埠上開啟與容器的 socket 連線。如果可以建立連線,則認為容器是健康的;如果無法建立連線,則認為是失敗。

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 [stable]

如果你的應用程式實作了 gRPC 健康檢查協定,此範例示範如何設定 Kubernetes 以將其用於應用程式存活檢查。同樣地,你可以設定就緒性和啟動探測。

以下是一個範例 manifest

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 = 300 秒) 的時間完成其啟動。一旦啟動探測成功一次,存活探測就會接管,以針對容器死鎖提供快速回應。如果啟動探測從未成功,容器將在 300 秒後被殺死,並受 Pod 的 restartPolicy 約束。

定義就緒性探測

有時,應用程式會暫時無法處理流量。例如,應用程式可能需要在啟動期間載入大型資料或組態檔,或在啟動後依賴外部服務。在這種情況下,你不想殺死應用程式,但你也不想向其發送請求。Kubernetes 提供了就緒性探測來偵測和減輕這些情況。容器回報為未就緒的 Pod 不會透過 Kubernetes 服務接收流量。

就緒性探測的組態方式與存活探測類似。唯一的區別是你使用 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 狀況 設定為 false
  • terminationGracePeriodSeconds:設定 kubelet 在觸發失敗容器的關閉與強制容器執行階段停止該容器之間等待的寬限期。預設值為繼承 Pod 層級的 terminationGracePeriodSeconds 值 (如果未指定,則為 30 秒),最小值為 1。有關更多詳細資訊,請參閱 探測層級 terminationGracePeriodSeconds

HTTP 探測器

HTTP 探測器 有可以在 httpGet 上設定的其他欄位

  • host:要連線的主機名稱,預設為主機 IP。你可能更希望在 httpHeaders 中設定 "Host"。
  • scheme:用於連線到主機的協定 (HTTP 或 HTTPS)。預設為 "HTTP"。
  • path:要在 HTTP 伺服器上存取的路徑。預設為 "/"。
  • httpHeaders:要在請求中設定的自訂標頭。HTTP 允許重複的標頭。
  • port:要在容器上存取的埠的名稱或數字。數字必須在 1 到 65535 的範圍內。

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

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

  • User-Agent:預設值為 kube-probe/1.32,其中 1.32 是 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 始終會遵循它。

  • 如果你有現有的 Pod,其中設定了 terminationGracePeriodSeconds 欄位,並且你不再希望使用每個探測的終止寬限期,則必須刪除這些現有的 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 參考資料

上次修改時間為 2024 年 11 月 05 日下午 1:10 PST:刪除不必要的反斜線 (9a31b7420a)