アプリケーション実験用にサンプルアプリケーションを用意しています。アプリの概要は以下のとおりです。Shutdownを行うかどうかを指定することができるpreStopフックはDeploymentのマニフェストに記述してデプロイするCPU負荷の調整方法ランダム値を生成する処理を繰り返すことでCPU負荷がかかるようにしています。することで指定できます。使って実装しています。こちらも起動フラグで、Gracefulに終了するかどうかを指定できる仕掛けにしています。{log.Print(err)}}preStopスリープの指定方法preStopフックによるスリープはDeploymentのmanifestに記述します。Shutdownの効果を検証してみます。以下の3通りの条件での結果を比較してみます。8s)に変え、それ以外の条件は同じにしてあります。200結果は以下のとおりです。29preStopスリープを行わない(0s)のケースでは4%のリクエストがエラーとなっていますが、5sのスリープによって1%に、8sでは0%という結果になりました。この結果を見る限り、preStopフックにエラーを抑制する効果があるように見えますが、実際にはKubernetesとアプリケーションにはどのような状況が起きているのでしょうか。以下の図は、ローリングアップデートに伴うPodの終了時の動作を描いたもので、preStopスリープの長さが十分でないためにエラーが発生するケースを表しています。注目すべき点は、kube-proxyによりiptablesが更新される前の時点でpreStopスリープが終わり、コンテナにSIGTERMが送信されていることです。これは、アプリケーションへのトラフィックへの配送がまだ続いている(サービスアウトしていない)にも関わらず、アプリケーションのシャットダウンが始まっていることを意味します。実験用のGoアプリケーションは、(多くのプロダクションのアプリケーションも同様と思われますが)シャットダウン処理が始まると新規リクエストを受け付けることができず、レスポンスとしてはエラーが返却されます。preStopスリープの時間を十分に取ることで、サービスアウトしてからコンテナのシャットダウンが開始されるようになり、8sのスリープの実験結果のように、エラーを抑制することができます。Shutdownの効果を調べるために、以下2通りの条件で実験してみます。Shutdownをする/しないの条件以外は同じにしてあります(CPU負荷フラグ、最大リクエスト数の値が先程と異なりますが、これの理由は後述します)。100結果は以下のとおりです。Shutdownをしない条件では5%ほどがエラー、する条件ではそのエラーが0%となっています。Shutdownにもローリングアップデート時のエラーを抑制する効果があるようですが、このケースではどのようなことが起こっているのでしょうか。Shutdownによってエラーを抑制可能なケースでの、Pod終了時の動作を表しています。この場合では、kube-proxyによりiptablesが更新されてから(サービスアウトしてから)コンテナの終了処理に入っています。なにも問題ないように見えますが、そうとは言い切れません。サービスアウトはされたとしても、それ以前にアプリケーションが受け付けたリクエストがまだ処理中かもしれないからです。いったリクエストがまだ残っている状態でGracefulでない終了を行ってしまうと、リクエストの処理が強制的に中断され、レスポンスはエラーとなってしまいます。Shutdownを行うことによって、処理中のリクエストの処理が完了してからアプリケーションプロセスを終了する動作となり、上記の実験結果のようにエラーを抑制することができます。Shutdonwのどちらもローリングアップデート時のエラーの抑制に効果があることが分かりました。それでは、実際の運用環境で使っていくには、他にどのようなことを考慮する必要があるでしょうか。preStopスリープの長さpreStopスリープの時間に関しては、サービスアウト処理が実際にどのくらいの速さで完了するかを考慮する必要があります。サービスアウト処理は、endpoints-controllerによるReconcile処理、kube-proxyによるiptablesの更新が該当しますが、これらはクラスター規模(Serviceリソース数や配下のPod数、クラスターのNode数など)によって変わってくると予想されます。それぞれの環境ごとに実際にサービスアウトにかかる時間を把握した上で、これを超えるようにpreStopスリープを設定してください。Shutdownの要否はアプリケーションの特性によって判断していく必要があります。Shutdownする必要があるでしょう。Shutdownを行わない選択をとると考えるのが無難ではないでしょうか。📘【note】preStopスリープを十分長く取って、処理中のリクエストさえなくなるまで待った上でシャットダウンに入る、という考え方もできそうですが、これは確実ではないと考えます。外部サービスやDBに依存するようなアプリケーションでは、それら外部コンポーネントから予想以上の影響をうけることがあります。してリクエストの処理に想定以上の時間がかかってしまい、レスポンスを返す前にシャットダウンが始まってしまうということがあり得るのではないでしょうか。まとめ本記事では、Deploymentのローリングアップデートに伴う再起動において、リクエストの欠損を防ぐために、以下の対策が有効であることが確認できました。ついてはアプリケーションの特性を考慮する必要があります。内部処理でループする回数を変えるだけなので、millicoresなどの単位が指定できるわけではありません。Native時代のアルパカ。オーガナイザー、「Kubernetes実践ガイド」共著者。ここに述べた発言は所属組織や所属牧場を代表するものではありません。バッジを贈って著者を応援しようバッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。Native時代のアルパカ。オーガナイザー、「Kubernetes実践ガイド」共著者。ここに述べた発言は所属組織や所属牧場を代表するものではありません。Proよくある質問LinksX(Twitter)GitHubメディアキットLegal利用規約プライバシーポリシ。