Kubernetes クラスタ上で稼働中のコンテナアプリケーションのヘルスチェックにはいくつかの方法がある。
- コンテナのメインプロセスの死活監視
- Liveness Probe
- Readiness Probe
コンテナのメインプロセスの死活監視 では、K8S クラスタのシステムが各 Pod 中の各コンテナのメインプロセスの死活監視を実施し、プロセスが終了してコンテナが終了し、破棄されたら、コンテナを再作成して起動する。これは、Pod の restartPolicy
の設定にもよるが、いずれにしろ Pod はそのままであり、再作成はされない。これは、設定なしで勝手に K8S がやってくれるのだが、プロセスがデッドロックを起こしたりして、終了してはいないけれども、仕事をすることができない事態には対処できない。
Liveness Probe と Rediness Probe では、
- httpGet
- tcpSocket
- exec
のいずれかの方法でヘルスチェックする。httpGet は HTTP Get リクエストをコンテナに送信し、そのレスポンスの Status コードで成否を判断する。tcpSocket はコンテナのポートへの TCP 接続が成功するかどうか、exec はコンテナ中でチェックスクリプトを実行し、その終了ステータスが 0
になるかどうかで成否を判断する。
Liveness Probe は、失敗回数が閾値を超えたら、コンテナを再起動する。これで、プロセスが終了してはいないけれども仕事ができないような事態に対処できる。
Readiness Probe は、失敗回数が閾値を超えたら、対象となっている Pod 中のポートへリクエストが到達できないようにするものであり、Service リソースオブジェクトと組み合わせて使うものだ。コンテナが再起動されることはなく、正常な状態に戻ったら、また Service リソースオブジェクトからリクエストが転送されるようになる。これにより、アプリケーションが過負荷になったりして一時的にリクエストを処理できなくなった場合に対処できるようになる。
以下では、
- コンテナのメインプロセスの死活監視
- Liveness Probe
- Readiness Probe
の3つについて実験する。
kubectl コマンドで、次のように実験用の Pod を起動する。
$ kubectl run test-health-check --image="fortunefield/linux-gcc:ubuntu-22.04" --command -- sleep 10
この Pod test-health-check 内にはコンテナが1つ存在し、そのメインプロセスは 10 秒後に終了する。したがって、10秒ごとにコンテナが再起動されるのを確認できる。
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 0 3s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 0 10s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 0/1 Completed 0 12s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 1 (2s ago) 13s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 1 (8s ago) 19s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 1 (10s ago) 21s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 0/1 Completed 1 (13s ago) 24s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 0/1 Completed 1 (18s ago) 29s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 0/1 Completed 1 (21s ago) 32s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 0/1 CrashLoopBackOff 1 (14s ago) 36s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 2 (17s ago) 39s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 2 (20s ago) 42s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 0/1 CrashLoopBackOff 2 (23s ago) 69s 10.1.0.82 docker-desktop <none> <none>
$ kubectl get pods test-health-check -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-health-check 1/1 Running 3 (31s ago) 77s 10.1.0.82 docker-desktop <none> <none>
10 秒という短い時間でプロセスが終了するので、コンテナを再起動するときに Pod の STATUS が CrashLoopBackOff になり、再起動されるまでの間隔が大きくなる。
オプションに --restart=Never
を指定すると、コンテナのメインプロセスが終了しても、コンテナを再作成して再起動しなくなる。デフォルトは、Always
なので上のような挙動になるのだ。
kubectl コマンドで Deployment リソースオブジェクトを作成する。Pod にしないのは、実行中にマニフェストを変更するためだ。
$ kubectl create deployment kuard --image=gcr.io/kuar-demo/kuard-amd64:1 --replicas=1
deployment.apps/kuard created
次に、kubectl edit コマンドにより、マニフェストファイルを動的に変更して、Liveness Probe の設定を追加する。
$ kubectl edit deployment kuard
kuard Deployment リソースオブジェクトのマニフェストファイルを編集するエディタが開くので、そこで Liveness Probe と次でやる Readiness Probe のための設定を追加する。設定はコンテナごとにやるので、``spec.template.spec.containers` の中に記述する。
livenessProbe:
httpGet:
path: /healthy
port: 8080
initialDelaySeconds: 5
timeoutSeconds: 1
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 0
timeoutSeconds: 1
periodSeconds: 2
failureThreshold: 3
successThreshold: 1
マニフェストの変更により、kuard
Deployment オブジェクトの中の Pod が再作成される。次に、ローカルからこの Pod にアクセスできるようにするためにポートフォワードを実行する。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kuard-9b7598687-pgnhm 1/1 Running 0 13s
$ kubectl port-forward kuard-9b7598687-pgnhm 5555:8080
Forwarding from 127.0.0.1:5555 -> 8080
Forwarding from [::1]:5555 -> 8080
kuard Deployment オブジェクト内には、kuard-9b7598687-pgnhm
Pod オブジェクトがあり、その中のコンテナアプリケーションが 8080 ポートを Listen している。上では、ローカルの 5555 番ポートをその 8080 ポートへとフォワードしている。これにより、http://localhost:5555 で Pod のコンテナアプリケーションへリクエストできる。
http://localhost:5555 へとアクセスして、コンテナの Web アプリケーションにリクエストしてページを表示する。そこで、Liveness Probe
のタブを選択すると、ヘルスチェックのためのリクエストがマニフェストに追加設定したとおりの間隔でやってきていることがわかる。Status コード 200 を返しているが、このコンテナアプリケーションが /healthy
パスに対して、そのレスポンスを返すようにしているからだ。ここで、
Fail
のリンクをクリックしてやると、コンテナアプリケーションは /healthy
パスへのリクエストに対し、Status コード 500 を返すようになるので、ヘルスチェックは失敗したとみなされるようになる。失敗回数が閾値(failureThreshold
)に達すると、K8S クラスタシステムは、このコンテナを再起動させる。したがって、再起動後に Pod を確認すると、
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
kuard-9b7598687-pgnhm 1/1 Running 1 (27s ago) 114m
となり、Pod オブジェクトはそのままだが、コンテナが再起動されたことが確認できる。また、
$ kubectl describe pod kuard-9b7598687-pgnhm
を実行して、この Pod の詳細情報を見てみると、その中に
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Pulled 3m35s (x2 over 117m) kubelet Container image "gcr.io/kuar-demo/kuard-amd64:1" already present on machine
Normal Created 3m35s (x2 over 117m) kubelet Created container kuard-amd64
Normal Started 3m35s (x2 over 117m) kubelet Started container kuard-amd64
Warning Unhealthy 3m35s (x3 over 3m55s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
Normal Killing 3m35s kubelet Container kuard-amd64 failed liveness probe, will be restarted
という表示がある。Liveness Probe に3回失敗し、kuard-amd64
というコンテナを再起動させたことがわかる。
同じ Web アプリケーションの表示ページの Readiness Probe のタブで、ヘルスチェックが実行されていることを確認できる。ここでも
Fail
リンクをクリックして、Readiness Probe のチェックが失敗するようにできる。Liveness のときとは違い、失敗回数の閾値に達しても、コンテナの再起動は起こらない。この実験ではやっていないが、連携する Service リソースオブジェクトがあれば、そこからのリクエストがやってこなくなる。その間に Readiness Probe のチェックが成功するようになり、成功回数の閾値に到達すれば、再び Service リソースオブジェクトからのリクエストが転送されてくるようになる。