Google Cloud Champion Innovators Advent Calendar 2024 の 10 日目の記事です。
今年も GKE (Google Kubernetes Engine) は数々のアップデートが行われました。
例えば以下のように、日々新しい機能が追加されたり、より良い機能に改善されたりといったアップデートが行われています。
これらのアップデートの多くは、コントロールプレーンのアップグレードや、ノード(データプレーン)のアップグレードという形でクラスタに反映されます。
ノードのアップグレードでは、例えば仮想マシンの OS イメージや kubelet のバージョンが上がるなど、ユーザにも見えやすいところが変更されるため、何が起きているのか比較的イメージしやすいのではないかと思います。
一方、コントロールプレーンのアップグレードについてはどうでしょうか。
GKE を使ったことのある方であれば、なんとなく Kubernetes のバージョンと連動する形で様々な変更が行われているのであろうというイメージはあるかと思います。
Autopilot のユーザーであれば尚のこと、Standard のユーザーよりもコントロールプレーンの変更を気にする機会は少ないかもしれません。
とはいえ、利用している GKE クラスタが Autopilot であっても Standard であっても、コントロールプレーンのアップグレードに伴って自身のクラスタにどのような変更が発生しているかを理解しておくと、障害対応や最適化を行う際にそうした知識が役立つことがあります。
そこで、本記事の前半では GKE のコントロールプレーンのアップグレードとは何かについて説明し、後半ではコントロールプレーンへの理解を深める過程で生成 AI に助けてもらうと効率化できるかもしれない一例をご紹介したいと思います。
GKE コントロールプレーンのアップグレード
コントロールプレーンとシステムコンポーネントの関係
コントロールプレーンのアップグレードについてみていく前に、まずはコントロールプレーンとは何かについて簡単に説明しておきたいと思います。
コントロールプレーンと聞くと、Google 管理の VPC に展開される Kubernetes API Server や Scheduler が真っ先にイメージされると思います。
実際、公式ドキュメントでの説明も、Google 管理の VPC に展開されるコンポーネントのことを中心に、コントロールプレーンとは何かを説明しています(以下のアーキテクチャ図は、該当の記載のある公式ドキュメントから引用しているものです)。
コントロール プレーンは、Kubernetes API サーバー、スケジューラ、コアリソース コントローラなどのプロセスを実行します。GKE は、クラスタの作成から削除までのコントロール プレーンのライフサイクルを管理します。コントロール プレーン上で実行される Kubernetes バージョンのアップグレードも管理の対象になります。
また、以下のような記載もあります。
GKE Autopilot は、コントロール プレーン、ノード、システム コンポーネントなど、クラスタの基盤となるインフラストラクチャ全体を管理します。 GKE Standard モードの場合、GKE がコントロール プレーンとシステム コンポーネントを管理し、ユーザーがノードを管理します。
この記載からもわかるように、GKE における Google とユーザの管理範囲は、「コントロールプレーンにあるもの = Google, ノードにあるもの = ユーザ」のように単純に分けることができません。
各々の GKE クラスタで Google が管理しているのは、コントロールプレーンと呼んでいる Google 管理の VPC に展開されるコンポーネントだけではないのです。
Google 管理の VPC に展開されるコンポーネント(API Server など)をアップグレードする際には、そのクライアントとしてノード上に展開されるコンポーネントも併せてアップグレードする必要があります。
(この「Google の管理によってノード上に展開されるコンポーネント」を公式ドキュメントでは システムコンポーネント と呼んでいるようです)
したがって、実際にコントロールプレーンのアップグレードを行うと、ユーザのコンテナが稼働するノード上に Google 管理のもと展開されているリソース(kube-system や namespace で稼働するコンテナ、カスタムリソース、Webhook など)にも変更が生じます。
ノードに展開されているシステムコンポーネントの見分け方は公式ドキュメントでの明確な記載がありませんが、基本的にはユーザが展開した覚えのない Kubernetes リソースが大体そうです(例: kube-system に展開されている metrics-server や konnectivity-agent など)。
GKE のバージョニングとアップグレード
GKE のバージョン体系には、x.y.z.gke-N といったフォーマットが使われています(参考)。
x.y.z の部分は Kubernetes のバージョンと一致するようになっており、-gke. 以降の部分は GKE としてのより細かいバージョニングを表しています。
コントロールプレーンのアップグレードによって生じる変更を把握するには、Kubernetes のバージョンが上がる場合(例えば、1.30.5-gke.~ から、1.31.1-gke.~ にマイナーバージョンをアップグレード)であれば、Kubernetes のリリースノートが役に立ちます。
パッチバージョン毎にどのような修正が行われたのか、どのような破壊的変更があったのかなどを事前に把握できますし、必要とあらばソースコードを確認することもできます。
GKE が Kubernetes と同じ挙動をするとは限りませんが、Kubernetes のバージョンに対応している部分(x.y.z-gke.N の x.y.z の部分)の変更は基本的には Kubernetes と同じような変更が行われているはずと考えることができます。
一方、GKE としてのパッチリリースは少々扱いが異なります。
まず、オープンソースの Kubernetes よりも遥かにパッチリリースが高頻度であるため、同じ Kubernetes バージョンに対して複数の GKE バージョンが存在することになります。
-gke. 以降の部分は、後からリリースされる(より新しい)バージョンほど、数字が上がっていくようになっています(GKE のバージョン表記の説明、リリースポリシー、サポートポリシーなどについての詳しくはこちらを参照)。
有効な GKE バージョンリストの例:
- 1.31.1-gke.1846000
- 1.30.5-gke.1443001 # Kubernetes 1.30.5 に対する、より新しい GKE パッチリリース
- 1.30.5-gke.1014003 # Kubernetes 1.30.5 に対する、より過去の GKE パッチリリース
- 1.29.9-gke.1496000
Kubernetes と GKE でパッチリリースの頻度に差があるのは、GKE が Kubernetes だけでできているサービスではないからです。
Kubernetes は複数のオープンソースを内包していますが、それを Google Cloud のサービスとして成立させている GKE では、さらに多くのオープンソース、あるいはプロプライエタリなプロダクトが使われています。
例えば クラスタ内 DNS コンポーネントには kube-dns を、Cloud Logging へのログ転送を行う仕組みには fluentbit を、Node Local DNS には CoreDNS を使用していますし、イメージや Configmap からベースプロダクトが推測できないプロプライエタリなコンポーネントもあります。
GKE パッチリリースでは、こうしたシステムコンポーネントのセキュリティアップデートやバグ修正が頻繁に行われています。
したがって、GKE ユーザとしては、Kubernetes バージョンの差異による変更だけではなく、GKE パッチバージョン間の変更についても意識を向けておくことが重要です。
ただし、GKE パッチバージョン間の変更点は、Kubernetes のマイナーバージョン/パッチバージョンとは異なり、事前にユーザが知ることは(基本的には)できません。
Security bulletins や release notes に明記されている場合は別ですが、通常は、コントロールプレーンのアップグレードを行ってみて初めて、fluentbit によるロギングの仕組みで Configmap がアップグレードされた、Node Loacl DNS に使用される CoreDNS のイメージのバージョンがアップグレードされた、といった変更を知ることになります。
こうした GKE パッチリリースによるシステムコンポーネントの変更の多くはワークロードに影響しませんが、だからといって無関心で良いとは言えません。
コントロールプレーンのアップグレードに伴うシステムコンポーネントへの変更が、自分たちのワークロードの信頼性に一切影響しないとは言い切れないからです。
例えば、以下のようなシチュエーションが考えられます:
- ネットワークアクセスを厳密に制限しているクラスタでは、コントロールプレーンのアップグレードに伴ってシステムコンポーネントのイメージレジストリが変更された場合、ノードが正常にイメージを pull できなくなるかもしれません
- コントロールプレーンのアップグレードに伴って Node Local DNS の Corefile が変更された場合、ノード内の名前解決プロセスに何らかの影響があるかもしれません
- Cloud Logging のログシンクを利用して GKE の監査ログから何らかのイベント駆動処理をしている場合、コントロールプレーンのアップグレードに伴ってログのフィールドや出力傾向が変わると処理に影響があるかもしれません
このように、コントロールプレーンのアップグレードに伴うシステムコンポーネントの変更は、基本的にはユーザ透過なはずではあるものの、自分たちのワークロードの信頼性に絶対に影響しないとは言い切れません。
コントロールプレーンのアップグレードに伴う変更がワークロードに影響し得るのであれば、ユーザとしてはアップグレードの際にどのような変更が行われたのかを可能な限り知りたい、という場合もあるのではないでしょうか。
システムコンポーネントの変更を生成 AI に教えてもらおう
前述のような背景から、GKE パッチバージョン間の変更点を知るには、アップグレード後の GKE クラスタを実際に観察してみるほかありません。
ワークロードへの影響があるか否かは、アップグレード毎に回帰テストなどを行うことで確認するのが一般的かと思います(費用対効果の問題なので、GKE パッチリリースの差分については回帰テストなどでワークロードに問題が生じなければ意識しない、という判断ももちろんあり得るでしょう)。
もう少し踏み込んで、システムコンポーネントに具体的にどのような変更があったかを知りたいという場合には、システムコンポーネントのリソース(YAML)がアップグレードの前後でどのように変更されたかを追跡する必要が出てきます。
そのために、例えば、kubectl コマンドなどで取得したシステムコンポーネントの YAML を、単純なテキスト差分比較ツールなどで洗い出すことは可能です。
しかし、以下のような理由から、現実的な作業量ではないとみなされることもあるかもしれません。
- Pod の IP アドレス変更やインスタンス ID の変更など、アップグレードに伴って当然に発生する本質的な変更ではない差分情報が多分に含まれる(ノイズが多い)
- 情報量が多く、人力で差分を調査するには時間がかかりすぎる
- GKE パッチバージョンは Kubernetes パッチバージョンよりはるかに高頻度でリリースされるため、GKE パッチアップグレードの都度あらゆる変更差分を調査する、という運用は労力に見合わない
こうした状況を緩和する手段の 1 つとして、この記事の以降では、生成 AI の助けを借りてみたいと思います。
以下は、システムコンポーネントの一部として kube-system namespace の Deployment, Daemonset, Configmap の YAML をアップグレード前後で取得し、その差分を Gemini に要約してもらった結果を示しています。
対象となっている GKE クラスタは検証用に新規作成した Standard クラスタで、バージョンはアップグレード前が 1.30.5-gke.1014003, アップグレード後が 1.30.5-gke.1713000 となっています。
モデルは gemini-pro-1.5-002, Temperature は 0.3 としました。
プロンプトの良し悪しや、パラメータの最適化については、この記事の主題ではないため一旦置いておきましょう。
内容としては、calico-node や filestore-node などのコンテナイメージが更新されたことが変更点として挙げられています。
実際にクラスタ内のリソースを確認したところ、コントロールプレーンのアップグレードに伴ってシステムコンポーネントにこれらの変更が発生していることを確認できました。
たまたまかもしれませんが、イメージのダイジェスト値などまでは変更点として列挙していないため、人が見ても比較的わかりやすいリストになっているような気もします。
回答に誤りが含まれる可能性も否定はできませんが、システムコンポーネントの変更を把握するうえでのヒントとなる情報をある程度の精度で短時間に得られるのであれば、こういう生成 AI の使い方もアリなのではないでしょうか。
ユーザとしてはそもそも GKE のコントロールプレーンのアップグレードに伴う変更のすべてを知ることはできないため、100 点が取り得ない以上、生成 AI でサクッとおおよその回答が得られるならそれで十分、という向きもあるかもしれません。
今回は kube-system namespace 内の一部のリソースだけを抜粋した比較としましたが、これだけでもアップグレード前後の YAML を合わせると入力トークンは 500,000 近くになっています。
入力を分割したり、不要な情報を削除するなどしてスリム化することは可能ですが、Gemini の場合は現在最大で 2,000,000 トークンを扱えるため、そうした前処理をせずとも大量の入力トークンを伴うリクエストをこなすことができます。
入力可能トークンの多さは、こうした思い付きを手軽に試せるという面でもメリットがありますね。
まとめ
この記事では、前半で GKE のコントロールプレーンのアップグレードによって発生するシステムコンポーネントの変更と、それらの変更がワークロードに与え得る影響について説明しました。
後半では、アップグレード前後の変更点を簡易に把握するための手段として生成 AI (Gemini) を活用する一例をご紹介しました。
GKE クラスタのアップグレードとの付き合い方はユーザやシステムの要件によって様々ですが、時には生成 AI を運用に取り入れてみるとうまくいく場面もあるかもしれません。
コントロールプレーンにまつわる余談
- 冒頭、GKE のアップデートの「多く」は、コントロールプレーンやノードのアップグレードによってクラスタに反映されると書いたとおり、例外もあります。一部のアップデートは既存のクラスタに後から導入することができないため、クラスタを新規に作成して(また、必要な場合は既存のクラスタからワークロードをマイグレーションして)使用することになります
- GKE は自動アップグレードの仕組みを持っており、コントロールプレーンは Autopilot であろうと Standard であろうと、またリリースチャンネルを使っていようといなかろうと、ある一定の時期が来ると自動でアップグレードされるようになっています(ここではコントロールプレーンの話をしていますが、ノードも同様にある一定の期限を迎えると自動でアップグレードされるようになっています)。自動アップグレードが行われる時間帯や、自動アップグレードでアップグレード可能な範囲は、メンテナンスポリシーとメンテナンスの除外設定によってある程度ユーザでもコントロールすることが可能です(参考)。最近は GKE Enterprise のフリートの機能を使うことで複数クラスタのロールアウトの順序制御を行うといったことも可能になっています(参考)
- 新規に作成したクラスタと、継続運用しているクラスタでは、同じバージョン(x.y.z-gke.N)であっても、ノードに展開されるシステムコンポーネントの構成は異なります。また、有効化しているアドオンやオプションの違いによっても展開されるコンポーネントが変わります。Autopilot と Standard でも展開されるシステムコンポーネントが異なります
- Google 管理のもとでクラスタに展開されているシステムコンポーネントは、ものによってユーザが参照できないことがあります。例えば、~AccessReview 系のリソースは、カスタムリソースとしてクラスタに展開されているものの、ユーザが参照することはできません。また、kube-system namespace などの binding リソースも同様に、ユーザが参照することはできない仕様になっています
- GKE のアップデートには、コントロールプレーンやノードに直接デプロイされない更新も存在します。それらのほんの一部ですが、最近の例を挙げると、限定公開クラスタにおいてコントロールプレーンとデータプレーンの VPC 間の接続方式が VPC Peering から Private Service Connect へと変更されています。また、Service 用のセカンダリIPアドレスレンジは Google 管理のアドレス範囲が利用可能となり、VPC 上のアドレス範囲を予約する必要がなくなるなどのアップデートが行われています(参考)
- Google の管理している VPC 側の実装はオープンソースの Kubernetes と同じではありません。外から見れば Kubernetes API Server や Scheduler のような振る舞いはしているものの、オープンソースと同じ実装とは限りません。最近の話題で例を挙げると、GKE のコントロールプレーンでは Etcd を Spanner に移行しつつある という話もあります