Certified Kubernetes Application Developer (CKAD) #11 Probe: liveness・readiness・startup (exec/HTTP/TCP)

コンテナが立ち上がっているからといって、その中のアプリケーションが正常に動作しているとは限りません。プロセスは生きているのにデッドロックに陥って応答できないこともあれば、プロセスは立ち上がっていてもまだ初期化が終わっておらず、リクエストを受けてはいけない状態のこともあります。Kubernetes がこの 2 つを区別して扱う仕組みが probe です。

この記事では、コンテナが生きているか、そしてトラフィックを受ける準備ができているかを Kubernetes がどう確認するのかを扱います。probe は CKAD ドメインのうち Observability and Maintenance (15%) に属し、マニフェストを直接書くタイプの問題としてよく出題されます。概念の深さよりも 正確な YAML 形式とパラメータの意味 が点数を分けるため、例を実際に打ち込んで手に馴染ませていきます。

probe とは何か #

probe は kubelet がコンテナの健康状態を周期的に検査する診断です。kubelet は決まった周期ごとにコンテナに対して検査を実行し、その結果 (成功・失敗) に応じてコンテナを再起動したり、Service エンドポイントから外したりして対応します。

probe がないと、Kubernetes はコンテナのメインプロセスが生きているかだけを見ます。プロセスが死ねば restartPolicy に従って再起動しますが、プロセスは生きているのに応答できない ゾンビ状態 は検知できません。probe はこの隙間を埋めます。

この K8s 実務トラック #5 の記事 で Pod とコンテナの基本動作を扱ったとすれば、この記事はそのコンテナの健康をどう判定するのかへ一段踏み込みます。

3 種類の probe #

Kubernetes には目的の異なる 3 種類の probe があります。この 3 つの違いを正確に区別することがこの記事の核心です。

probe何を問うのか失敗時の動作
livenessProbeコンテナが生きているかコンテナを 再起動
readinessProbeトラフィックを受ける準備ができているかService エンドポイントから除外 (再起動しない)
startupProbe遅いアプリケーションの初期化が終わったか通過するまで他の probe を 無効化

livenessProbe #

livenessProbe は コンテナが生きているか を問います。検査が失敗すると、kubelet はコンテナを殺して restartPolicy に従って再起動します。プロセスは立ち上がっているのにデッドロックや無限ループで応答できないコンテナを自動復旧する用途です。

注意すべき点は、攻撃的すぎる liveness 設定がかえって障害を作る ことです。初期化が遅いアプリケーションに短い liveness だけをかけると、正常だがまだ準備中のコンテナを繰り返し殺して再起動する CrashLoop が発生します。

readinessProbe #

readinessProbe は トラフィックを受ける準備ができているか を問います。検査が失敗しても、kubelet はコンテナを殺さず、代わりにその Pod の IP を Service エンドポイントのリストから外します。つまりトラフィックがその Pod へ行かないようにします。検査が再び成功すれば、自動的にエンドポイントへ復帰します。

キャッシュのウォームアップ、DB 接続の確立、依存サービスの待機のように、一時的にリクエストを受けてはいけない状態 を扱うときに使います。コンテナを再起動しない点が liveness との決定的な違いです。

startupProbe #

startupProbe は 遅いアプリケーションの初期化保護 のための probe です。起動に時間がかかるレガシーアプリケーションでは、startupProbe が通過するまで liveness と readiness probe が無効化されます。おかげで長い初期化の間に liveness がコンテナを殺すことを防ぎます。

startupProbe が成功すれば、その後は liveness と readiness が正常に動作します。つまり startupProbe は 起動区間専用の安全装置 であり、一度通過すればもう実行されません。

3 種類のハンドラー #

各 probe は検査を実行する方法をハンドラーで指定します。ハンドラーは 3 種類あり、どの probe にも付けられます。

ハンドラー検査方法成功判定
execコンテナ内で command を実行終了コード 0
httpGet指定の path・port へ HTTP GET応答コード 200〜399
tcpSocket指定の port へ TCP 接続を試みる接続成立

exec #

コンテナ内でコマンドを実行し、終了コードが 0 なら成功とみなします。ファイルの存在確認や独自のヘルススクリプトのように、HTTP がないワークロードに適しています。

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

