ヘルスチェックとは?LivenessとReadinessの分離と閾値設計を実装目線で解説

Adureを利用したインフラ構築

ヘルスチェックを既定値のまま本番へ出すと、異常が起きてから切り離されるまで最悪65秒かかり、データベースの数十秒の不調で全台が同時に再起動する事故も起きます。この記事では、定義と検査方法の違い、LivenessとReadinessを分ける実装、Kubernetes・ALB・ECSの既定値、許容停止時間から閾値を逆算する手順を実装目線で整理しました。あえて設定しない判断と失敗パターンも条件付きで示します。

まとめ:ヘルスチェック設計で先に決める検証の深さと復帰閾値

ヘルスチェックの設計で先に決めるのは2つだけです。どこまで検証するかという検証の深さと、何回連続で失敗したら切り離すかという閾値。この2つが決まれば、KubernetesでもALBでもECSでも設定値は機械的に埋まります。

結論から示します。自プロセスが応答できるかだけを見る浅いチェックを既定にし、データベースや外部APIまで見る深いチェックはReadiness側に限定してください。Livenessに依存先の確認を入れると、共有依存が1つ落ちただけで全台が同時に再起動し、復旧をかえって遅らせる結果になります。

閾値を既定値のままにすると検知が遅れます。ALBのターゲットグループは既定でinterval 30秒・UnhealthyThresholdCount 2回のため、異常発生から切り離しまで最悪65秒。Kubernetesのlivenessは既定でperiodSeconds 10秒・failureThreshold 3回、再起動までおよそ30秒です。許容できる停止時間から逆算し、この2つの数字を先に握ってください。

ヘルスチェックの定義とping・TCP・HTTPで変わる検知範囲の差

ヘルスチェックという言葉は、ロードバランサーの機能名としても監視ツールの機能名としても使われます。まず用語の範囲と、検査方法ごとに拾える障害の差を揃えます。

ヘルスチェックの定義と死活監視・外形監視・障害切り離しとの関係

ヘルスチェックは、対象のプロセスやサービスへ外部から一定間隔で問い合わせ、応答内容から正常か異常かを判定し、その判定を経路制御や再起動といった自動処理に結びつける仕組みを指します。判定結果が自動アクションに直結する点が、人が見るための監視と分かれる境目になります。

死活監視は、対象が生きているかを継続的に確かめる行為の総称です。外形監視は、利用者と同じ経路から公開URLを叩いて応答を確かめる監視を指します。ヘルスチェックはこの2つと検査内容が重なりますが、切り離しや再起動という制御まで含みます。

ping・TCP接続・HTTP応答で検知できる障害と見逃す障害の境界

ICMPのping応答は、OSが生きていれば返ります。アプリケーションのプロセスが落ちていても成功するため、pingだけではWebサーバーの停止を検知できません。

TCP接続の確立まで見ると、プロセスがポートを待ち受けているところまで確認できます。ただしスレッドプールが枯渇して処理できない状態でも、カーネルのbacklogに積まれて接続自体は成功する点に注意が必要です。デッドロックしたJavaプロセスがTCPチェックを通り続ける事象は、ここから生まれます。

HTTPリクエストを送り、ステータスコードと応答時間まで見る方法が実務での既定です。アプリケーションのコードが実行されるところまで到達するためです。それでもHTTP 200が返るのに非同期処理が止まった状態は起こります。検知範囲は「どの層まで実際に実行させたか」で決まります。

Liveness・Readiness・Startupが担う判定の役割分担

Kubernetesは、判定の目的ごとにプローブを3種類に分けています。livenessProbeは「このコンテナは再起動しないと戻らないか」を判定し、失敗が続くとkubeletがコンテナを再起動する仕組みです。readinessProbeは「いまトラフィックを受けられるか」を判定し、失敗するとPodはEndpointsから外れますが、コンテナ自体は動き続けます。

startupProbeは起動が遅いアプリケーションのための猶予枠で、これが成功するまで他の2つの評価は始まりません。プローブの実行方式はexec・httpGet・tcpSocket・grpcの4種類あり、httpGetは200から399までを成功として扱います。gRPCのネイティブプローブは1.27系で正式版になり、アプリ側はgrpc.health.v1.Healthサービスの実装が前提になります。

3種類のKubernetes固有の設定手順は分量が多いため、Liveness Probeの設定方法とReadiness・Startupとの違いに分けてまとめています。ここでは判定の役割分担だけ押さえてください。

LivenessとReadinessを分けるヘルスチェック実装の判断基準

エンドポイントの実装で決めるのは、何を検証するかと、その結果をどう返すかの2点です。

依存先を含む深いチェックと自プロセスだけの浅いチェックの使い分け

