TT Lab
はじめる
学ぶ 学習パス コース

K8sを壊してみよう — 犯人は自分のYAML

生存と受付準備は別のこと

TT Labで続きを見る

一言でいうと

readinessの失敗は、トラフィックを受け取る資格を失うことであり、コンテナプロセスが終了したという意味ではありません。

なぜ必要なのか

サービスの起動時に、データやキャッシュを読み込むのに時間がかかるとします。プロセスは生きていますが、まだ正常なリクエストを処理する準備ができていません。この状態でリクエストを送り続けると、顧客にエラーを返すことになり、無条件に再起動すると、準備の作業を最初からやり直すことになりかねません。そのため、プロセスの生存、初期起動の完了、トラフィックを受け付けられるかどうかを、区別する必要があります。

Kubernetesのliveness・startup・readinessプローブは、その区別を表現するための道具です。livenessは再起動の判断に、startupは遅い起動区間を保護するために、readinessはサービスのトラフィックを受け取れるかどうかの判断に使います。プローブの名前を覚えるだけでなく、「失敗したときに何が変わるのか」を問う必要があります。このラボにはreadinessしかないので、失敗をlivenessによる再起動で説明すると、観測と矛盾します。

どう動くのか

ラボのアプリは、ポート8080の/パスで、決められた本文を返します。正常なreadinessも、同じ/を検査します。readinessのpathだけを/missingに変えると、アプリ自体は依然として/へのリクエストを処理しますが、プローブは存在しないパスのHTTP 404を受け取ります。PodはRunningでいられても、コンテナのreadyはfalseになります。ServiceのEndpointSliceにアドレスが残っていても、ready条件がfalseの対象は、通常のサービストラフィックの準備ができた対象ではありません。

GET PodIP:8080/         → 200, 앱은 살아 있다
readiness /missing     → 404, 수신 준비 조건은 실패한다
Pod phase              → Running일 수 있다
container ready        → false
Service의 Ready 대상   → 0

この5行が一緒にそろっていれば、「アプリが死んでサービスが止まった」よりも、「プローブの契約が実際のパスと異なっていて、トラフィックの対象から外れた」のほうが、よく合う説明です。ただし、過去のPodの404イベントを、現在のPodの原因として持ち込んではいけません。テンプレートの変更でPodが入れ替わるので、名前とUIDを一緒に見て、現在のPodのイベントかどうかを確認します。古いイベントは、障害があったことの参考資料であり、現在の原因を自動的に証明するものではありません。

readinessの失敗だけで、同じコンテナが再起動されることはありません。このラボは、現在のPodのrestartCountが0である状態と、直接のHTTPの成功を、一緒に観測します。テンプレートのreadinessのパスを変える動作そのものが新しいPodを作ることと、プローブの失敗がコンテナを繰り返し再起動することを、混同しないでください。新しいPodのUIDができたのはDeploymentの変更の結果であり、その新しいPodの中のrestartCountは、コンテナの再起動の記録です。

一般的なDeploymentは、RollingUpdateを使います。新しいPodが準備できていなければ、以前の正常なPodが残って、リクエストを処理し続けられます。これは障害がないという意味ではなく、ロールアウトが止まっているが、既存のバージョンがサービスを守っている状況です。このコースでは、単一のレプリカとRecreateを使って、以前のPodが実験の症状を覆い隠さないようにしています。無停止運用のための推奨設定ではなく、因果関係を観察するための統制条件です。

ほかの設定をすべて同じ状態のままにして、1つだけ変える理由も、ここにあります。readinessのパスとServiceのセレクターを同時に間違えると、Readyな対象が0である理由が2つになります。片方だけを復旧してもリクエストが戻らないので、学習者が復旧コマンドそのものを疑ってしまうことがあります。まずセレクターの実験を完全に復旧してから、readinessの実験に進んでください。複合障害は、それぞれの単一障害を区別できるようになったあとの課題です。

現場での姿

新しいバージョンで、ヘルスチェックのパスが/healthから/readyに変わったのに、デプロイ設定が以前のパスのままになっていることがあります。アプリの主要な業務リクエストは正常なのに、新しいPodだけがReadyにならないのです。このとき、プローブを削除して緑色にするのは、原因の解決ではないかもしれません。実際にトラフィックを受け付けられるかどうかを判断していた保護の仕組みを、なくしたことになるからです。アプリとデプロイ設定が合意したパスに合わせ、状態が変わるかどうかを確認する必要があります。

逆に、プローブが実際の依存関係の障害を正直に示している場合もあります。データベースが使えないのに、readinessだけを無条件に200を返すように修正すると、チェックは通過しますが、顧客のリクエストは失敗し続けます。プローブは、サービスの契約の縮約版です。どの依存関係を含めるか、一時的なエラーにどれだけ敏感に反応するか、検査のコストがどれだけかかるかは、サービスの特性に応じて設計する必要があります。あるラボの/を、すべてのサービスにコピーすることは、答えではありません。

readinessとlivenessが、同じ重い業務APIを呼び出すと、負荷が大きくなり、依存するサービスが少し遅くなっただけなのに、複数のPodが一緒に再起動される悪循環を生むことがあります。このコースは、そのような連鎖障害を、運用クラスターに注入しません。ただし、2つのプローブが異なる判断を担当することを学んでおけば、あとでしきい値と失敗ポリシーを議論できます。検査の間隔を短くしたという理由だけで、障害対応が常に速く安全になるわけでもありません。

観測を見て文章を書く練習をしてみてください。「Kubernetesが壊れた」の代わりに、「現在のPodはRunningで、直接の/リクエストは成功するが、該当するPodのreadiness /missingが404であり、ReadyなEndpointは0個である」と記録します。後者の文は、次の人が同じリクエストを試して、反論したり確認したりできます。原因を急いで断定しないまま、次の確認の範囲を狭められます。

次の確認ですること

クイズで、Podの入れ替えとコンテナの再起動、プローブの目的と結果を切り分けます。ラボのステップ4–5では、誤ったreadinessのパスと復旧を、それぞれ保存します。観測ツールの成功は、該当する実験の条件が再現されたという意味であり、そのツールがプローブを自動的に修正してくれたという意味ではありません。最後に、原因と予防策を、レポートの中でつなげます。