インフラ●●○○○

ヘルスチェック

Health Checkヘルスチェック

サービスが正常かを定期的に確認する仕組み。切り離しと復帰の自動化の土台。

概要

ヘルスチェックは、サービスが「今、正常に動いているか」を機械が定期的に確かめる仕組みです。典型的には /healthz のようなエンドポイントを用意しておき、ロードバランサ監視システムが数秒〜数十秒間隔で問い合わせます。応答が返ってくれば健康、返ってこない・エラーが返るなら異常 — この単純な問答が、障害時の「切り離し」と回復時の「復帰」を自動化する土台になります。

ポイントは、ヘルスチェックが人間のためではなく機械のための仕組みだということです。ダッシュボードで人が眺める監視とは違い、ヘルスチェックの結果は即座に自動アクションに直結します。異常なサーバへのトラフィックを止める、コンテナを再起動する、デプロイ直後のインスタンスに徐々にトラフィックを流し始める — 現代のインフラで「勝手に治る」ように見える挙動の多くは、その裏でヘルスチェックが回っています。

なぜ生まれたか

サーバが1台だけの時代、障害対応は人間の仕事でした。サービスが落ちれば誰かが気づき、ログインして再起動する。しかし複数台のサーバにロードバランサでトラフィックを分散するようになると、新しい問題が生まれます。10台のうち1台が死んでいても、ロードバランサはそれを知らなければ死んだサーバにもリクエストを送り続け、ユーザーの1割は延々とエラーを見せられることになります。人間が気づいて手で外すのでは遅すぎるのです。

そこで「振り分ける側が、振り分け先の生死を自動で確かめ続ける」という発想が生まれました。さらにコンテナマイクロサービスの時代になると、インスタンスは数十〜数百に増え、起動と停止が日常的に繰り返されます。もはや「どのインスタンスが今使えるか」を人間が把握することは不可能で、ヘルスチェックはオーケストレーション基盤の前提機能になりました。Kubernetes がプローブ(探針)という名前でこれを標準装備しているのは、その到達点です。

詳細

切り離しと復帰のサイクル

もっとも基本的な形は、ロードバランサによる切り離しと復帰です。一度の失敗で即切り離すとネットワークの一瞬の揺らぎで健全なサーバまで外してしまうため、「連続N回失敗したら不健康、連続M回成功したら健康」という閾値を設けるのが定石です。

サーバBロードバランササーバBロードバランサ障害発生閾値超過 サーバBを切り離し以降のリクエストは他サーバへ再起動などで回復連続成功で復帰トラフィック配分を再開ヘルスチェック200 OKヘルスチェック応答なし 1回目ヘルスチェック応答なし 2回目ヘルスチェック応答なし 3回目ヘルスチェック200 OK 1回目ヘルスチェック200 OK 2回目

3つの問いを区別する — Liveness / Readiness / Startup

「健康か」という問いは、実はひとつではありません。Kubernetes はこれを3種類のプローブに分けており、この区別はどの環境でも通用する整理です。

Liveness(生きているか) は「プロセスが回復不能な状態に陥っていないか」を問います。デッドロックやメモリ枯渇でハングしたプロセスは、外から再起動するしかありません。Liveness に失敗すると、コンテナは殺されて作り直されます。Readiness(受け入れられるか) は「今この瞬間、リクエストを処理できる状態か」を問います。起動直後のウォームアップ中、依存先との接続待ち、あるいはグレースフルシャットダウン(新規受付を止めて処理中のリクエストだけ完了させる終了手順)の最中 — プロセスは生きているがトラフィックは受けられない、という状態はごく普通にあります。Readiness に失敗しても再起動はされず、単にトラフィックの振り分け対象から外されるだけです。Startup(起動し終えたか) は起動に時間がかかるアプリのための猶予で、これが成功するまで Liveness の判定を保留します。これがないと、起動に2分かかるアプリが Liveness の閾値に引っかかり、「起動中に殺されてまた起動する」無限ループに陥ります。

この3つを混同すると事故になります。典型例は「Readiness で見るべき一時的な不調を Liveness に入れてしまい、再起動の嵐を起こす」パターンです。再起動で治るものだけを Liveness に、それ以外は Readiness に、が原則です。

浅いチェックと深いチェック

もうひとつの設計軸は「どこまで確かめるか」です。浅いチェックは自プロセスが HTTP を返せるかだけを見ます。高速で安定していますが、「プロセスは生きているがDBに繋がらず全リクエストがエラー」という状態を健康と誤判定します。深いチェックはデータベースやキャッシュなど依存先への疎通まで確かめます。実態に近い判定ができる反面、重大な落とし穴があります。

依存先のDBが一時的に不調になったとき、深いチェックを入れた全インスタンスが一斉に「不健康」になり、ロードバランサが全台を切り離してしまう — 本来なら一部のリクエストが失敗するだけで済んだ障害が、依存の連鎖を伝って全面停止に化けるのです。原則は「Liveness は浅く、Readiness も自分の努力で治せないものは含めない」。依存先の不調には、切り離しではなくサーキットブレーカーグレースフルデグラデーションで応じるほうが、被害を局所に留められます。

浅いチェック深いチェックチェッカー自プロセスDB・依存先依存先は確認しないチェッカー自プロセスDB・依存先依存先の不調で全台が不健康判定になりうる
浅いチェックと深いチェック — 深いチェックは依存先の不調を全台切り離しに増幅しうる

実務での勘所

ヘルスチェックのエンドポイントは軽く・速く・副作用なしが鉄則です。判定にはタイムアウトを必ず設定し、チェック自体が詰まって判定不能になることを防ぎます。またグレースフルシャットダウンとの連携も重要で、終了シグナルを受けたらまず Readiness を落とし、ロードバランサから外れるのを待ってから接続を閉じる、という順序を守ることで、デプロイのたびにエラーが漏れる問題を防げます。サービスメッシュの環境では、アプリ本体とサイドカーの両方の健康を合成して判定する構成も一般的です。切り離しはあくまで自動回復の仕組みなので、頻発するなら根本原因をアラートで人間に知らせる線引きも忘れずに設計します。