Pod内に複数のコンテナがある場合の起動シーケンス

これはなに 「Kubernetesマイクロサービス開発の実践」というKubernetes本を執筆しまして、めでたく先日発売となりました!ぜひ買ってくださいー。 「Kubernetesマイクロサービス開発の実践」 この書籍の第7章には、PodオブジェクトがAPI Serverに作成されてから実際にPodが起動するまでの処理を解説した箇所があるのですが、諸事情のため書籍では載せられなかったマニアックな挙動があります。それは、Podに複数のコンテナがあり、いずれかまたはすべてのコンテナでpostStart Lifecycle Hookを使っている場合です。このエントリーではそのようなPodが起動するとき、どのような動作になるかを書きます。 📘【note】 これはKubernetes Advent Calendar 2023の20日目の記事です。 お題のPod 本記事では、以下のマニフェストのような複数のコンテナ(container1, container2)を含むPodを使って実験します。ちょっと長いですが、やっていることは以下のような感じです。 container1はメインの処理、postStartフック、Readines Probe、Liveness Probeで、それぞれ1秒おきに /var/log/startup-sequence-test/message にログを出力する container2はメインの処理で、1秒おきに、上と同じファイルにログを出力する apiVersion: v1 kind: Pod metadata: name: startup-sequence-test namespace: default spec: containers: - name: container1 image: busybox:latest args: - /bin/sh - -c - | for i in $(seq 60); do echo "$(date): container1 / main" >> /var/log/startup-sequence-test/message sleep 1 done tail -f /dev/null lifecycle: postStart: exec: command: - "/bin/sh" - "-c" - | for i in $(seq 30); do echo "$(date): container1 / post start hook" >> /var/log/startup-sequence-test/message sleep 1 done readinessProbe: exec: command: - "/bin/sh" - "-c" - | echo "$(date): container1 / readiness probe" >> /var/log/startup-sequence-test/message livenessProbe: exec: command: - "/bin/sh" - "-c" - | echo "$(date): container1 / liveness probe" >> /var/log/startup-sequence-test/message volumeMounts: - mountPath: /var/log/startup-sequence-test name: log-volume - name: container2 image: busybox:latest args: - /bin/sh - -c - | for i in $(seq 60); do echo "$(date): container2 / main" >> /var/log/startup-sequence-test/message sleep 1 done tail -f /dev/null volumeMounts: - mountPath: /var/log/startup-sequence-test name: log-volume volumes: - name: log-volume emptyDir: {} このPodをクラスタに適用した後、/var/log/startup-sequence-test/message に書かれている内容を確認すれば、それぞれの処理がどのような順番で実行されているかが分かるというわけです。 ...

2023年12月21日 · 5 分 · hhiroshell

kubeletのAPIを調べてみた