上の例は、コンテナ内に /tmp/healthy ファイルがあれば cat の終了コードが 0 になるので成功、ファイルがなければ失敗と判定します。

httpGet #

指定した path と port へ HTTP GET リクエストを送り、応答コードが 200〜399 なら成功とみなします。ウェブアプリケーションで最も一般的な probe の形式です。

readinessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
      - name: X-Probe
        value: readiness
  initialDelaySeconds: 10
  periodSeconds: 5

port は数値だけでなくコンテナポートの名前でも指定でき、httpHeaders でカスタムヘッダーを付けることもできます。HTTPS 検査が必要なら scheme: HTTPS を追加します。

tcpSocket #

指定した port へ TCP 接続が成立 すれば成功とみなします。HTTP エンドポイントがないデータベースやメッセージブローカーのように、ポートが開いているかだけ確認すればよい場合に使います。

livenessProbe:
  tcpSocket:
    port: 6379
  initialDelaySeconds: 15
  periodSeconds: 10

grpc #

gRPC サーバーなら、grpc ハンドラーで標準の gRPC health check プロトコルを使うこともできます。最新バージョンでは標準でサポートされており、形式は grpc: { port: 50051 } です。

probe パラメータ #

ハンドラーとは別に、すべての probe は検査のタイミングを調整する共通パラメータを持ちます。これらの値の意味と計算を正確に知ることが、試験でよく要求されます。

パラメータデフォルト値意味
initialDelaySeconds0コンテナ起動後、最初の検査までの待機時間
periodSeconds10検査の周期
timeoutSeconds11 回の検査が応答を待つ制限時間
successThreshold1失敗後に成功へ復帰するのに必要な連続成功回数
failureThreshold3失敗として確定するのに必要な連続失敗回数
  • initialDelaySeconds。コンテナが立ち上がったばかりのときはまだ準備ができていないことがあるので、最初の検査をこの分だけ遅らせます。liveness でこの値が小さすぎると、初期化中にコンテナを殺すことがあります。
  • periodSeconds。検査を繰り返す間隔です。短いほど速く検知できますが負荷が増えます。
  • timeoutSeconds。検査がこの時間内に応答しなければ、その検査は失敗とみなします。デフォルトの 1 秒は重いハンドラーには短いことがあります。
  • successThreshold。readiness でよく意味を持ち、liveness と startup では必ず 1 でなければなりません。
  • failureThreshold。1 回失敗してもすぐに対処せず、この回数だけ連続で失敗してはじめて失敗として確定します。

失敗までかかる時間の計算 #

liveness がコンテナを実際に再起動するまでにかかる最大時間は、次で見積もります。

最初の検査開始 = initialDelaySeconds
失敗確定      = initialDelaySeconds + periodSeconds × failureThreshold

例えば initialDelaySeconds: 10periodSeconds: 5failureThreshold: 3 なら、コンテナ起動後およそ 10 + 5 × 3 = 25 秒の時点で再起動が起こり得ます。startupProbe の failureThreshold × periodSecondsアプリケーションが起動に使える最大時間 に等しくなります。

liveness vs readiness: 試験の定番混同 #

この 2 つの違いは CKAD で最もよく混乱する箇所なので、もう一度はっきり整理します。

区分livenessProbereadinessProbe
問うこと生きているかトラフィックを受ける準備ができているか
失敗時コンテナ再起動エンドポイントから除外
コンテナを殺すか殺す殺さない
回復方法再起動後に再検査検査成功時にエンドポイントへ復帰

核心だけ覚えれば十分です。liveness の失敗は再起動、readiness の失敗はエンドポイントからの除外 です。「トラフィックルーティング」という言葉が問題に見えたら readiness、「再起動」や「復旧」が見えたら liveness と判断します。2 つを一緒に使うのが一般的で、通常は readiness が liveness より先に通過すべきです。

総合 YAML 例 #

3 つの probe を 1 つのコンテナにまとめて付けた形です。実務と試験で最もよくある組み合わせです。

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: app
      image: nginx
      ports:
        - containerPort: 80
      startupProbe:
        httpGet:
          path: /healthz
          port: 80
        failureThreshold: 30
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /ready
          port: 80
        initialDelaySeconds: 5
        periodSeconds: 5
      livenessProbe:
        httpGet:
          path: /healthz
          port: 80
        initialDelaySeconds: 15
        periodSeconds: 10
        failureThreshold: 3