浅いチェックは、プロセスが生きていてイベントループが応答できることだけを確かめます。深いチェックは、データベース接続・キャッシュ・外部APIの疎通まで確かめます。どちらを選ぶかの判断基準は1つで、そのチェックが失敗したときに「このインスタンスを切り離すことが復旧に寄与するか」で決めてください。

メモリリークやデッドロックはそのインスタンス固有の問題なので、切り離しや再起動が効きます。共有データベースの障害は全インスタンスで同時に失敗するため、1台を切り離しても何も改善しません。前者は浅いチェックで拾い、後者はヘルスチェックではなく監視とアラートで拾う。この線引きが設計の骨格です。

実装では、/healthzを自プロセスの浅いチェック、/readyzを起動完了と依存先の準備確認に割り当てる構成が扱いやすくなります。このパス名はKubernetes本体のエンドポイント名に揃えた慣習で、仕様ではありません。

DB接続チェックをLivenessに入れた場合に起きる連鎖再起動の実例

多い失敗が、livenessProbeにデータベース接続確認を入れた構成です。データベースが30秒だけ詰まったとします。全PodのlivenessProbeが同時に失敗し、periodSeconds 10秒・failureThreshold 3回の既定なら、30秒後に全Podがまとめて再起動されます。

再起動したPodは接続プールを張り直すため、復旧しかけたデータベースへ一斉に接続要求が飛びます。これで再び詰まり、次の30秒でまた全Podが落ちる。障害が自走する状態です。依存先の確認はreadinessProbe側へ置き、依存先が落ちても縮退して応答を返せないかを先に検討してください。

200と503の返し分けとレスポンスボディに載せる診断情報の設計

返し分けは、正常を200、異常を503 Service Unavailableにします。ロードバランサーもkubeletもステータスコードだけで判定するため、ここに凝った設計は不要です。ALBのMatcherは既定で200のみを成功とするので、204や302を返す実装にすると既定値のままでは異常判定になります。

レスポンスボディは人とデバッグのために使います。ビルドのコミットハッシュ、起動時刻、依存先ごとの判定結果をJSONで返しておくと、切り離された理由を後から追えます。ボディの内容は判定に使われないため、重いクエリの結果を載せる価値はありません。

チェック用のパスは、キャッシュを明示的に抑止してください。CDNやリバースプロキシが200をキャッシュすると、停止したインスタンスが正常のまま残ります。

Kubernetes・ALB・ECSの既定値と本番で変更すべき設定項目

既定値は「誤検知しにくいが検知が遅い」側に振ってあります。3つのプラットフォームの数字を並べます。

Kubernetesのprobe既定値と検知までにかかる最悪時間

Kubernetesのプローブは、設定を省略すると次の既定値で動きます。

設定項目 既定値 実務での目安
initialDelaySeconds 0秒 startupProbeへ寄せる
periodSeconds 10秒 5〜10秒
timeoutSeconds 1秒 p99の2〜3倍
failureThreshold 3回 2回以上を維持
successThreshold 1回 readinessのみ変更可

livenessProbeの既定値では、異常発生から再起動まで最悪でperiodSeconds 10秒×failureThreshold 3回の30秒に、直前のチェックからのずれとtimeoutSecondsが加わります。timeoutSecondsの既定1秒は短く、GCが走った瞬間や初回リクエストで失敗しやすい値です。

ALBターゲットグループの既定値と2回連続失敗で外れるまでの挙動

ALBのターゲットグループは、HealthCheckIntervalSecondsが既定30秒、HealthCheckTimeoutSecondsが5秒、UnhealthyThresholdCountが2回、HealthyThresholdCountが5回、Matcherが200です。異常が起きてから切り離されるまでは最悪で30秒×2回に5秒を足した65秒。復帰は30秒×5回で150秒かかります。設定範囲はintervalが5〜300秒、timeoutが2〜120秒、閾値が2〜10回となっています。

ALB固有の挙動として、あるターゲットグループの全ターゲットが異常になると、ロードバランサーは全ターゲットへのルーティングを続けます。フェイルオープンと呼ばれる仕様で、全滅時に「どこにも流さない」ではなく「健全性を問わず流す」を選ぶ設計です。ロードバランサーの仕組みと振り分け方式を押さえておくと、この挙動の理由が掴めます。

ECSコンテナヘルスチェックのstartPeriodと起動遅延の吸収

ECSのコンテナヘルスチェックは、タスク定義のhealthCheckに書きます。既定はinterval 30秒、timeout 5秒、retries 3回で、コマンドの終了コード0が正常、非0が異常。startPeriodは既定で無効になっており、0〜300秒の範囲で指定できます。

startPeriodを指定すると、その猶予時間内の失敗はretriesに数えられません。起動に40秒かかるアプリケーションで未設定のまま出すと、interval 30秒×retries 3回の判定に起動時間が食い込み、起動完了前に置き換えが走ります。猶予期間中に一度でも成功すれば、通常のリトライ計算へ切り替わります。

