Probe: Kubernetes でコンテナが動いているかを確かめる
- 公開日
- カテゴリ:Kubernetes
- タグ:Kubernetes,学習メモ

Deployment で Pod を動かすと、Kubernetes はコンテナのプロセスが生きているかを見ている。プロセスが終了すれば、コンテナを起動し直す。一方、プロセスは生きているのにアプリとしては壊れている状態、たとえばエラーしか返さない、固まって応答しない、といった状態は、そのままでは Kubernetes に伝わらない。
Probe は、コンテナの中のアプリが動いているかを、kubelet に定期的に確かめさせる設定。確かめ方と、失敗が続いたときに何をするかを、Deployment の YAML に書く。
この記事では、Probe を書いていない Pod のアプリを壊して Kubernetes がどう見るかを確認したうえで、readiness probe と liveness probe を付けて、失敗したときに何が起きるかを確認する。
この記事でやること:
- Probe なしの Pod のアプリを壊し、Kubernetes から見た状態の確認
- readiness probe の追加と、失敗したときの
READYと Service の届け先の確認 - liveness probe の追加と、失敗したときのコンテナの起動し直しの確認
- 2つの Probe の違いの確認
- 両方を付けたときの順番の確認
contents
- 環境
- Probe の種類
- Probe なしで動かす
- アプリを壊す(Probe なし)
- readiness probe を付ける
- readiness probe を失敗させる
- liveness probe を付ける
- liveness probe を失敗させる
- 2つの Probe の違い
- 両方を付けたときの順番
- startup probe
- 確認のしかたの種類
- 消す
- 用語メモ
- 参考 URL
環境
kind で作ったクラスタ(Control Plane 1台 + Worker 2台)を使う。(作り方は「Kubernetes の Pod とは?コンテナを動かす最小単位を整理する」を参照)
- 環境: macOS(Apple Silicon)、Docker Desktop 29.8.0、kind v0.33.0、Kubernetes v1.37.0、kubectl v1.36.1
Probe の種類
Probe は3種類あり、失敗が続いたときに起きることが違う。
| 種類 | 確かめること | 失敗が続いたとき |
|---|---|---|
| readiness probe | 今、アクセスを受けられるか | その Pod を Service の届け先から外す。コンテナはそのまま |
| liveness probe | 生きているか | コンテナを起動し直す |
| startup probe | 起動が終わったか | コンテナを起動し直す。起動中だけ実行される |
この記事で動かすのは readiness probe と liveness probe。startup probe は末尾で書き方だけ触れる。
Probe はどこにあるか
Probe は、動いているプログラムではなく、Deployment の YAML に書く設定。Deployment の中の Pod の定義の一部として、Control Plane で動いている etcd に保存される。(「ConfigMap: Kubernetes で Pod に渡す設定値」の「ConfigMap はどこにあるか」を参照)
実際に確認の作業(HTTP リクエストを送る、など)をするのは、その Pod が置かれた Node で動いている kubelet。失敗が続いたときにコンテナを起動し直すのも、Pod の Ready を false にするのも kubelet。
- Deployment、ReplicaSet、Service、EndpointSlice は、Control Plane の etcd に保存されたデータ。Control Plane で動いているプログラムではない
- 図のうち、Node の上にあるのは Pod だけ
- 矢印は、そのデータが何についての定義かを示す。データ自身が何かを確認したり、起動し直したりするわけではない
- Probe の確認を実行するのは、Pod が置かれた Node(図では
k8s-study-worker2)の kubelet - readiness probe の結果は、EndpointSlice の届け先ごとの
Readyに反映される。EndpointSlice を書き換えるのは、Control Plane の EndpointSlice コントローラー
Probe なしで動かす
まず、Probe を書いていない Deployment と Service を動かす。(Deployment は「Deployment: Kubernetes で Pod の数と更新を管理する」、Service は「Service: Kubernetes で Pod への宛先を固定する」を参照)
# demo-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80
# demo-service.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
selector:
app: demo-app
ports:
- port: 80
targetPort: 80
$ kubectl apply -f demo-deployment.yaml
# => deployment.apps/demo-app created
$ kubectl apply -f demo-service.yaml
# => service/demo-service created
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-app-6d69c7cc8c-njl9r 1/1 Running 0 17s 10.244.1.2 k8s-study-worker <none> <none>
作業用の Pod からアクセスする
Service にはクラスタの中からアクセスする。コマンドを打つ場所として、作業用の Pod tmp を作る。
$ kubectl run tmp --image=busybox:1.36 --restart=Never -- sleep 3600
# => pod/tmp created
tmp の中から、Service の名前でアクセスする。nginx の初期ページ(HTML)が返ってくるので、head -n 4 で最初の4行だけ表示する。
$ kubectl exec tmp -- wget -qO- http://demo-service | head -n 4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
| 部分 | 意味 |
|---|---|
kubectl exec tmp -- | Pod tmp の中で、-- の後ろのコマンドを実行する |
wget -qO- http://demo-service | Service demo-service にアクセスして、返ってきた中身を出力する |
head -n 4 | 最初の4行だけ表示する |
届け先の一覧を見る
Service の届け先の一覧(EndpointSlice)で、この Pod が Ready と記録されていることを確認する。
$ kubectl describe endpointslice -l kubernetes.io/service-name=demo-service | grep -A 2 Addresses:
- Addresses: 10.244.1.2
Conditions:
Ready: true
| 部分 | 意味 |
|---|---|
kubectl describe endpointslice | EndpointSlice の詳細を表示する |
-l kubernetes.io/service-name=demo-service | ラベル kubernetes.io/service-name が demo-service のものだけを対象にする。EndpointSlice には、どの Service のものかを示すこのラベルが自動で付く |
grep -A 2 Addresses: | Addresses: の行と、その後ろの2行だけを表示する |
Addressesの IP は、kubectl get pods -o wideのIPと同じReady: trueは、この届け先にアクセスを送ってよい、という状態
アプリを壊す(Probe なし)
プロセスは生きているのに、アプリとしては壊れている状態を作る。nginx は、/ にアクセスされると index.html の中身を返す。このファイルを、コンテナの中から消す。
$ kubectl exec deploy/demo-app -- rm /usr/share/nginx/html/index.html
nginx のプロセスは動いたまま。返すファイルがなくなったので、/ へのアクセスにはエラーを返す。
$ kubectl exec tmp -- wget -qO- http://demo-service | head -n 4
wget: server returned error: HTTP/1.1 403 Forbidden
command terminated with exit code 1
利用者から見れば壊れている。この状態を Kubernetes はどう見ているか。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-app-6d69c7cc8c-njl9r 1/1 Running 0 5m6s
tmp 1/1 Running 0 3m37s
$ kubectl describe endpointslice -l kubernetes.io/service-name=demo-service | grep -A 2 Addresses:
- Addresses: 10.244.1.2
Conditions:
Ready: true
READYは1/1、STATUSはRunning、RESTARTSは0のまま- 届け先の
Readyもtrueのまま。壊れた Pod にアクセスが届き続ける
Probe を書いていないと、Kubernetes が見ているのはコンテナのプロセスが生きているかだけ。アプリがエラーを返しているかは、Kubernetes には伝わらない。
readiness probe を付ける
Kubernetes に、GET / が成功するかを確かめさせる。まず readiness probe。Deployment の YAML のコンテナに readinessProbe を足す。
# demo-deployment-readiness.yaml(containers の部分のみ)
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
| 場所 | 意味 |
|---|---|
readinessProbe | readiness probe の設定。コンテナごとに書く |
httpGet | 確認のしかた。コンテナに HTTP GET のリクエストを送り、ステータスコードが 200 以上 400 未満なら成功、それ以外は失敗 |
path / port | リクエストを送る先。この Pod の IP の、ポート 80 の / |
periodSeconds | 何秒ごとに確認するか。省略すると 10 秒 |
書いていない設定には既定値が入る。
| 設定 | 既定値 | 意味 |
|---|---|---|
initialDelaySeconds | 0 | コンテナの起動から、最初の確認までに待つ秒数 |
timeoutSeconds | 1 | 1回の確認で、応答を待つ秒数 |
failureThreshold | 3 | 何回続けて失敗したら失敗と判定するか |
この設定では、5 秒ごとに GET / を送り、3回続けて失敗したら(約 15 秒)失敗と判定される。
$ kubectl apply -f demo-deployment-readiness.yaml
# => deployment.apps/demo-app configured
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-app-7c46747bd-sp285 1/1 Running 0 5s
tmp 1/1 Running 0 5m30s
- Pod の定義が変わったので、Pod が入れ替わった(
demo-app-6d69c7cc8c-njl9rからdemo-app-7c46747bd-sp285に) - 新しいコンテナは image から作られるので、消した
index.htmlは元に戻っている
kubectl describe pod で、設定が付いていることを確認する。
$ kubectl describe pod -l app=demo-app | grep Readiness:
Readiness: http-get http://:80/ delay=0s timeout=1s period=5s #success=1 #failure=3
http-get http://:80/が YAML のhttpGet/path/portperiod=5sがperiodSeconds。delay=0s、timeout=1s、#failure=3は既定値
readiness probe を失敗させる
同じ壊し方をして、15 秒ほど待つ。
$ kubectl exec deploy/demo-app -- rm /usr/share/nginx/html/index.html
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-app-7c46747bd-sp285 0/1 Running 0 114s
tmp 1/1 Running 0 7m19s
$ kubectl describe endpointslice -l kubernetes.io/service-name=demo-service | grep -A 2 Addresses:
- Addresses: 10.244.2.3
Conditions:
Ready: false
READYが0/1になった。Pod の中のコンテナ1つのうち、準備できているものが0STATUSはRunning、RESTARTSは0のまま。コンテナは止まっていないし、起動し直されてもいない- 届け先の
Readyがfalseになった。IP は残っているが、アクセスを送ってよい状態ではなくなった
この状態で Service にアクセスする。
$ kubectl exec tmp -- wget -qO- http://demo-service | head -n 4
wget: can't connect to remote host (10.96.237.107): Connection refused
command terminated with exit code 1
Probe なしのときは 403 Forbidden(壊れた Pod に届いて、エラーが返ってきた)だったのが、Connection refused(届け先がないので、つながらない)になった。壊れた Pod にはアクセスが届かなくなっている。今回は Pod が1つなので届け先がなくなったが、Pod が複数あれば、準備できている Pod にだけ届く。
kubectl describe pod の Events: は次のとおり。
$ kubectl describe pod -l app=demo-app
(省略)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m17s default-scheduler Successfully assigned default/demo-app-7c46747bd-sp285 to k8s-study-worker2
Normal Pulled 3m17s kubelet spec.containers{nginx}: Container image "nginx:1.16.1" already present on machine and can be accessed by the pod
Normal Created 3m17s kubelet spec.containers{nginx}: Container created
Normal Started 3m17s kubelet spec.containers{nginx}: Container started
Warning Unhealthy 3m17s kubelet spec.containers{nginx}: Readiness probe failed: Get "http://10.244.2.3:80/": dial tcp 10.244.2.3:80: connect: connection refused
Warning Unhealthy 1s (x24 over 107s) kubelet spec.containers{nginx}: Readiness probe failed: HTTP probe failed with statuscode: 403
Readiness probe failed: HTTP probe failed with statuscode: 403が、kubelet が送ったGET /に nginx が403を返した記録。Fromはkubeletx24 over 107sは、107 秒の間に 24 回。5 秒ごとに失敗し続けている- 1つ目の
Unhealthyは、コンテナが起動した直後(Startedと同じ時刻)の記録。initialDelaySecondsが0なので起動してすぐ確認が始まり、nginx がまだポート 80 を開いていなかったためconnection refusedになった
直すと戻る
readiness probe は、今アクセスを受けられるかを見ているだけ。アプリが直れば、届け先に自動で戻る。消したファイルを作り直す。
$ kubectl exec deploy/demo-app -- sh -c 'echo ok > /usr/share/nginx/html/index.html'
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-app-7c46747bd-sp285 1/1 Running 0 4m44s
tmp 1/1 Running 0 10m
$ kubectl describe endpointslice -l kubernetes.io/service-name=demo-service | grep -A 2 Addresses:
- Addresses: 10.244.2.3
Conditions:
Ready: true
$ kubectl exec tmp -- wget -qO- http://demo-service | head -n 4
# => ok
READYが1/1に、届け先のReadyがtrueに戻った- Pod の名前は
demo-app-7c46747bd-sp285のまま、RESTARTSも0のまま。コンテナは一度も起動し直されていない - アクセスすると、作り直した
index.htmlの中身(ok)が返る
liveness probe を付ける
次に liveness probe。readiness probe と比べると、キーの名前が livenessProbe に変わっただけで、確認のしかたは同じ。1つずつ見るため、このファイルには readiness probe は書いていない。
# demo-deployment-liveness.yaml(containers の部分のみ)
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
| 場所 | 意味 |
|---|---|
livenessProbe | liveness probe の設定。コンテナごとに書く |
httpGet / path / port | 確認のしかたと送る先。readiness probe と同じ |
periodSeconds | 何秒ごとに確認するか。省略すると 10 秒 |
書いていない設定の既定値も、readiness probe と同じ。
| 設定 | 既定値 | 意味 |
|---|---|---|
initialDelaySeconds | 0 | コンテナの起動から、最初の確認までに待つ秒数 |
timeoutSeconds | 1 | 1回の確認で、応答を待つ秒数 |
failureThreshold | 3 | 何回続けて失敗したら失敗と判定するか |
この設定では、5 秒ごとに GET / を送り、3回続けて失敗したら(約 15 秒)コンテナが起動し直される。
$ kubectl apply -f demo-deployment-liveness.yaml
# => deployment.apps/demo-app configured
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-app-546f5449fd-prgc5 1/1 Running 0 7s
tmp 1/1 Running 0 11m
$ kubectl describe pod -l app=demo-app | grep Liveness:
Liveness: http-get http://:80/ delay=0s timeout=1s period=5s #success=1 #failure=3
liveness probe を失敗させる
同じ壊し方をして、20 秒ほど待つ。
$ kubectl exec deploy/demo-app -- rm /usr/share/nginx/html/index.html
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-app-546f5449fd-prgc5 1/1 Running 1 (1s ago) 77s
tmp 1/1 Running 0 12m
RESTARTSが1になった。(1s ago)は、最後に起動し直されたのが1秒前READYは1/1、STATUSはRunning、Pod の名前はdemo-app-546f5449fd-prgc5のまま。Pod はそのままで、中のコンテナだけが起動し直された
$ kubectl describe pod -l app=demo-app
(省略)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 112s default-scheduler Successfully assigned default/demo-app-546f5449fd-prgc5 to k8s-study-worker
Normal Pulled 37s (x2 over 112s) kubelet spec.containers{nginx}: Container image "nginx:1.16.1" already present on machine and can be accessed by the pod
Normal Created 37s (x2 over 112s) kubelet spec.containers{nginx}: Container created
Normal Started 37s (x2 over 112s) kubelet spec.containers{nginx}: Container started
Warning Unhealthy 37s (x3 over 47s) kubelet spec.containers{nginx}: Liveness probe failed: HTTP probe failed with statuscode: 403
Normal Killing 37s kubelet spec.containers{nginx}: Container nginx failed liveness probe, will be restarted
Liveness probe failed: HTTP probe failed with statuscode: 403が3回(x3 over 47s)- 3回目と同じ時刻に
KillingのContainer nginx failed liveness probe, will be restarted。kubelet がコンテナを止めて、起動し直した記録 CreatedとStartedはx2。Scheduledは1回だけ。Pod は作り直されていない
この状態で Service にアクセスする。
$ kubectl exec tmp -- wget -qO- http://demo-service | head -n 4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
nginx の初期ページが返ってきた。消した index.html が元に戻っている。コンテナは image から作り直されるので、コンテナの中で消したファイルは、起動し直しで元に戻る。壊れた状態が起動し直せば直るものなら、liveness probe だけで自動で復旧する。
2つの Probe の違い
ここまでで確認した違い。
| readiness probe | liveness probe | |
|---|---|---|
| 失敗が続いたときに kubelet がすること | Pod の Ready を false にする | コンテナを止めて、起動し直す |
kubectl get pods の READY | 0/1 になる | 1/1 のまま(起動し直しの間だけ 0/1) |
kubectl get pods の RESTARTS | 0 のまま | 増える |
Service の届け先(EndpointSlice の Ready) | false になる。アクセスが届かなくなる | この記事では確認していない |
| コンテナの中の壊れた状態 | そのまま | 起動し直しで元に戻る |
Events: の Reason | Unhealthy | Unhealthy と Killing |
同じところ:
- 書き方は同じ(
httpGet/path/port/periodSeconds)。キーの名前がreadinessProbeかlivenessProbeかだけ - 確認を実行するのは、どちらも Pod が置かれた Node の kubelet
- 失敗の判定は、どちらも
failureThreshold(既定 3 回)続けて失敗したとき
公式ドキュメントにある使い分け。
- readiness probe は、アプリが一時的にアクセスを受けられないとき向け。起動時の読み込み中や、依存する外部サービスに接続できないときなど。アプリを止めたくはないが、リクエストも送りたくない場合
- liveness probe は、起動し直さないと直らないとき向け。デッドロックなど、アプリが動いているが処理が進まない状態を検知して起動し直す
- liveness probe は慎重に設定する。誤った設定は、高負荷のときにコンテナが起動し直され、残った Pod の負荷がさらに増える連鎖的な障害につながる
両方を付けたときの順番
公式ドキュメントでは、readiness probe と同じ確認先を liveness probe にも使い、liveness probe の failureThreshold を大きくする形が挙げられている。先に届け先から外れ、それでも直らなければ起動し直す、という順になる。
# demo-deployment-both.yaml(containers の部分のみ)
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
failureThreshold: 6
- readiness probe は 5 秒ごとに3回(約 15 秒)で失敗と判定される
- liveness probe は 5 秒ごとに6回(約 30 秒)で失敗と判定される
kubectl get pods -w で変化を表示し続けながら、同じ壊し方をする。
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
demo-app-7c7595b6d-lkvhb 1/1 Running 0 20s
tmp 1/1 Running 0 8s
demo-app-7c7595b6d-lkvhb 0/1 Running 0 36s
demo-app-7c7595b6d-lkvhb 0/1 Running 1 (0s ago) 50s
demo-app-7c7595b6d-lkvhb 1/1 Running 1 (0s ago) 50s
READYが0/1になる(readiness probe が失敗と判定。届け先から外れる)RESTARTSが1になる(liveness probe が失敗と判定。コンテナが起動し直される)- 起動し直したコンテナで readiness probe が成功して、
READYが1/1に戻る
startup probe
startup probe は、起動に時間がかかるアプリのためのもの。起動が終わる前に liveness probe が失敗して、起動し直しを繰り返すのを防ぐ。この記事では動かしていない。
- startup probe が成功するまで、liveness probe と readiness probe は実行されない
- 起動時だけ実行される。liveness probe と readiness probe は、コンテナが動いている間ずっと実行される
- 失敗し続けると、kubelet がコンテナを止める。その後はコンテナの再起動ポリシーに従う
書き方は同じで、キーの名前が startupProbe になる。failureThreshold を大きくして、起動にかかる最大の時間をまかなう。
startupProbe:
httpGet:
path: /
port: 80
periodSeconds: 10
failureThreshold: 30
この例では、10 秒 × 30 回で、最大 300 秒まで起動を待つ。
確認のしかたの種類
httpGet のほかに、確認のしかたが3つある。Probe には、このうち1つを書く。
| 確認のしかた | 成功の条件 |
|---|---|
httpGet | HTTP GET のステータスコードが 200 以上 400 未満 |
tcpSocket | 指定したポートに TCP で接続できる |
exec | コンテナの中で実行したコマンドの終了コードが 0 |
grpc | gRPC のヘルスチェックの応答が SERVING |
消す
Deployment、Service、作業用の Pod を消す。
$ kubectl delete -f demo-deployment-liveness.yaml
# => deployment.apps "demo-app" deleted from default namespace
$ kubectl delete -f demo-service.yaml
# => service "demo-service" deleted from default namespace
$ kubectl delete pod tmp
# => pod "tmp" deleted from default namespace
用語メモ
| 用語 | 読み方 | 意味 |
|---|---|---|
| Probe | プローブ | コンテナの中のアプリが動いているかを、kubelet に定期的に確かめさせる設定 |
| readiness probe | レディネス プローブ | アクセスを受けられるかの確認。失敗が続くと、Service の届け先から外れる |
| liveness probe | ライブネス プローブ | 生きているかの確認。失敗が続くと、コンテナが起動し直される |
| startup probe | スタートアップ プローブ | 起動が終わったかの確認。成功するまで、ほかの2つの Probe は実行されない |
| httpGet | エイチティーティーピー ゲット | 確認のしかたの1つ。HTTP GET を送り、ステータスコードで判定する |
| periodSeconds | ピリオド セカンズ | 確認の間隔(秒)。既定 10 秒 |
| failureThreshold | フェイリュア スレッショルド | 失敗と判定するまでの連続失敗回数。既定 3 回 |
| initialDelaySeconds | イニシャル ディレイ セカンズ | コンテナの起動から最初の確認までに待つ秒数。既定 0 秒 |
| timeoutSeconds | タイムアウト セカンズ | 1回の確認で応答を待つ秒数。既定 1 秒 |
| READY | レディ | kubectl get pods の列。Pod の中のコンテナの数のうち、準備できているものの数 |
| RESTARTS | リスターツ | kubectl get pods の列。Pod の中のコンテナが起動し直された回数 |
まとめ
- Probe を書いていないと、Kubernetes が見ているのはコンテナのプロセスが生きているかだけ。アプリがエラーを返していても、Pod は
Runningのままで、アクセスも届き続ける - Probe は、Deployment の YAML のコンテナごとに書く設定。確認を実行するのは、Pod が置かれた Node の kubelet
- readiness probe が失敗し続けると、
READYが0/1になり、Service の届け先から外れる。コンテナはそのままで、直れば自動で戻る - liveness probe が失敗し続けると、kubelet がコンテナを起動し直す。
RESTARTSが増え、Pod はそのまま。コンテナの中の壊れた状態は元に戻る - 書き方は同じで、キーの名前(
readinessProbe/livenessProbe/startupProbe)で、失敗したときに起きることが変わる - 両方を付けて liveness probe の
failureThresholdを大きくすると、先に届け先から外れ、それでも直らなければ起動し直す、という順になる
参考 URL
- [official] Liveness, Readiness, and Startup Probes | Kubernetes
- [official] Liveness Probe、Readiness ProbeおよびStartup Probeを使用する | Kubernetes
- [official] Podのライフサイクル | Kubernetes
- [official] EndpointSlice | Kubernetes

