本文へスキップ

CKAD 概念ノート: Probe と Pod lifecycle

  • kubernetes
  • ckad
  • probe
  • pod-lifecycle

基準: Kubernetes v1.35 (kind kindest/node:v1.35.8 でコマンド・YAML を確認)

目次

Pod phase

status.phase は、Pod が lifecycle のどのあたりにいるかを示す要約値。コンテナ・Pod の状態をすべて表す state machine ではない、と公式ドキュメントもはっきり書いている。

phase意味
Pendingクラスタは Pod を受け付けたが、コンテナの一つ以上がまだ実行準備前。スケジューリング待ち、イメージのダウンロード時間を含む
Runningノードに bind され、コンテナがすべて作成された。少なくとも一つが実行中、または起動・再起動中
Succeededすべてのコンテナが正常終了し、再起動しない
Failedすべてのコンテナが終了し、一つ以上が失敗 (non-zero exit またはシステムによる終了)、自動再起動の対象でもない
UnknownPod の状態を取得できない。普通はノードとの通信の問題

kubectl get pod の STATUS 列に出る CrashLoopBackOff や Terminating は phase ではない。kubectl が見せる表示用の値で、phase は Pod API のフィールド。

Pod conditions

status.conditions は、Pod が通過した、またはまだ通過していない条件のリスト。kubelet が管理するもののうち probe と直接関係するもの:

conditionTrue になるタイミング
PodScheduledノードにスケジュールされた
PodReadyToStartContainerssandbox の作成とネットワーク設定が完了 (beta、デフォルト有効)
Initializedinit container がすべて正常終了
ContainersReadyPod のすべてのコンテナが ready
Readyリクエストを受けられる。マッチする Service の load balancing pool に入る

各 condition には status (True/False/Unknown)、lastTransitionTime、reason、message などが付く。readinessGates でカスタム condition を足すと、コンテナがすべて ready で、かつ gate の condition もすべて True のときだけ Pod が ready になる。

Container state

コンテナごとの state は Waiting、Running、Terminated の三つ。kubectl describe pod <name> で確認する。

  • Waiting: Running でも Terminated でもない状態。イメージの pull や Secret の適用など、起動に必要な作業の途中。Reason フィールドが一緒に表示される
  • Running: 問題なく実行中。postStart hook があったなら、それはもう終わっている
  • Terminated: 実行を始めて、完了したか失敗した状態。reason、exit code、開始・終了時刻が見える。preStop hook はこの状態に入る前に実行される

restartPolicy

Pod の spec.restartPolicy。値は Always (デフォルト)、OnFailure、Never。app コンテナと通常の init container に適用される。

exit codeAlwaysOnFailureNever
0 (成功)再起動再起動しない再起動しない
non-zero (失敗)再起動再起動再起動しない
  • 再起動の間隔は exponential backoff: 10s、20s、40s … 最大 300s (5 分)。10 分間問題なく動くと backoff はリセット
  • backoff がかかっている間、kubectl に表示されるのが CrashLoopBackOff
  • liveness/startup probe の失敗も CrashLoopBackOff の原因になりうる
  • sidecar container (initContainers の中でコンテナ単位の restartPolicy: Always を指定したもの) は Pod の restartPolicy を無視して常に再起動
  • ワークロードごとの制約: Deployment は Always のみ、Job は OnFailure か Never のみ

Probe の種類

startup probeliveness probereadiness probe
確認するものアプリケーションの起動が終わったかコンテナを生かし続けるべきかトラフィックを受けてよいか
実行タイミング起動時のみ。成功したら終わり定期的にずっと (startup probe があればその成功後)コンテナの lifecycle の間ずっと定期的に (startup probe があればその成功後)
失敗時kubelet がコンテナを kill、restartPolicy を適用kubelet がコンテナを kill、restartPolicy を適用コンテナを not ready にし、Pod の Ready condition が False、EndpointSlice から Pod IP を外す。再起動なし
主な用途起動が遅いコンテナdeadlock のように動いてはいるが先に進めない状態ウォームアップ、一時的な過負荷、バックエンド依存の確認

startup probe があると何が変わるか

startup probe が成功するまで liveness と readiness の probe は実行されない。liveness・readiness の initialDelaySeconds も startup probe の成功後から数える。