ALBのヘルスチェックとECSのコンテナヘルスチェックは別レイヤーです。前者はターゲットの登録解除を、後者はコンテナの置き換えを引き起こします。両方を設定するなら、ALB側で先に切り離してからECS側で置き換わるよう、ALBの検知を短めに寄せてください。

検知時間と誤検知率の綱引きから閾値とタイムアウトを逆算する手順

閾値は感覚で決めず、許容できる停止時間から逆算します。

interval×failureThresholdで求める最悪検知時間の見積もり

最悪検知時間は、KubernetesならperiodSeconds×failureThreshold+timeoutSeconds、ALBならHealthCheckIntervalSeconds×UnhealthyThresholdCount+HealthCheckTimeoutSecondsで概算できます。異常がチェック直後に起きた場合、次のチェックまでの待ち時間が最大で1周期分乗るためです。

許容停止時間が30秒なら、periodSeconds 5秒・failureThreshold 3回で最悪16秒前後に収まります。短くするほど誤検知は増えます。1回のネットワーク瞬断で切り離される構成を避けるため、failureThresholdは2回を下限にしてください。

復帰側の閾値も同時に決めます。ALBのHealthyThresholdCountは既定の5回だと復帰まで150秒かかり、デプロイのたびに積み上がるため、起動が速いアプリケーションでは2〜3回へ下げてください。

タイムアウト値をp99レイテンシから決める設定手順と検証の順序

タイムアウト値は、正常時の応答時間の実測から決めます。

  1. チェック用パスの応答時間を、本番相当の負荷でp99まで計測する
  2. 依存先を含まない浅いパスであることを確認する(含むならp99が依存先に引きずられる)
  3. p99の2〜3倍をタイムアウトの初期値に置く
  4. タイムアウトが実行間隔より小さいことを確認する
  5. ピーク負荷とデプロイ直後に再計測し、失敗ログが出ていないか確かめる

KubernetesのtimeoutSeconds既定1秒は、この手順を踏むとほぼ確実に引き上げになります。p99が200ミリ秒なら500ミリ秒から1秒へ、JVMのように起動直後が重い環境なら2〜3秒へ。タイムアウトが実行間隔を超えるとチェックが重なって走るため、そこは超えないでください。

起動が遅いアプリをstartupProbeに退避させる判断ライン

起動が遅いアプリケーションでは、livenessProbeのinitialDelaySecondsを伸ばす対処が使われがちです。これは起動後の障害検知まで遅らせます。

startupProbeへ退避させる判断ラインは、起動時間のばらつきがperiodSeconds×failureThresholdの枠に収まらなくなった時点です。起動に最大90秒かかるなら、startupProbeにperiodSeconds 10秒・failureThreshold 30回を与えて300秒の猶予を作り、livenessProbe側はinitialDelaySeconds 0のまま短い間隔に保つ。これで起動は待ちつつ、起動後の異常はすぐ拾う形になります。

ヘルスチェックを設定しない判断と全台同時503を招く失敗パターン

ここからは判断の話です。ヘルスチェックは、入れないほうが壊れにくい場面があります。

Livenessプローブをあえて設定しない判断が成り立つ2つの条件

livenessProbeを設定しないほうがよい条件は2つです。1つは、異常時にプロセスが自ら終了する設計になっている場合。未捕捉例外でプロセスが落ちるなら、コンテナランタイムのrestartPolicyが再起動を担うため、プローブは重複します。もう1つは、再起動しても回復しない障害しか想定できない場合です。

迷ったらlivenessProbeは設定しない、と決めてよいと考えます。誤検知で再起動が走るリスクのほうが、再起動で助かる確率より高い構成が多いためです。readinessProbeは逆で、ほぼ全てのサービスに設定してください。トラフィックを受けられない状態を外へ伝える手段が他にありません。

共有DB障害で全台が同時に503を返しfail-openする失敗

共有データベースの障害で全台が同時に503を返すと、プラットフォームによって挙動が割れます。ALBはフェイルオープンし、全ターゲットへ流し続ける挙動です。Kubernetes ServiceはEndpointsが空になり、呼び出し側は接続不能かIngress側の503を受け取ります。

この違いを知らずに深いチェックを全レイヤーへ入れると、ALB配下では判定が実質無効になり、Kubernetes配下では全断を自ら作り出します。深いチェックを入れるなら、結果が切り離しに使われるのか通知だけなのかを設定ごとに確かめてください。

ヘルスチェック通過だけを正常とみなす監視設計に空く観測範囲の穴

ヘルスチェックが200を返し続けていても、非同期ジョブの滞留や前日から止まったバッチは拾えません。見ているのは、リクエストに応答できるかどうかだけです。

