Dev Study
← 宣言的に動かす — Kubernetes と Terraform に共通する仕組み
レッスン 9 / 12·約 8 分·第26期 宣言的に動かす

生きているかと、受けられるかは別の問い — liveness と readiness

アプリと DB の下に何が積まれているか(左が下、右が上)
  1. 物理機リージョン/AZ
  2. ハイパーバイザVM exit で捕まえる
  3. VMゲストOS
  4. コンテナnamespace / cgroup今ここ
  5. プロセスアプリ / DB

宣言した Pod が動いていることと、仕事を受けられることは別です。kubelet が同じコンテナに2つの別の問いを投げ、答えごとに別の手を打つ仕組みを見ます。

まず結論

readiness プローブは「いま通信を送ってよいか」、liveness プローブは「作り直すべきか」を尋ねます。readiness が失敗しても再起動は起きず、Pod の Ready が false になるだけです。

liveness の失敗で起きる再起動は、止めてから起こす2段の動作です。手元の実演では、TERM に応じない PID 1 のせいで、止まるまでに猶予の30秒をそのまま使いました。

? 読む前に予測

readiness プローブだけを付けた Pod で、プローブが失敗し続けるようにしました。70秒ほど後、この Pod はどうなっているでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

同じコンテナに、2つの別の問いを投げる

Pod は Kubernetes がコンテナを置いて動かす最小の単位です。宣言した Pod が Running でも、それが仕事を受けられるとは限りません。そこで kubelet(各ノードでコンテナを動かす係)が、コンテナに定期的に問いを投げます。

Based on the probe results, Kubernetes can restart unhealthy containers or stop sending traffic to containers that are not ready.

打つ手は2つあり、問いも2つに分かれています。

Liveness probes determine when to restart a container.

Readiness probes determine when a container is ready to accept traffic.

liveness は「作り直すべきか」、readiness は「いま送ってよいか」 を尋ねます。答えが「いいえ」のときの手も違います。readiness の失敗について、公式はこう書きます。

For a failed readiness probe, the kubelet continues running the container that failed checks, and also continues to run more probes; because the check failed, the kubelet sets the Ready condition on the Pod to false.

コンテナは止めません。Ready を落とすだけです。Service の送り先について、概念ページは the EndpointSlice controller removes the Pod's IP address from the EndpointSlices of all Services that match the Pod と書きます。ただし後で見るとおり、k3s v1.37.0 の実測では IP は消えず、ready=false の印つきで残りました。EndpointSlices のページは、serving 条件が Service の送り先に使うべきかを示し、Pod では this maps to the Pod's Ready condition だと書きます。2つのプローブは do not depend on each other to succeed で、互いを待ちません。

kubelet が投げる2つの問いと、失敗したときの手
kubeletコンテナに定期的に診断を投げる
問いが2つに分かれる
liveness作り直すべきか
readinessいま通信を送ってよいか
失敗が続いたとき
コンテナを止めて起こし直す止めるのに猶予期間を使う
動かしたまま Ready を false にService の送り先に使われなくなる
  • ・2つのプローブは互いの成功を待たない
  • ・LB が送り先を外す死活監視そのものは第3期で扱う
具体実物

プローブの種類だけ違う3つの Pod

k3s rancher/k3s:v1.37.0-k3s1 に、busybox:1.36 の Pod を3つ置きました。どれも起動時に /tmp/ok を作り、20秒後に消します。プローブは cat /tmp/ok を5秒ごとに実行します(periodSeconds: 5。公式の既定は10秒。failureThreshold は既定の3)。

  • ready-demo: readinessProbe だけ
  • live-demo: livenessProbe だけ
  • live-trap: livenessProbe に加え、シェルに TERM の trap を入れたもの(後で使う)

作成から71秒後と91秒後の kubectl get pods -o wide です。

kubectl get pods ready-demo live-demo live-trap -o wide(71秒後・91秒後。IP 以降の列は省略)コマンド出力
NAME READY STATUS RESTARTS AGE
ready-demo 0/1 Running 0 71s
live-demo 1/1 Running 1 (5s ago) 71s
live-trap 1/1 Running 1 (35s ago) 71s
NAME READY STATUS RESTARTS AGE
ready-demo 0/1 Running 0 91s
live-demo 1/1 Running 1 (25s ago) 91s
live-trap 1/1 Running 2 (20s ago) 91s

