はじめに
こんにちは!
本記事は「本気で学ぶKubernetes」シリーズの第8回です。このシリーズでは、Kubernetesの基礎から実践まで、段階的に学んでいきます。
このシリーズは、第1回から順に読むことで体系的に学べる構成にしています。
まだご覧になっていない方は、ぜひ最初からご覧ください!
前回は、KubernetesのRBAC(Role-Based Access Control)を使った権限管理について触れました。
今回は、KubernetesのProbe(ヘルスチェック)について学んでいきます。Probeを使うことで、コンテナの健康状態を監視し、異常を検知したり、準備ができていないコンテナにトラフィックを流さないようにしたりできます。
この記事は人間がKubernetesの公式ドキュメントを読み漁りながら、人間の手で書いていますのでご安心ください!
Kubernetesでの運用上の課題
Kubernetesに限らず多くのアプリケーションでは外部のデータベースを使っていたり、APIサーバーを別で作成してアクセスしていることが多いと思います。
プロセス自体は動いていても実はデッドロックが発生していて、アプリケーションが反応/応答できないという状況も考えられます。
そのため安定稼働させるためにはヘルスチェックや死活監視を行なっていくことが重要となります。
KubernetesにはProbeという仕組みが用意されていて、上記のような状況を検知し対応することが可能です。
Probeにはいくつか種類がありますので、今回はそれらについて触れていきたいと思います。
Readiness Probe
Readiness Probe は、コンテナが「トラフィックを受ける準備ができているか」を確認する機能です。
もし失敗した場合はServiceのエンドポイントから除外されますが、コンテナ自体は再起動されません。そのため起動時の初期化待ちや、一時的な過負荷でトラフィックを止めたい場合に使われるようです。
実際にReadiness Probeを設定したPodをデプロイして、挙動を確認してみたいと思います。
公式ドキュメントではregistry.k8s.io/livenessを使っており、古いイメージを使っているようでしたので今回はnginxのイメージを使って確認していこうと思います。
nginxを使ってReadiness Probeの挙動確認用のマニフェストを用意します。
apiVersion: v1
kind: Pod
metadata:
name: readiness-demo
labels:
app: readiness-demo
spec:
containers:
- name: readiness-demo
image: nginx:1.25
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 3
periodSeconds: 3
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: readiness-demo-service
spec:
selector:
app: readiness-demo
ports:
- protocol: TCP
port: 80
targetPort: 80
Readiness Probeの設定をみると、/healthzエンドポイントにHTTP GETリクエストを送っています。
initialDelaySeconds: 3は最初のProbe実行までの待機時間、periodSeconds: 3はProbeの実行間隔、failureThreshold: 3は3回失敗したら準備未完了と判定する設定です。
nginxには最初から/healthzというファイルが存在しないため、Readiness Probeは失敗します。後ほど手動でこのファイルを作成することで、Probeが成功するようになります。
デプロイして挙動を確認してみます
kubectl apply -f readiness-probe.yaml
# pod/readiness-demo created
# service/readiness-demo-service created
# Podの確認
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# readiness-demo 0/1 Running 0 15s
PodはRunning状態ですが、Readiness Probeが失敗しているためREADYが0/1になっています。
次に、Serviceのエンドポイントを確認してみます。
kubectl get endpoints readiness-demo-service
# NAME ENDPOINTS AGE
# readiness-demo-service 20s
エンドポイントが空になっています。
これはReadiness Probeが失敗しているため、ServiceはこのPodをバックエンドとして登録していない状況です。
ここでPodの詳細を見てみます。
kubectl describe pod readiness-demo
# Warning Unhealthy 3s (x7 over 21s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 404
Readiness probe failed: HTTP probe failed with statuscode: 404というメッセージが表示されていますが、/healthzファイルが存在しないため、404エラーが返されていなす。
では、コンテナ内に/healthzファイルを作成してみます。
kubectl exec readiness-demo -- bash -c "echo 'OK' > /usr/share/nginx/html/healthz"
# Podの確認
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# readiness-demo 1/1 Running 0 45s
READYが1/1に変わり、これはReadiness Probeが成功しました。
Serviceのエンドポイントも確認してみると、PodのIPアドレスが登録されていることが確認できます。
kubectl get endpoints readiness-demo-service
# NAME ENDPOINTS AGE
# readiness-demo-service 10.228.1.77:80 50s
上記のように、もしReadiness Probeが失敗してもコンテナは再起動されず、Serviceのエンドポイントから除外されるようになっています。
Probeが成功すれば、自動的にエンドポイントに追加されますので準備ができていないPodにトラフィックが送られるといったことはなくなりますね。
Liveness Probeを試してみる
Liveness Probeは、アプリケーションが正常に動作しているかを確認します。失敗すると、Kubernetesがコンテナを再起動します。
こちらもまずはマニフェストを作成します。
apiVersion: v1
kind: Pod
metadata:
name: liveness-demo
spec:
containers:
- name: liveness-demo
image: nginx:1.25
ports:
- containerPort: 80
lifecycle:
postStart:
exec:
command: ['sh', '-c', 'echo "OK" > /usr/share/nginx/html/healthz']
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 3
periodSeconds: 5
failureThreshold: 2
今回はlivenessProbeを設定しています。
このマニフェストでは、lifecycle.postStartを使ってコンテナ起動時に/healthzファイルを作成しています。
そのため、Pod起動時はLiveness Probeが成功します。後ほど手動でこのファイルを削除することで、Liveness Probeが失敗し、コンテナが再起動される様子を確認できます。
デプロイして挙動を確認してみます。
kubectl apply -f liveness-probe.yaml
# pod/liveness-demo created
Podの状態を確認してみます。。
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# liveness-demo 1/1 Running 0 10s
READYが1/1、RESTARTSは0ということで、Liveness Probeは成功していることがわかります。
/healthzファイルを削除してみると、
kubectl exec liveness-demo -- rm /usr/share/nginx/html/healthz
# Defaulted container "liveness-demo" out of: liveness-demo, setup (init)
数秒待ってから、Podの状態を監視してみます。
kubectl get pods -w
# NAME READY STATUS RESTARTS AGE
# liveness-demo 1/1 Running 0 30s
# liveness-demo 1/1 Running 1 (1s ago) 42s
RESTARTSが1に増えたことから、Liveness Probeが失敗してKubernetesがコンテナを再起動したことが分かります。
Ctrl+Cで監視を終了して、Podの詳細を確認してみます。
kubectl describe pod liveness-demo
# ・・・略・・・
# Normal Killing 28s kubelet Container liveness-demo failed liveness probe, will be restarted
今度はContainer liveness-demo failed liveness probe, will be restartedというメッセージが出ていました。
Liveness Probeが失敗した場合はKubernetesのコンテナを再起動し、新しいコンテナを起動することで正常な状態に戻そうとする挙動だということがわかります。
整理すると、用途としては以下のようなイメージでしょうか。
-
Readiness Probe
- 起動時の初期化待ち、一時的な過負荷でのトラフィック制御
-
Liveness Probe
- デッドロック、無限ループなどの異常検知
Startup Probeを試してみる
Startup Probeは、起動に時間がかかるアプリケーション専用のProbeとして用意されています。
起動に時間がかかるアプリケーションの場合、早い段階でLiveness Probeによってヘルスチェックが行われてしまうと異常だと検知されてしまう可能性があります。
そのためinitialDelaySecondsを長く設定する必要がありますが、長くしすぎると逆に起動後に異常が発生した場合の検知が遅れてしまいます。
Startup Probeで起動時と通常時で異なる設定を使い分けられるようになります。
マニフェストを作成します。
apiVersion: v1
kind: Pod
metadata:
name: startup-demo
spec:
initContainers:
- name: slow-start
image: busybox:1.36
command: ['sh', '-c', 'echo "Simulating slow startup..." && sleep 15']
- name: setup
image: nginx:1.25
command: ['sh', '-c', 'echo "OK" > /usr/share/nginx/html/healthz']
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
containers:
- name: startup-demo
image: nginx:1.25
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 2
startupProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 0
periodSeconds: 3
failureThreshold: 10
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
emptyDir: {}
このマニフェストでは、Liveness ProbeとStartup Probeの両方を設定しています。
Startup Probeの設定を見てみると、periodSeconds: 3、failureThreshold: 10としていて、3秒ごとに確認し最大10回までの失敗を許容する設定になっています。
一方で、Liveness ProbeはperiodSeconds: 5、failureThreshold: 2としているので、Startup Probe成功後は5秒ごとにチェックして、2回失敗したら再起動します。
デプロイして挙動を確認します。
kubectl apply -f startup-probe.yaml
# pod/startup-demo created
Podの状態を確認します。
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# startup-demo 0/1 Init:0/2 0 5s
Init:0/2となっていますが、これは2つのInitContainerのうち最初のものが実行中(準備中)であることが分かります。
15秒ほど待ってから再度確認してみます。
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# startup-demo 1/1 Running 0 20s
Running状態になりました。
InitContainerの完了してからメインコンテナが起動し、Startup Probeが成功したためコンテナが準備完了状態になっています。
kubectl describe podで詳細を確認してみます。
kubectl describe pod startup-demo
Conditionsセクションを見ると、以下のようになっています。
# Conditions:
# Type Status
# PodReadyToStartContainers True
# Initialized True
# Ready True
# ContainersReady True
# PodScheduled True
ReadyがTrueになっています。
上記のようにStartup Probeを使うことで、起動に時間がかかるアプリケーションでも起動中にLiveness Probeで再起動されることを防ぐことができます。
Startup Probeは起動が遅いアプリケーションに適していますが、通常のアプリケーションでは基本的にReadinessとLivenessで十分なことも多いように思います。
出典: Kubernetes公式ドキュメント - Configure Liveness, Readiness and Startup Probes
クリーンアップ
全ての挙動確認が終わったので、作成したリソースを削除しておきましょう。
kubectl delete -f readiness-probe.yaml
kubectl delete -f liveness-probe.yaml
kubectl delete -f startup-probe.yaml
まとめと次回予告
今回は、KubernetesのProbe(ヘルスチェック)について触れてきました。
以下のように使い分けをおさせておくと良いと思います。
- Readiness Probe: トラフィックを受ける準備ができているか確認
- Liveness Probe: アプリケーションが正常に動作しているか確認する
- Startup Probe: 起動が重いアプリケーション向けで、成功するまで他のProbeは待機させる
Probeは適切に設定することで、障害の自動復旧とサービスの安定化に繋げられますのでぜひ導入して可用性を高めていきましょう!
次回は、StatefulSetについて触れていきたいと思います!
それでは、また明日!