このマニフェストで startupProbe は最大 30 × 10 = 300 秒まで起動を待ちます。その間に /healthz が一度 200 を返せば startup が通過し、その後から readiness と liveness が動作します。

試験で速く作る方法 #

probe 専用の generator はないので、#1 の記事 で身につけた dry-run で Pod の骨格を作ってから probe ブロックだけ追加する流れが速いです。

k run web --image=nginx --port=80 $do > web.yaml
# その後 web.yaml の containers の下に probe ブロックを追加
# フィールドのパスが分からなければ explain で確認
k explain pod.spec.containers.livenessProbe --recursive

トラブルシューティング #

probe の設定ミスは正常なアプリケーションを壊します。試験でも「この Pod はなぜこうなるのか」というタイプで出ます。

CrashLoopBackOff: liveness が過激なとき #

liveness の initialDelaySeconds が短すぎたり path・port が間違っていたりすると、正常なコンテナを繰り返し殺して CrashLoopBackOff になります。まずイベントと状態を見ます。

k describe pod web
# Events で "Liveness probe failed" と再起動の履歴を確認
k get pod web -o jsonpath='{.status.containerStatuses[0].restartCount}'

Liveness probe failed イベントが見えたら、initialDelaySeconds を増やすか、初期化が遅い場合は startupProbe を追加して起動区間を保護します。

エンドポイントの欠落: readiness が通過できないとき #

Service へトラフィックが行かなければ readiness が疑われます。readiness が失敗すると、その Pod はエンドポイントのリストに載りません。

k get endpoints my-svc
# ADDRESSES が空ならどの Pod も ready ではない
k describe pod web | grep -A3 Readiness

エンドポイントが空なのに Pod は Running なら、readiness の path・port がアプリケーションの実際のヘルスパスと合っているかから確認します。

よくある間違い #

  • liveness と readiness の path を同じにして、依存サービスの状態まで liveness が検査するようにする場合。依存サービスが一瞬死ぬと正常なコンテナまで再起動されます。liveness は自分自身だけ、readiness は依存性まで 検査するのが原則です。
  • port にコンテナが実際には開いていないポートを書く場合。tcpSocket と httpGet の両方でよくある間違いです。
  • startupProbe なしで遅いアプリケーションに短い liveness だけをかける場合。

試験ポイント #

  • 3 つの probe の失敗動作。liveness = 再起動、readiness = エンドポイントから除外、startup = 通過前は他の probe を無効化。この一行が核心です。
  • 3 つのハンドラーの成功判定。exec = 終了コード 0、httpGet = 200〜399、tcpSocket = 接続成立。
  • パラメータ 5 種 の意味とデフォルト値、そして initialDelaySeconds + periodSeconds × failureThreshold で失敗確定時間を計算する方法。
  • liveness と readiness を問う文章で区別すること。「再起動」なら liveness、「トラフィック」なら readiness。
  • k explain pod.spec.containers.livenessProbe --recursive でフィールドのパスを即座に確認すること。

まとめ #

この記事で押さえたこと:

  • probe は kubelet がコンテナの健康を周期的に検査する仕組み。プロセスの生存だけでは捕まえられないゾンビ状態を埋めます。
  • liveness (再起動) ・ readiness (エンドポイントから除外) ・ startup (起動保護) の 3 種類の目的と失敗動作。
  • exec・httpGet・tcpSocket の 3 つのハンドラーの形式と成功判定、そして grpc の一行。
  • initialDelaySeconds・periodSeconds・timeoutSeconds・successThreshold・failureThreshold の意味と失敗確定時間の計算。
  • トラブルシューティング。liveness の過激な設定による CrashLoop、readiness の未通過によるエンドポイントの欠落。

次へ: Observability #

probe でコンテナの健康を判定する方法を身につけました。probe が失敗したとき なぜ失敗したのかをのぞき込む道具 が次のテーマです。

#12 Observability: logging、kubectl debug、port-forward、ephemeral container では、kubectl logs のさまざまなオプション、死んだコンテナを診断する kubectl debug と ephemeral container、ローカルから Pod に直接つなぐ kubectl port-forward まで、実技でトラブルシューティングの点数を稼ぐ道具を手に馴染ませます。

X