これはなに ひょんなことからkubeletのAPIを使った開発をしてみたくなりまして、そのための調査をしたので共有したいと思います。 自分は今までこれといって触る機会がなく、コンテナのメトリクスを取るのに使われているということで、たまーに「ああ、kubeletさんのAPIは今日も頑張ってくれているのだなぁ」と思いを馳せるくらいの温度感でした。Kubernetesエンジニアの多くが、同じような感想を持たれているのではないでしょうか。 そんな縁の下の力持ち、kubeletのAPIさん、この記事をきっかけに、今までより少しだけ身近に感じてくれたらいいなって、そんなふうに思います♪ 📘【note】 この記事はKubernetes Advent Calendar 2022の19日目です。 どんなAPIがあるの kubeletのAPIの詳細を記したドキュメントが見つけられなかったので、以下の箇所を起点にして、コードを追って調べていきます。 https://github.com/kubernetes/kubernetes/blob/v1.25.5/pkg/kubelet/server/server.go 以下、調査の結果わかったもの。 Kubernetesリソースの取得 /pods kubeletと同一Node上の、Podリソースのリストを取得できる メトリクスの取得 /metrics kubelet自身の各種メトリクスがを取得できる /metrics/cadvisor kubeletと同一Node上の、コンテナのCPU使用量などの各種メトリクスを取得できる kubeletにはcAdvisorが組み込まれており、それが提供するメトリクスを取得する。そのあたりの詳しい話は@ryysudさんの記事を参照ください /metrics/probes kubeletと同一Node上の、コンテナのProbe(Readiness/Liveness/StartUp)の成否を集計したメトリクスを取得できる /metrics/resource kubeletと同一Node上の、コンテナ単位、Pod単位それぞれのCPU、メモリ使用量を取得できる v0.6.0以降のMetrics Serverはこのエンドポイントからメトリクスを収集している1 /stats/summary kubeletがあるNodeと、そのNode上のPodのCPU、メモリ、ネットワーク、ディスク関連のメトリクスを取得できる 他はPrometheusのExporterの形式だが、このエンドポイントはJSONで値が取得できる v0.5.x以前のMetrics Serverはこのエンドポイントからメトリクスを収集している1 kubectlコマンドの機能で内部的に利用されるもの 以下は、kubectlのサブコマンドを実行したときに使われているAPIと推測されます。 /run /exec /attach /portForward /logs パスの名前から察するに run, exec, attach, port-forward, logs の各コマンドで使われているものだと思われます。 他 /checkpoint Kubernetes v1.25からalpha機能として提供されているCheckpoint Restoreで使われるAPI ContainerCheckpoint フィーチャーゲートを有効にしつつ、コンテナランタイムとしてこれに対応するものを利用していると利用可能になる Checkpoint Restore機能については、KubeCon NA 2021のセッションがあります /debug/pprof pprofプロファイラのエンドポイント /debug/flags/v kubeletで利用可能な起動フラグの一覧を取得できる 認証、認可の仕組み 認証、認可については以下のドキュメントがあります。 https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/ ポイントをピックアップすると、以下のような感じです。 認証 認証はデフォルトではOFF X.509クライアント証明書認証とAPI Bearer Token認証をかけることができる 認可 Role/ClusterRoleで nodes/[sub-resource] に対する権限を与えるとアクセスが許可される APIのパスに応じて、対応するsub-resource名が異なってくる(具体的な対応関係は上記のリンクを参照) 実際にアクセスしてみる というわけで、API Bearer Tokenによる認証がかかったkubeletのAPIにアクセスしてみます。 ここでは簡単のためにKubernetes上にデプロイしたPodから、kubeletのAPIにアクセスすることにします。 ...

2022年12月19日 · 2 分 · hhiroshell

カスタムコントローラーで任意のイベントを起点にReconcileを実行する

これはなに Kubernetesクラスター外で起きたイベントを起点にReconcileをおこなうカスタムコントローラーの書き方を調べたので、その内容を書きます。 これができると、Kubernetesのカスタムリソースでクラスターの外にあるシステムを管理する、そんなコントローラーを作ることができます。素敵! 📘【note】 この記事はKubernetes Advent Calendar 2021の3日目です。 昨日は@makocchiさんのAdvanced StatefulSet を使ってみようでした。 目次 例えばこんなものを作りたい controller-runtimeのWatches()の話 実装していく! Reconcileメソッド 定期的にイベントを発生させるRunnable構造体 コントローラーのセットアップ コントローラーをManagerに登録する まとめ 例えばこんなものを作りたい 本エントリでは、簡単な例として以下のようなコントローラーを題材にします。 Kubernetesクラスターの外にあるオブジェクトストレージのバケットを、カスタムリソース"StorageBucket"で定義する カスタムリソースで定義されたStorageBucketに対してヘルスチェックを定期的に行い、結果をStorageBucketリソースのStatusフィールドに記録する これを実現するため、「一定時間が経過した」というイベントを起点にカスタムコントローラーのReconcileを実行する、という実装をしてみます。 controller-runtimeのWatches()の話 controller-runtimeのbuilderパッケージには、コントローラーのBuilderユーティリティが用意されていて、これによって所定のリソースを監視対象とするコントローラーを作ることができます。 例えば、builder.For(), builder.Owns() を以下のように実行すると、ReplicaSetとPodリソースに何らかのイベントがあったときにReconcileがトリガーされることになります。 ※ Controller Runtimeのコード例より // ...(snip)... err = builder. ControllerManagedBy(mgr). // Create the ControllerManagedBy For(&appsv1.ReplicaSet{}). // ReplicaSet is the Application API Owns(&corev1.Pod{}). // ReplicaSet owns Pods created by it Complete(&ReplicaSetReconciler{}) if err != nil { log.Error(err, "could not create controller") os.Exit(1) } // ...(snip)... ですが、これはあくまでKubernetesリソースに起きたイベントを起点にReconcileをトリガーするもので、それ以外のイベント、このエントリの例で言えば「一定時間が経過した」ことをきっかけにReconcileを実行することはできません。 そんなときのために、builderパッケージにはbuilder.Watches()メソッドが用意されています。 Watches()メソッドを使うと、とあるチャネルにオブジェクトをエンキューすることを起点にしてReconcileを実行させることができます。そのチャネルに一定時間ごとにオブジェクトを投入してあげれば、今回やりたいことができるわけです。 Watches()メソッドのシグニチャは以下のようになっています。 ...