起動にかかる時間が initialDelaySeconds + failureThreshold × periodSeconds より長いなら startup probe を使う、というのが公式ドキュメントの推奨。liveness probe と同じ endpoint を見るようにして、failureThreshold を多めに取る。liveness 側のデフォルト値はいじらない。

liveness probe は readiness を待つのか

待たない。二つの probe は互いの成功に依存しない。liveness を遅らせて始めたいなら initialDelaySeconds か startup probe を使う。

probe を定義しなかったら

その probe の結果は常に Success とみなされる。ただし readiness probe は、定義されていれば initial delay が過ぎるまで Failure。だから readiness probe がある Pod はトラフィックなしで起動し、probe が成功してからトラフィックを受け始める。

liveness probe を間違って設定すると

公式ドキュメントが caution で警告している部分。liveness probe は本当に復旧不能な状態 (deadlock など) のときだけ失敗するべき。負荷が高いときに liveness が失敗すると、コンテナが次々に再起動し、残った Pod に負荷が集中する cascading failure につながる。

問題があるとプロセスが自分で落ちる作りなら、liveness probe がなくても kubelet が restartPolicy どおりに処理する。よくあるパターンは、readiness と同じ軽い HTTP endpoint を使いつつ liveness 側の failureThreshold を高めにすること。そうすると kill の前に not ready の状態がしばらく続く。

Probe のハンドラ

一つの probe に次の四つのうちちょうど一つを指定する。

ハンドラ成功の条件メモ
execコンテナ内でコマンドを実行し、exit code 0実行のたびにプロセスを fork する。Pod の密度が高く周期が短いとノードの CPU の負担に
httpGetPod IP に GET し、ステータスコードが 200 以上 400 未満path のデフォルトは /、scheme のデフォルトは HTTP。HTTPS は証明書を検証しない
tcpSocket指定ポートへの TCP 接続が成立接続はノードから行う。host に Service 名を入れても kubelet は名前解決できない
grpcgRPC Health Checking Protocol の応答の status が SERVINGv1.27 で stable。port 必須、named port は不可

結果は Success、Failure、Unknown。Unknown は診断自体が失敗した場合なので、何もせず次のチェックに進む。

named port

container のポートに名前を付けておき、ほかの場所では番号の代わりにその名前で参照する方法。httpGet と tcpSocket の port で使える。

ports:
  - name: http # 8080 に "http" という名前を付ける
    containerPort: 8080
livenessProbe:
  httpGet:
    path: /healthz
    port: http # 番号の代わりに名前で指定
  • ポート番号は containerPort の 1 か所だけで管理する。8080 が 9090 に変わっても probe はそのまま
  • 同じ名前は Service の targetPort、NetworkPolicy の ports.port からも参照できる。Service は Pod ごとにその名前のポートを探すので、Pod 間で番号が違っても 1 つにまとめられる
  • 命名規則: 最大 15 文字、小文字・数字・-、英字を 1 文字以上含む、- で始まったり終わったりしない。1 つの Pod 内で重複不可
  • grpc ハンドラは named port を受け付けない。数字のみ

タイミングのフィールド

フィールドデフォルト最小値意味
initialDelaySeconds00コンテナ起動から最初の probe までの待ち時間
periodSeconds101probe の周期
timeoutSeconds11probe のタイムアウト
successThreshold11失敗の後に成功とみなすための連続成功回数。liveness・startup は必ず 1
failureThreshold31連続失敗がこの回数に達するとチェック全体が失敗
terminationGracePeriodSecondsPod の値を継承 (未指定なら 30)1probe の失敗でコンテナを落とすとき、SIGKILL までの待ち時間。readiness probe には設定不可 (API server が拒否)

startup probe が許す最大の起動時間は failureThreshold × periodSeconds。failureThreshold: 30、periodSeconds: 10 なら 300 秒。

例

三つの probe を一緒に使う Pod

apiVersion: v1
kind: Pod
metadata:
  name: probe-demo
