Ritolabo
  1. Home
  2. Kubernetes
  3. Probe: Kubernetes でコンテナが動いているかを確かめる

Probe: Kubernetes でコンテナが動いているかを確かめる

  • 公開日
  • カテゴリ:Kubernetes
  • タグ:Kubernetes,学習メモ
Probe: 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

  1. 環境
  2. Probe の種類
    1. Probe はどこにあるか
  3. Probe なしで動かす
    1. 作業用の Pod からアクセスする
    2. 届け先の一覧を見る
  4. アプリを壊す(Probe なし)
  5. readiness probe を付ける
  6. readiness probe を失敗させる
    1. 直すと戻る
  7. liveness probe を付ける
  8. liveness probe を失敗させる
  9. 2つの Probe の違い
  10. 両方を付けたときの順番
  11. startup probe
  12. 確認のしかたの種類
  13. 消す
  14. 用語メモ
  15. 参考 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-serviceService 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 endpointsliceEndpointSlice の詳細を表示する
-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
場所意味
readinessProbereadiness probe の設定。コンテナごとに書く
httpGet確認のしかた。コンテナに HTTP GET のリクエストを送り、ステータスコードが 200 以上 400 未満なら成功、それ以外は失敗
path / portリクエストを送る先。この Pod の IP の、ポート 80 の /
periodSeconds何秒ごとに確認するか。省略すると 10 秒

書いていない設定には既定値が入る。

設定既定値意味
initialDelaySeconds0コンテナの起動から、最初の確認までに待つ秒数
timeoutSeconds11回の確認で、応答を待つ秒数
failureThreshold3何回続けて失敗したら失敗と判定するか

この設定では、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 / port
  • period=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つのうち、準備できているものが0
  • STATUS は 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 は kubelet
  • x24 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
場所意味
livenessProbeliveness probe の設定。コンテナごとに書く
httpGet / path / port確認のしかたと送る先。readiness probe と同じ
periodSeconds何秒ごとに確認するか。省略すると 10 秒

書いていない設定の既定値も、readiness probe と同じ。

設定既定値意味
initialDelaySeconds0コンテナの起動から、最初の確認までに待つ秒数
timeoutSeconds11回の確認で、応答を待つ秒数
failureThreshold3何回続けて失敗したら失敗と判定するか

この設定では、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 probeliveness probe
失敗が続いたときに kubelet がすることPod の Ready を false にするコンテナを止めて、起動し直す
kubectl get pods の READY0/1 になる1/1 のまま(起動し直しの間だけ 0/1)
kubectl get pods の RESTARTS0 のまま増える
Service の届け先(EndpointSlice の Ready)false になる。アクセスが届かなくなるこの記事では確認していない
コンテナの中の壊れた状態そのまま起動し直しで元に戻る
Events: の ReasonUnhealthyUnhealthy と 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
  1. READY が 0/1 になる(readiness probe が失敗と判定。届け先から外れる)
  2. RESTARTS が 1 になる(liveness probe が失敗と判定。コンテナが起動し直される)
  3. 起動し直したコンテナで 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つを書く。

確認のしかた成功の条件
httpGetHTTP GET のステータスコードが 200 以上 400 未満
tcpSocket指定したポートに TCP で接続できる
execコンテナの中で実行したコマンドの終了コードが 0
grpcgRPC のヘルスチェックの応答が 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


[Prev] Secret: Kubernetes で機密情報を Pod に渡す

Author

rito

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級