2021年12月2日 · 2 分 · hhiroshell

アルパカでもわかる安全なPodの終了 - 実験編

これはなに この記事はアルパカでもわかる安全なPodの終了の続編です。 前回は、Podの終了時の動作をKubernetesの各種コンポーネントの仕組みを踏まえつつ考察しました。 Deploymentのローリングアップデートを行うとPodの再起動を伴うことになりますが、このときリクエストを欠損なく処理するために、以下2つの対策が有効であることが分かりました。 preStopフックでのスリープ アプリケーションのGraceful Shutdown この記事では、Deploymentのローリングアップデートをオンラインで実際に行い、上記対策が本当に有効かどうかを確かめていきます。 📘【note】 この記事はKubernetes2 Advent Calendar 2020の18日目です。 昨日は@south37さんの手を動かして学ぶコンテナ標準 - Container Runtime 編でした。 安全にPodを終了するための2つの対策 上述のとおり、Podが安全に終了するためにできることは大きく2つがあります。以下にそれぞれの概要を記します。 内部的な動作の詳細については、前回の記事を参照ください。 preStopフックでのスリープ preStopフックは、コンテナを停止する前に実行される前処理(フック)を定義する機能です。 preStopフックで一定時間の sleep を行うと、コンテナが停止される前に指定した時間だけ待機する動作になります。 これによって、Podへのトラフィックの配送が止まってから(サービスアウトしてから)終了処理に入るようにすることができます。 ここで注意すべき点は、サービスアウトとpreStopフックに依存関係は持たせられず、サービスアウトを確認してからpreStopフックを抜けるといったような制御はできないことです。 このため、コンテナの終了処理をサービスアウトの後に行う、ということを保証することはできません。 アプリケーションのGraceful Shutdown Graceful Shutdownをアプリケーションに実装すると、アプリケーションのシャットダウンが開始されたとき、その時点で受け付けているリクエストが処理されてからプロセスを終了するということが保証できます。 ただし、アプリケーションのシャットダウンが開始されて以降は、新たにリクエストを受け付けることはできないことに注意してください。 それでは、これら2つの対策がローリングアップデート中のエラーの抑制に役立つのか、実験して確かめていきたいと思います。 実験してみた! 実験の流れ 実験用のアプリケーションをKubernetesクラスターにデプロイしておき、一定量のトラフィックを送ります。 リクエストが送られている間に、Deploymentの再起動 (kubectl rollout restart) を行います。 実際の運用場面では、再起動ではなくローリングアップデートが行われることが多いと思いますが、Podの停止・起動さえされれば検証の目的としては足りるため、 kubectl rollout restart で代替します。 アプリケーション 実験用にサンプルアプリケーションを用意しています。アプリの概要は以下のとおりです。 https://github.com/hhiroshell/cowweb-go/tree/v1.1.1 Go製のサンプルアプリケーション 起動フラグで1リクエストの処理でかかるCPU負荷を調整できる1 起動フラグで終了時にGraceful Shutdownを行うかどうかを指定することができる preStopフックはDeploymentのマニフェストに記述してデプロイする CPU負荷の調整方法 ランダム値を生成する処理を繰り返すことでCPU負荷がかかるようにしています。 ループ回数を起動フラグで l=640 などとすることで指定できます。 https://github.com/hhiroshell/cowweb-go/blob/823894c18cbdec4c796e6b91deab078034d75fb8/pkg/infrastructure/cowsay/slow_cowsay.go#L19-L23 // c.load が l フラグで指定した値となる for i := 0; i < c.load; i++ { for j := 0; j < c.load; j++ { rand.Intn(len(cows)) } } Graceful Shutdownの実装方法 Graceful ShutdownはGo標準の http.Server.Shutdown() を使って実装しています。 こちらも起動フラグで、Gracefulに終了するかどうかを指定できる仕掛けにしています。 ...