キューの滞留数やジョブの最終成功時刻はメトリクスとして別に監視し、しきい値でアラートを出す。この役割分担はシステム監視の監視項目と死活監視・性能監視の違いで整理しています。ヘルスチェックの通過を、正常稼働の証明として扱わないでください。

デプロイ・停止処理・監視とヘルスチェックを噛み合わせる運用設計

ヘルスチェックは単体では完結しません。デプロイと停止処理に噛み合わせて、はじめて無停止が成立します。

ローリングアップデートがreadinessの成否に依存する前提条件

ローリングアップデートは、新しいPodがreadinessを満たしたことを確認してから次のPodを置き換えます。readinessProbeが常に200を返す実装だと、初期化の終わっていないPodにトラフィックが流れ、デプロイのたびに数秒のエラーが出ます。maxSurgeとmaxUnavailableをどう振っても、readinessの実装が甘ければ無停止にはなりません。

更新中の挙動と設定値はローリングアップデートの仕組みとKubernetes・ECSの設定値にまとめています。readinessProbeは、依存先の初期化・キャッシュのウォームアップ・マイグレーション完了まで含めて成功を返すよう実装してください。

停止シグナル受信と切り離し順序を合わせるグレースフルな終了設計

停止側は順序が全てです。SIGTERMを受け取ったらまずreadinessを失敗へ切り替え、ロードバランサーが切り離すまで待ってから受付を止め、処理中のリクエストを終えて終了する。順序を守らないと、切り離される前にプロセスが終わり、502や504が出ます。

待ち時間はプラットフォーム側の設定から決めます。ALBは登録解除の遅延(既定300秒)、KubernetesはEndpointsの伝播にかかる数秒。グレースフルシャットダウンの停止順序と猶予時間の設計と突き合わせ、terminationGracePeriodSecondsを処理時間より長く取ってください。

閾値の見直しと停止処理の作り込みは、リリースのたびに検証が要る領域になります。運用の手が足りない場合は、保守運用・内製化支援のように、設定値の設計から監視との接続までを含めて外部に預ける選択肢もあります。

よくある質問

ヘルスチェックの設計時に相談を受けることの多い質問を、実務での判断とあわせて整理します。

ヘルスチェックの間隔は何秒に設定すればよいですか?

許容できる検知時間から逆算します。KubernetesではperiodSeconds×failureThresholdが検知時間の上限になるため、30秒以内に検知したいならperiodSeconds 5〜10秒・failureThreshold 3回が現実的です。ALBは既定のinterval 30秒だと切り離しまで最悪65秒かかるので、短縮するなら10秒前後まで下げてください。間隔を詰めるほどチェックの負荷とログ量が増えるため、1秒間隔のような設定は割に合いません。

LivenessとReadinessは両方設定する必要がありますか?

readinessProbeはほぼ全てのサービスで必要になります。トラフィックを受けられない状態をServiceやロードバランサーへ伝える手段が、他にないためです。livenessProbeは任意で、異常時にプロセスが自ら終了する設計なら省略して構いません。両方を設定するなら検証内容を変え、livenessは自プロセスの応答だけ、readinessは起動完了と依存先の準備という分担にしてください。依存先の障害で全台が同時に再起動する事故を避けられます。

ヘルスチェック用のエンドポイントは認証をかけるべきですか?

ロードバランサーやkubeletは認証情報を付けられないため、チェック用のパスは認証なしで応答する必要があります。代わりに、外部から到達できない経路へ閉じるか、返す情報を絞ってください。バージョン番号や依存先のホスト名まで返す実装は、構成情報を外部へ渡すのと同じです。外部公開が避けられないなら、浅いチェックのパスだけを公開し、詳細な判定結果は内部ネットワークからの要求にだけ返す形へ分けます。

ヘルスチェックのログはどこまで残すべきですか?

成功ログはアクセスログから除外し、失敗と状態遷移だけを残してください。間隔10秒・10台構成なら、成功ログだけで1日およそ86,400行に達し、保管費用と検索性を圧迫します。失敗時は、ステータスコード・応答時間・依存先ごとの結果を残してください。ALBにはヘルスチェックログをS3へ出力する機能があり、切り離しの理由コードを後から追えます。

ロードバランサーとKubernetesのヘルスチェックは二重に必要ですか?

役割が違うため、結果として両方が動く構成になります。readinessProbeはPodをEndpointsから外し、ALBのターゲットグループのヘルスチェックはターゲットを登録解除する仕組みです。ALBにPod IPを直接登録する構成では、両者の閾値がずれると、片方が切り離したのに片方は流し続ける状態が起こります。ALB側の検知を先に効かせてください。

関連記事

資料請求

RELATED POSTS 関連記事