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

CKAD — Kubernetesアプリケーション開発者

営業終了と注文キャンセルは別のこと

TT Labで続きを見る

一言でいうと

新しいお客さんを受け入れないことと、厨房がすでに受けた注文を仕上げることは別です。Kubernetesのトラフィックの除外と、アプリケーションの終了処理も、分けて確認する必要があります。

なぜ必要なのか

デプロイのあと、新しいバージョンは問題なく開くのに、デプロイの瞬間に決済やファイル生成のリクエストが1件だけ失敗することがあります。Deploymentの入れ替えが終わったという表示には、そのリクエストの運命は含まれていません。新しいPodの準備ができたか、古いPodが新しいリクエストの対象から外れたか、すでに処理していたリクエストを終えたかは、それぞれ別の問いです。3つの問いを1つの緑のランプで代用すると、短い状態確認は通っても、長い作業は途切れることがあります。

今回の単元は、複数のレプリカを置いたサービス全体の無停止を証明するものではありません。Serviceの背後にPodを1つ置き、そのPodが実際に受け付けた1つのリクエストの一生を、最後まで追いかけます。実験の範囲を小さく決めた理由は、数字をよく見せるためではなく、どの接続とどのプロセスが結果を作ったのかを、はっきり識別するためです。HTTPレスポンスには、Downward APIで注入したPod UIDを入れます。名前は再利用できますが、UIDは生成のたびに変わります。

どう動くのか

readinessProbeは、サービスの対象にする準備ができたかどうかを表現します。その結果がEndpointSliceとトラフィックの処理経路に反映される過程は、別に進みます。Podに削除リクエストが入ると、終了中のendpointのreadyはfalseになります。これを、既存のTCP接続をすぐに切れという命令として読んではいけません。すでにサーバーが受けて処理しているリクエストをいつ終えるかは、サーバーのシグナル処理と接続の動作も決めます。

EndpointSliceのconditionsには、ready、serving、terminatingがあります。terminatingは終了中であることを知らせ、servingは終了の状態とは別に、実際の準備状態を表現できます。したがって、終了中でreadyがfalseでも、servingがtrueである観測が可能ですが、常にその組み合わせを見る必要があるわけではありません。今回のサーバーは、drainが始まるとhealthzも503を返すので、続く準備の検査によってservingが変わることがあります。最初の終了endpointと、あとのendpointを混ぜて、1つの固定された状態のように使わないでください。

ここでは、publishNotReadyAddressesを有効にしていない通常のServiceを使います。この設定を有効にすると、readyの解釈に例外が生じます。また、利用可能なすべてのendpointが終了中のとき、servingとterminatingが両方trueの対象にルーティングできるプロキシの動作もあります。したがって、ready=falseだけで新しいリクエストが1件も到着しないと保証されるわけではありません。アプリのdrain処理とトラフィック経路の実際の動作を、一緒に確認する必要があります。

実験のヘルパーは、まずServiceのhealthzのレスポンスを確認します。Pod Readyだけを見てすぐに作業のリクエストを送ると、Service経路がまだ準備できていないことによる接続エラーを、終了の効果と誤認することがあるからです。続いてworkリクエストを送り、サーバーのactiveが1であることを確認してから削除します。観測の順序は、接続の準備、サーバーの受け付け、削除のリクエスト、レスポンスまたは切断です。このうち受け付けの証拠がなければ、「処理していたリクエストが切断された」という結論も出せません。

現場での姿

APIゲートウェイ、サービスメッシュ、外部ロードバランサーがあれば、トラフィックを選ぶ層がさらに増えます。単一のk3s Serviceで得た結果を、別の経路の保証として移すことはできません。実際の運用では、どの層が新しい接続を止め、どの層が既存の接続を維持するのかを、別々に観測する必要があります。成功率だけでなく、リクエストのレイテンシとレスポンスの本文、再試行が作った副作用も一緒に見ます。

失敗の数え方も重要です。curlの転送の成功とHTTP 200は、同じ条件ではありません。HTTP 500を受け取っても、接続は正常なので、成功として数えてしまうことがあります。逆に、接続準備のタイムアウトを、アプリの強制終了と断定することもできません。今回のラボは、正常な完了の本文、接続の切断の例外、Podの終了コードをそれぞれ残して、異なる失敗を区別します。

次のラボですること

即座に終了するサーバーと、既存のリクエストを待つサーバーを比較し、最初のterminating endpointのconditionsを記録します。ready=falseという値1つが、すでに受け付けたリクエストの結果の代わりにはならないことを、レスポンスとUIDで説明してみてください。

公式ドキュメント: Podとendpointの終了の観測、EndpointSlice。