1. 概要
- 筆者が管理している Kubernetes のマニフェスト生成運用を変更した話です。
- テクニカルな話というよりは体験談です。
- 世間一般で言われているモダンな運用と、当方の事情を加味した落としどころを見つけた、という内容です。
2. 背景
- 筆者は Kubernetes 基盤や仮想サーバを含むシステム基盤のひな型を開発する業務を担当しています。開発したひな型を配布し、それをもとに複数の利用者がシステム基盤を構築しています。
- その中で、Kubernetes 基盤を構築するためのひな型を Ansible+Jinja2 テンプレート(以降 Jinja2)ベースで用意し、それを利用者に配布しています。
- Kubernetes のバージョンアップに伴い、ひな型を更新し、利用者に都度取り込んでもらっていますが、従来のひな型更新フローが複雑で、Kubernetes バージョンアップのたびに当方・利用者ともに大きな負荷がかかっていたため、運用改修に取り組みました。
3. 世間一般でのモダンなマニフェスト運用
まず、世間一般で言われているモダンなマニフェスト運用を整理します。
- 現代の Kubernetes 運用においては、マニフェストを個別に生成し、手動で
kubectl applyを実行することはアンチパターンとなりつつあります。 - 現在は、環境差分の管理効率化と、Git を信頼できる唯一の情報源(SSOT: Single Source of Truth)とするアプローチが主流です。
-
Kustomize による「オーバーレイ」管理
- 環境(Dev/Staging/Prod)ごとに YAML をコピー&ペーストする冗長さを排除する手法です。
- 特徴: ベースとなる共通マニフェスト(Base)に対し、環境ごとの差分(Overlay)を上書きするパッチ形式を採用します。
-
メリット:
kubectlに標準組み込みされており、学習コストが低く、マニフェストの原型を保っているため可読性が高いことが特徴です。
-
Helm による「パッケージング」とテンプレート化
- アプリケーションを一つの「チャート」としてパッケージ化し、変数を注入して生成する手法です。
- 特徴: Go Template 記法を用い、複雑な条件分岐やループ処理が可能。
- メリット: 汎用的なミドルウェアの導入(Redis、Prometheus など)や、構成が大きく変わる複雑なアプリケーションの配布・管理に適しています。現在は OCI レジストリ(コンテナレジストリ)でのチャート管理がモダンな手法です。
-
GitOps による「継続的デリバリー」(ArgoCD / Flux)
- マニフェストの適用を CI(Jenkins/GitHub Actions)から切り離し、専用のコントローラーに任せる手法です。Kubernetes 側から Git リポジトリをポーリングし、更新があった場合に定義を更新します。
- 従来の
kubectl applyなどによる適用は push 型と分類され、GitOps による継続的デリバリーは pull 型と呼ばれています。 - 特徴: Git リポジトリの状態を Kubernetes クラスタに自動同期します。
- メリット: 「ドリフト検出(あるべき姿と実際のズレの検知)」が容易になります。誰かが手動で設定を変更しても、自動的に Git の状態に戻されるため、堅牢な運用が実現します。また、kubectl apply を実行する側(オペレータなど)に強い権限を渡す必要がなくなり、セキュリティの観点でも強化されます。
-
どのような組み合わせを選ぶか
-
上記の運用手法は組み合わせて利用されます。
-
システム要件などに合わせて採用しますが、例えば以下のような組み合わせが考えられます。
アプローチ 推奨シナリオ Kustomize + GitOps 自社開発アプリなど、頻繁に変更が発生し、可読性を重視する場合の王道パターン Helm + GitOps 複雑な依存関係があるアプリや、サードパーティ製ミドルウェアを管理する場合 Kustomize + Helm Helm チャートを Kustomize でレンダリングして微調整するハイブリッド構成(高度な運用) -
例えば上記の
Helm+GitOpsのアプローチであれば、以下図のような構成となります。
-
4. 従来の運用フローとその課題
次に、ひな型のコード構成と、従来の運用フローについて説明します。
4-1. ひな型と利用者のコード構成
- ひな型となるコードでは、仮想サーバと Kuberentes を構築しており、双方の構築に必要な環境別変数を Ansible の変数として定義しています。
(ネーミングルール、サーバスペック、各種設定等) - 利用者は配布されたひな型を利用して既にシステム環境を構築しており、アップデートやリファクタリングを行う際には、出来る限り移行コストの少ない方式をとる必要があります。
4-2. 従来のマニフェスト運用フロー
-
利用するマニフェストの元ネタには以下の 3 種類あり、それぞれの種別に応じた対応をひな型開発担当にて行っていました。
- Helm チャート (公開): Helm として公開されているマニフェスト。values.yaml&手動で設計作りこみを行い、その後 Jinja2 化。
- マニフェスト (公開): GitHub などにそのまま登録されているマニフェスト。手動で設計作りこみを行い、その後 Jinja2 化。
- マニフェスト (オリジナル): オリジナルのマニフェスト。Jinja2 化。
-
ひな型チームにて設計を取り込んだ Jinja2 を完成させた後、利用者側に展開します。
-
利用者側では(必要に応じて)Jinja2 をカスタマイズし、それを Ansible の Template モジュールで展開しマニフェストを完成させます。
-
マニフェストの適用も Ansible にて実施しています。(
kubernetes.core.k8sモジュール) -
以下フロー図です。
- 水色系: k8s マニフェスト
- 赤系: Ansbile 関連
- 黄緑系: Helm 関連
- 白系: その他
4-2. 従来の運用の課題
-
仕組みが複雑で手間が大きい
- パブリックな Helm チャートを一度展開し、その差分を Jinja2 に落とし込んでいましたが、どちらもテンプレートの仕組みなので「二度手間」となっていました。
-
設計意図が把握しづらい
- 利用者に渡す段階では、ひな型としての設計意図が Jinja2 テンプレートに埋め込まれた状態になっており、プロダクトとしてのデフォルト設計と区別できませんでした。
-
利用者の負荷が大きい
- ひな型を取り込む際、従来の Jinja2 形式では中身がそのまま提供されており、利用者に関係のない変更(例えばオプションで設定できる内容が増えた場合)についても内容を逐次確認する必要があり、取り込みに負荷がかかっていました。
-
適用のリスク
- 従来の運用フローでは、いったん Jinja2 をマニフェストに展開し、それを適用する、という風に処理が分かれているため、古いファイルが適用されるリスクがありました。
- また、各所に手動運用が介在しており、可能な範囲で自動化を推し進める必要がありました。
5. 改修後の構成
5-1. 改修方針
今回は、モダンなマニフェスト運用を目指しながら、現状の運用に即して、利用者になるべく負荷のかからない構成を取ることとしました。
-
マニフェストは Jinja2 テンプレートではなく Helm チャートとして用意・配布する
- 従来はさまざまな方法で最終的に Jinja2 テンプレートに変換していましたが、マニフェストを管理する際のデファクトスタンダードである Helm に集約します。
- マニフェストの元ネタ別の対応は以下の通りです。
- Helm チャート(公開): 基本的にはそのまま利用することができ、バージョンアップの際の取り込み負荷も低減します。Helm チャートの仕組み上どうしても加えられない設定が必要な場合は、明記の上カスタマイズします(次項で詳細説明)。
- マニフェスト(公開): Helm としても公開されているものはそちらに切り替え、そうでないものについては Helm チャートをオリジナルで作成します。
- マニフェスト(オリジナル): Helm チャートをオリジナルで作成します。
- Helm としてひな型を提供することで、利用者はチャート自体の具体的な内容を気にする必要はなくなり、入出力(values.yaml と展開されたマニフェスト)のみに留意すればよくなります。
- モダンなマニフェスト運用でも、Helm を利用することが前提となっています。
-
Kustomize は利用せず、どうしても改修が必要な部分については、ひな型側で Helm チャートを改修する
- Helm の values.yaml で変更しきれない内容については、Kustomize で編集する運用が一般的ですが、以下の観点から Kustomize は利用せず、(当面は)Helm チャート自体の部分的改修で運用します。
- パブリックな Helm チャートは、大抵のシステムの要件に応じて変更できるよう予め values.yaml の変数が用意されていることがほとんどで、今回対象となるシステム基盤で values.yaml で補いきれない部分はごくわずかです。
- パブリックな Helm チャートはなるべく改修せずに運用する方が望ましいですが、今回すでに Helm という新しい技術要素を追加しているため、利用者目線でなるべくシンプルにし、移行負荷がかからないようにすることを優先しました。
- Helm の values.yaml で変更しきれない内容については、Kustomize で編集する運用が一般的ですが、以下の観点から Kustomize は利用せず、(当面は)Helm チャート自体の部分的改修で運用します。
-
values.yaml の環境別のレイヤー部分は、Ansible のテンプレート機能で生成する
- 先述の通り、従来より環境別の変数がすべて Ansible に集約されて管理されています。
- values.yaml は本来は静的に宣言し、いくつかのレイヤーの内容を複数適用することで運用することが望ましいと考えられますが、前項と同様、環境別変数に手を入れず、そのまま使う(ロジックの部分だけを入れ替える)ことで、利用者としての移行負荷を最低限に抑えることを優先しました。
- ひな型としての設計を盛り込んだ values.yaml は静的ファイルとして利用者に配布し、利用者別・環境別で内容が変化する部分は、従来の環境変数を使って values.yaml に展開できる jinja2 テンプレートを用意し配布し、この 2 つの values.yaml を利用することで完成する仕組みとしました。
- 従来はマニフェストの Jinja2 テンプレートに混在していた設計意図が、values.yaml ファイルに集約(ひな型設計・環境別設計)されることで、可読性が向上しました。
-
実環境への適用の仕組みは従来と同じく Ansible による Push 型とする(ただし、フラットファイルのマニフェストは利用しない)
- モダンなマニフェスト運用では、GitOps による pull 型が一般的ですが、既存の利用者の運用まで大きく変更するのは難しいため、今回の改修では一旦 Ansible による Push 型を採用しています。
- 一方で、今回の改修により、最終的な成果物が Helm チャート + レイヤードな values.yaml となっているため、今後、ArgoCD などを使った GitOps への切り替えがスムーズになることを前提とした構成としています。
- 従来はいったんマニフェストをフラットファイルとして生成し、それをさらに適用する、という運用だったため、ファイル生成と適用の間で予期せぬ操作が入った場合、想定通りの内容が適用されないリスクがありましたが、Helm による適用の場合、Helm チャートから直接 Kubernetes に適用(
kubernetes.core.helmモジュール)できるため、そのリスクが軽減されています。- 先述の環境別 values.yaml については、jinja2 テンプレートとして配布していますが、フラットファイルとしては展開せず、
lookupモジュールのテンプレート機能を使うことで、環境別変数の内容を直接参照しています。 - 実環境に適用するためのマニフェストのフラットファイルを生成しなくてよくなったため、Secret などの管理の面でセキュリティ性が向上しています。
- 先述の環境別 values.yaml については、jinja2 テンプレートとして配布していますが、フラットファイルとしては展開せず、
5-2. 改修後の運用フロー
以下フロー図です。
6. まとめ
- Kubernetes マニフェストの管理方法を改修し、モダンなマニフェスト運用を指向しつつ、Ansible+Helm による運用を実現しました。
- ひな型と利用者という関係性や、環境差分が Ansible に集約されているという状況を加味した上で、本来のゴールへの道筋を立てることができました。
- 最新の運用を取り入れたくても、なかなか難しいシチュエーションも多々存在するかと思いますので、参考になれば幸いです。