ヘルスチェック
Health Check ・ へるすちぇっく
サービスが正常かを機械が定期的に確かめる仕組み。切り離し・再起動・復帰を自動で行う土台。
✦ 30秒で試す — 航海再起動しても治らない不調で、サーバが再起動を繰り返すこのとき、利用者から見た結果は?予想してみる概要
ヘルスチェックは、サービスが「いま、正常に動いているか」を機械が定期的に確かめる仕組みです。典型的には /healthz のようなエンドポイントを用意しておき、ロードバランサや監視の仕組みが数秒〜数十秒の間隔で問い合わせます。応答が返ってくれば健康、返ってこない・エラーが返るなら異常 — この単純な問答が、障害時の「切り離し」と回復時の「復帰」を自動化する土台になります。
ポイントは、ヘルスチェックが人間のためではなく機械のための仕組みだということです。ダッシュボードで人が眺める監視とは違い、ヘルスチェックの結果は即座に自動の行動に直結します。異常なサーバへのトラフィックを止める、コンテナを再起動する、デプロイ直後のインスタンスに準備ができてからトラフィックを流し始める — いまのインフラで「勝手に治る」ように見える挙動の多くは、その裏でヘルスチェックが回っています。だからこそ、何を問うかを間違えると、機械が自動で障害を広げてしまうこともあります。
なぜ生まれたか
サーバが1台だけの時代、障害対応は人間の仕事でした。サービスが落ちれば誰かが気づき、ログインして再起動する。しかし複数台のサーバにロードバランサでトラフィックを分散するようになると、新しい問題が生まれます。10台のうち1台が死んでいても、ロードバランサはそれを知らなければ死んだサーバにもリクエストを送り続け、利用者の1割は延々とエラーを見せられます。人間が気づいて手で外すのでは遅すぎるのです。
そこで「振り分ける側が、振り分け先の生死を自動で確かめ続ける」という発想が生まれました。さらにコンテナやマイクロサービスの時代になると、インスタンスは数十〜数百に増え、起動と停止が日常的に繰り返されます。「どのインスタンスがいま使えるか」を人間が把握することはもはや不可能で、ヘルスチェックはオーケストレーション基盤の前提機能になりました。Kubernetes がプローブ(探針)という名前でこれを標準装備し、しかも問いを3種類に分けているのは、「健康か」という問いを1つにまとめると事故が起きることを、現場が学んできた結果です。
詳細
切り離しと復帰のサイクル
もっとも基本的な形は、ロードバランサによる切り離しと復帰です。一度の失敗で即座に切り離すと、ネットワークの一瞬の揺らぎで健全なサーバまで外してしまうため、「連続 N 回失敗したら不健康、連続 M 回成功したら健康」という閾値を設けるのが定石です。
閾値と間隔の積が、障害を検知するまでの時間になります。10秒間隔で3回なら、最悪30秒ほどは壊れたサーバにリクエストが流れ続けます。間隔を詰めれば検知は速くなりますが、チェック自体の負荷と、揺らぎによる誤判定が増えます。
3つの問いを区別する — Liveness / Readiness / Startup
「健康か」という問いは、実はひとつではありません。Kubernetes はこれを3種類のプローブに分けていて、この区別はどの環境でも通用する整理です。
Liveness(生きているか) は「プロセスが回復不能な状態に陥っていないか」を問います。デッドロックやメモリの枯渇でハングしたプロセスは、外から再起動するしかありません。Liveness に失敗すると、コンテナは殺されて作り直されます。Readiness(受け入れられるか) は「いまこの瞬間、リクエストを処理できる状態か」を問います。起動直後のウォームアップ中、キャッシュの読み込み中、あるいはグレースフルシャットダウン(新規の受付を止めて、処理中のリクエストだけ終わらせる終了手順)の最中 — プロセスは生きているがトラフィックは受けられない、という状態はごく普通にあります。Readiness に失敗しても再起動はされず、振り分け先から外されるだけです。Startup(起動し終えたか) は起動に時間がかかるアプリのための猶予で、これが成功するまで Liveness の判定を保留します。これがないと、起動に2分かかるアプリが Liveness の閾値に引っかかり、「起動中に殺されてまた起動する」無限ループに陥ります。
この3つを混同すると事故になります。典型は「Readiness で見るべき一時的な不調を Liveness に入れてしまい、再起動の嵐を起こす」パターンです。原則は、再起動で治るものだけを Liveness に、それ以外は Readiness に、です。
浅いチェックと深いチェック
もうひとつの設計軸は「どこまで確かめるか」です。浅いチェックは自プロセスが HTTP を返せるかだけを見ます。速くて安定していますが、「プロセスは生きているが DB につながらず、全リクエストがエラー」という状態を健康と誤判定します。深いチェックはデータベースやキャッシュなど依存先への疎通まで確かめます。実態に近い判定ができる反面、重大な落とし穴があります。
依存先の DB が一時的に不調になると、深いチェックを入れた全インスタンスが一斉に「不健康」になります。Readiness に入れていれば全台が振り分け先から外れ、ロードバランサには送り先が残りません。本来なら DB を使う一部のリクエストが失敗するだけで済んだ障害が、DB を使わないリクエストまで巻き込んだ全面停止に化けるのです。Liveness に入れていれば、全台が再起動を繰り返したうえに、再起動しても DB は直りません。
原則は「Liveness は浅く。Readiness にも、自分の努力で治せない依存先の状態は入れない」です。依存先の不調には、切り離しではなくサーキットブレーカーやグレースフルデグラデーションで応じるほうが、被害を局所に留められます。例外は、そのインスタンスだけが依存先に届かない(自分のコネクションプールが壊れている、など)場合で、これは他の台に任せるべき状況なので Readiness で外す意味があります。見分けるには「全台が同時に落ちるなら、それはヘルスチェックで扱う問題ではない」と覚えておくとよいでしょう。
下の部品で確かめてください。「DB に届くか」の札を、入れない・Readiness・Liveness のどこかへ運び、DB を押して不調にします。インスタンスを押すとハング(デッドロック)させることもでき、浅い Liveness がそれを見つけて再起動する様子も見られます。
いまのプローブは「自分が応答できるか」だけを見る浅いチェックです。まず DB を押して不調にしてみてください。そのあと「DB に届くか」の札を Liveness や Readiness へ運んで、同じことを試します。
実務での勘所
ヘルスチェックのエンドポイントは軽く・速く・副作用なしが鉄則です。判定にはタイムアウトを必ず設け、チェック自体が詰まって判定不能になることを防ぎます。重い処理(DB への問い合わせなど)をチェックのたびに実行すると、チェックがサービスの負荷になり、負荷が高いときほどチェックが落ちるという悪循環も起きます。
グレースフルシャットダウンとの連携も重要です。終了の合図を受けたらまず Readiness を落とし、ロードバランサから外れるのを待ってから接続を閉じる、という順序を守ると、デプロイのたびにエラーが漏れる問題を防げます。サービスメッシュの環境では、アプリ本体とサイドカーの両方の準備がそろってから受け付ける必要があります。最後に、切り離しや再起動はあくまで自動回復の仕組みです。再起動が頻発しているなら、その根本原因をアラートで人間に知らせる線引きも忘れずに設計します。自動で直り続けていると、壊れていることに誰も気づかないからです。
試問
ロードバランサの後ろに3台のインスタンスがあり、要求の6割は DB を使い、4割はキャッシュで返せます。「DB に届くか」の札は、Liveness プローブ(落ちたら再起動)・Readiness プローブ(落ちたら切り離し)・入れない、のどこかへ運べます(いまは Readiness)。DB を押すと不調になり、もう一度押すと戻ります。記録は DB を不調にした時点から数え直します。
いまは「DB に届くか」の札が Readiness に入っています。札はドラッグ(またはタップ)で置き場所を変えられ、DB やインスタンスは押すと不調・ハングになります。