2020年12月17日 · 2 分 · hhiroshell

アルパカでもわかる安全なPodの終了

これはなに KubernetesにおいてPodが終了するまでの動作を整理します。また、それを踏まえて、安全に(リクエストの欠損を極小化した)Podを終了する方法を考察します。 アプリケーションとしては、HTTPリクエストを受けてレスポンスを返却する、一般的なWebアプリケーションを想定します。 📘【note】 この記事の内容は、@superbrothersさんによる詳解 Pods の終了と公式ドキュメントが元になっていますのでぜひそちらも参照ください。この記事は 2020/09/23 現在の最新版のKubernetes で内容を再確認するとともに、図を足したり、解説を増やしたりしています。 目次 Podが終了する過程 安全なPodの終了のために注意すべきこと Podが終了する過程 コンテナイメージの更新や kubectl delete pod の実行など、Podの終了のトリガーとなる事象が起きると、それまで起動していたPodに対する終了処理が開始されます。Podの終了処理の全体の流れは以下のとおりです。 Podの終了予定時刻をPodリソースに設定する Podリソースをウォッチする複数のコンポーネントが、それぞれの終了処理を実行する 2-a. kubeletによるプロセスのシャットダウン 2-b. endpoints controllerとkube-proxyによるサービスアウト 2-c. Ownerリソースによる管理からの除外 2.の3つの処理は、それぞれを担当するコンポーネントが独立して実行するため、例えば「サービスアウトしてからシャットダウンする」といったような互いに依存関係を持った制御は行われません。これは、本エントリーのテーマのひとつである、「Podの安全な終了」を考える上で重要なポイントになりますので、注意してください。 以降は、上に挙げた各処理において具体的にどのような処理が行われているかを説明します。 1 . Podの終了予定時刻をPodリソースに設定する 削除対象しようとしているPodに対応するPodリソースに対して、 .metadata.deletionTimestamp と .metadata.deletionGracePeriodSeconds が設定されます。 .metadata.deletionTimestamp : 削除予定時刻。このフィールドの設定が行われる時刻に .spec.terminationGracePeriodSeconds (デフォルト: 30秒)を加算した値が設定される .metadata.deletionGracePeriodSeconds : このフィールドの設定が行われる時点での .spec.terminationGracePeriodSeconds の値が設定される これをきっかけに、Podリソースをウォッチしている各コンポーネントが後続の終了処理を開始します。 2-a. kubeletによるプロセスのシャットダウン Podリソースに .metadata.deletionTimestamp が設定されたことをkubeletが検知すると、kubeletは以下のシャットダウンプロセスを開始します。 2-a-1. preStopフックを実行する 2-a-2. Dockerデーモンにコンテナの終了を依頼する preStopフックは、プロセスの終了前に実行する事前処理です。.spec.containers[].lifecycle.preStop に処理内容を記述することができます。指定可能な処理は、任意のコマンドの実行、所定のエンドポイントへのHTTP GETリクエスト、TCPソケットのオープンの試行、の3種です。 preSropフックが終了するか、 .metadata.deletionGracePeriodSeconds の時間が経過した場合、kubeletがDockerデーモンにコンテナの終了を依頼します1 2。このとき、終了処理のタイムアウト時間として、以下の値が渡されます。 preStopフックが .metadata.deletionGracePeriodSeconds までに終了した場合: .metadata.deletionGracePeriodSeconds からpreStopフックの所要時間で引いた値(2秒以下だった場合は2秒に切り上げ) preStopフックが .metadata.deletionGracePeriodSeconds までに終了しなかった場合 2秒 コンテナの終了処理では、始めにコンテナにSIGTERMシグナルが送信されます。多くのアプリケーションでは、SIGTERMの受信を受けて終了処理を開始するように実装することが多いと思います(Gracefl Shutdown)。 ...

2020年9月18日 · 1 分 · hhiroshell