ready-demo は READY 0/1、RESTARTS 0 のままです。状態は ready=false restartCount=0 lastState={} conditions.Ready=False、イベントは Unhealthy Readiness probe failed が16回積み上がっただけでした。失敗し続けても止められていません。

live-demo のほうは再起動され、新しいコンテナは /tmp/ok を作り直すので READY 1/1 に戻っています。

この3つの Pod には Service を付けていませんでした。そこで別の k3s(v1.37.0+k3s1)で、ready-demo と同じ Pod に Service を付けて EndpointSlice を見直しました。

kubectl get endpointslices(jsonpath で各 endpoint の条件を表示。上が Ready の間、下が readiness 失敗後)コマンド出力
["10.42.0.5"] ready=true serving=true terminating=false
["10.42.0.5"] ready=false serving=false terminating=false

失敗後も IP は EndpointSlice に残っています。変わったのは ready と serving です。概念ページの removes を「一覧から消える」と読むと、この出力と合いません。確かめられたのは「送り先として使う印が false になる」までで、実際に通信が届かなくなるかは測っていません。

✓ ここまでの確認

実演の ready-demo は readiness が16回失敗した後も RESTARTS 0 でした。この結果の読み方として適切なのはどれですか。

抽象原理

再起動は、止めることと起こすことの2段

liveness の失敗で起きる「再起動」は、1つの操作ではありません。公式の既定値の説明は、liveness による再起動について The kubelet honors the setting of terminationGracePeriodSeconds for that container. と書き、その既定を 30 seconds if not specified とします。猶予の中で何が起きるかは、Pod の終了の節にこう書かれています。

kubelet makes requests to the container runtime to attempt to stop the containers in the pod by first sending a TERM (aka. SIGTERM) signal, with a grace period timeout, to the main process in each container.

Once the grace period has expired, the KILL signal is sent to any remaining processes, ...

つまり TERM で止まらないプロセスは、猶予を使い切るまで止まりません。 次のコンテナはその後に起きます。

もう1つ、liveness が「いいえ」と答えると、コンテナは確実に作り直されます。それが正しい手になるのは、作り直せば直る故障のときだけです。公式は注意書きでこう書きます。

Liveness probes must be configured carefully to ensure that they truly indicate unrecoverable application failure, for example a deadlock. Incorrect implementation of liveness probes can lead to cascading failures.

続けて、高負荷時の再起動、失敗するクライアント要求、残った Pod への負荷増を挙げています。「readiness で聞くべきことを liveness で聞く」と、送り先から外せば済む場面でもコンテナが作り直されます。

具体実物

TERM に応じるかどうかで、30秒が消える

live-demo のコマンドは sh -c "touch /tmp/ok; sleep 20; rm /tmp/ok; sleep 3600" で、PID 1 は sh です。live-trap は同じ sh に trap 'echo got TERM; exit 0' TERM を入れ、最後を while true; do sleep 1; done に変えました。変えたのはこの2点です。

1回目の実行ログから抜粋(live-demo の Killing、2つの Pod の開始・終了時刻、live-trap の前のコンテナのログ)コマンド出力
2026-09-27T22:51:27Z 2026-09-27T22:51:27Z 1 Killing Container c failed liveness probe, will be restarted
live-demo startedAt=2026-09-27T22:51:57Z lastTerminated.startedAt=2026-09-27T22:50:52Z finishedAt=2026-09-27T22:51:57Z exitCode=137 reason=Error
live-trap startedAt=2026-09-27T22:52:02Z lastTerminated.startedAt=2026-09-27T22:51:27Z finishedAt=2026-09-27T22:52:02Z exitCode=0 reason=Completed
got TERM

1行目は live-demo の Killing イベントです。旧コンテナの終了は22:51:57で、Killing の 30秒後、exitCode=137 reason=Error でした。

live-trap は、2回目の Killing が22:52:02で、旧コンテナの終了も新コンテナの開始も同じ22:52:02、exitCode=0 reason=Completed です。最終行は kubectl logs live-trap --previous の出力で、trap が TERM を受け取って動いた証拠です。

2回目の実行でも同じでした。live-demo は Killing 22:55:57 から新コンテナ開始 22:56:27 まで30秒、live-trap は Killing と開始がともに22:56:32です。