spec:
  containers:
    - name: app
      image: registry.k8s.io/e2e-test-images/agnhost:2.40
      args: ["liveness"]
      ports:
        - name: http
          containerPort: 8080
      startupProbe:
        httpGet:
          path: /healthz
          port: http
        failureThreshold: 30
        periodSeconds: 10
      livenessProbe:
        httpGet:
          path: /healthz
          port: http
        periodSeconds: 10
        failureThreshold: 3
      readinessProbe:
        tcpSocket:
          port: 8080
        periodSeconds: 5
  • 起動後最大 300 秒まで startup probe が /healthz を見る。一度成功すると liveness・readiness に引き継がれる
  • イメージと args は公式ドキュメントの HTTP liveness の例から持ってきたもの。このイメージの /healthz は最初の 10 秒だけ 200 を返し、その後は 500 を返す。だからこの Pod が probe の失敗で再起動を繰り返すのは正常。v1.35 の kind クラスタで動かすと、約 40 秒後に最初の liveness 失敗による再起動が出て、再起動後は startup probe が最初の 10 秒を逃すので Startup probe failed ... statuscode: 500 のイベントがたまっていく

exec、grpc

livenessProbe:
  exec:
    command: ["cat", "/tmp/healthy"]
  initialDelaySeconds: 5
  periodSeconds: 5
livenessProbe:
  grpc:
    port: 2379
  initialDelaySeconds: 10

確認コマンド

kubectl describe pod probe-demo      # Events に Unhealthy / Killing、Liveness probe failed のメッセージ
kubectl get pod probe-demo           # READY 0/1、RESTARTS が増えるか
kubectl get pod probe-demo -o jsonpath='{.status.phase}'
kubectl get pod probe-demo -o jsonpath='{.status.conditions}'
kubectl get pod probe-demo -o jsonpath='{.status.containerStatuses[0].state}'
kubectl explain pod.spec.containers.livenessProbe

RESTARTS は、失敗したコンテナが再び running 状態に戻った瞬間に増える。

probe と init container

入門記事で例に挙げた「アプリが立ち上がる前に DB への接続を確認したい」という要件は、どちらでも解決できそうに見えて紛らわしい。動くタイミングと失敗したときの結果が違う。

init containerstartup probereadiness probe
定義する場所spec.initContainersapp コンテナの startupProbeapp コンテナの readinessProbe
実行タイミングapp コンテナの起動前、順番に一つずつapp コンテナの起動後、成功するまでapp コンテナの lifecycle の間ずっと
成功の基準最後まで実行して exit 0ハンドラが 1 回成功連続 successThreshold 回成功
失敗時Pod の restartPolicy どおりに再試行 (Always なら OnFailure として動作)。Never なら Pod 全体が失敗コンテナを kill、restartPolicy を適用トラフィックから外す、コンテナは動き続ける
関連する conditionInitialized-ContainersReady、Ready
probe 対応なし (livenessProbe、readinessProbe、startupProbe フィールド不可)--
  • 依存先の準備ができるまでアプリのプロセス自体を立ち上げないなら: init container
  • アプリは動かしたまま、依存先が切れたらトラフィックだけ外したいなら: readiness probe。公式ドキュメントも、バックエンドへの依存が強いアプリは liveness でアプリ自身の健全性を、readiness でバックエンドの可用性まで見るように勧めている
  • sidecar container は initContainers に入るが、動き続けて probe にも対応する

試験のポイント

  • probe には imperative なフラグがない。kubectl run app --image=nginx --dry-run=client -o yaml > pod.yaml で骨組みを作って、probe は YAML で足す
  • フィールドの場所は spec.containers[].livenessProbe。spec の直下ではない
  • フィールド名は livenessProbe、readinessProbe、startupProbe と camelCase。ハンドラは httpGet、tcpSocket、exec、grpc
  • liveness・startup の successThreshold に 1 以外を入れてはいけない
  • readiness の失敗では再起動は起きない。RESTARTS が増えているなら liveness か startup の方を見る
  • restartPolicy: Never の Pod で liveness が失敗すると、コンテナは kill されて二度と起動しない
  • STATUS 列の CrashLoopBackOff は phase ではない。phase を聞かれたら -o jsonpath='{.status.phase}' で確認

kubectl explain

kubectl explain pod.spec.containers.livenessProbe          # 共通フィールド: periodSeconds、failureThreshold など
kubectl explain pod.spec.containers.livenessProbe.httpGet  # path、port、httpHeaders
kubectl explain pod.spec.containers.startupProbe
kubectl explain pod.spec.containers.lifecycle              # postStart、preStop
kubectl explain pod.spec.terminationGracePeriodSeconds
kubectl explain pod.spec.restartPolicy                     # Always / OnFailure / Never

参考ドキュメント