trap の無い sh が TERM で止まらなかった理由は、PID 1 の扱いにあります。pid_namespaces(7)(第8期でも引いたページ)はこう書きます。

... send signals to the "init" process of a child PID namespace only if the "init" process has established a handler for that signal.

SIGKILL or SIGSTOP are treated exceptionally: these signals are forcibly delivered when sent from an ancestor PID namespace.

コンテナの中の PID 1 は、その PID namespace の init です。TERM を送るランタイムはコンテナの外にいるので、ハンドラの無い sh には TERM が届かず、強制的に届く KILL で終わった、と読むと実測と整合します(ランタイムが祖先の namespace から送るという点は文書の逐語ではなく教材側の読みです)。trap はハンドラなので TERM が届き、got TERM が残りました。137 という終了コードの読み方(128+N)は第10期で扱います。ここで言えるのは、同じ liveness の失敗でも、再起動にかかる時間は中のプロセスが TERM に応じるかで変わったということです。

✓ ここまでの確認

trap の無い live-demo では、Killing から新しいコンテナの Started まで30秒かかりました。この30秒を最もよく説明しているのはどれですか。

⇄ 2つを見比べる
具体例 A

readiness の失敗(ready-demo)

失敗が16回積み上がっても、コンテナは同じまま動き続けました。変わったのは Ready だけで、lastState は空です。

状態とイベントの抜粋(ヘッダ省略)コマンド出力
ready=false restartCount=0 lastState={} conditions.Ready=False
2026-09-27T22:51:12Z 2026-09-27T22:52:22Z 16 Unhealthy Readiness probe failed: cat: can't open '/tmp/ok': No such file or directory
具体例 B

liveness の失敗(live-demo)

3回失敗した時点で Killing が出て、コンテナは止められ、新しいコンテナが起きました。新しいコンテナはファイルを作り直すので Ready は true に戻っています。

状態とイベントの抜粋(containerID を切り詰め、ヘッダ省略)コマンド出力
ready=true restartCount=1 lastState={"terminated":{"containerID":"containerd://61dbbc7c...","exitCode":137,"finishedAt":"2026-09-27T22:51:57Z","reason":"Error","startedAt":"2026-09-27T22:50:52Z"}} conditions.Ready=True
2026-09-27T22:51:27Z 2026-09-27T22:51:27Z 1 Killing Container c failed liveness probe, will be restarted

ファイルが無いという同じ事実に対して、2つの Pod の結末が逆向きに見えます。何が違ったのでしょうか。

失敗の後で Ready がどうなったかと、コンテナが入れ替わったかを分けて見てください。

この章のまとめ

  • liveness は 「作り直すべきか」、readiness は 「いま送ってよいか」。2つは互いの成功を待たない。
  • readiness の失敗ではコンテナを止めない。実演では READY 0/1、RESTARTS 0、conditions.Ready=False。EndpointSlice の IP は ready=false serving=false で残った。
  • liveness の再起動は TERM → 猶予 → KILL → 起こし直し。猶予の既定は30秒。
  • trap の無い sh では Killing から30秒後に exit 137、trap を入れると同じ秒に exit 0 で、got TERM が残った。
  • 公式は、誤った liveness は cascading failures を招くと注意している。作り直して直る故障だけを liveness で尋ねる。
  • periodSeconds 5秒は実演の設定(既定10秒)、failureThreshold 3回と猶予30秒は既定値。一般則ではない。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

liveness と readiness を両方付けた場合、2つのプローブの関係について公式文書と合うのはどれですか。

用語集(4語)
プローブ
kubelet がコンテナに対して定期的に行う診断。公式は `A probe is a diagnostic performed periodically by the kubelet on a container.` と書く。kubelet は各ノードでコンテナを動かし見張る係。
liveness プローブ
コンテナを作り直すべきかを尋ねる。`Liveness probes determine when to restart a container.` 失敗が許容回数を超えると kubelet がそのコンテナを再起動する。
readiness プローブ
通信を受けられるかを尋ねる。`Readiness probes determine when a container is ready to accept traffic.` 失敗しても kubelet はコンテナを動かしたまま Pod の Ready を false にする。EndpointSlice 上ではその Pod の ready/serving が false になり、送り先に使われなくなる。
terminationGracePeriodSeconds
TERM を送ってから KILL を送るまでの猶予。公式の既定は30秒。liveness の失敗による再起動でも、kubelet はこの値に従う。