はじめに
こんにちは!アソビューSREチームの( @kassshi_dev )です。
日々サービスを運営していると、開発や運用の方法も少しずつ変化していきます。特に事業やチームの成長に伴って、以前は十分だった仕組みが少しずつ現状にフィットしなくなることもありますよね。
そこで今回は、Kubernetesのmanifest管理をKustomizeからHelmfileへ移行した事例をご紹介します!
きっかけは、事業成長によるアプリケーション数の増加に伴い、既存のmanifestの運用負荷や視認性低下が顕著になったことでした。より柔軟かつ保守しやすいmanifest管理方法の実現手段としてHelmfileを選択しました。 移行プロセスについてご紹介します!
前提
- マネージドサービス : EKS
- デプロイ : ArgoCD
- ArgoCD Application数 : 155個 (1stパーティ(自前のアプリケーション) + 3rdパーティ(主にOSS))
manifest管理の歴史
アソビューではKubernetes導入以降、以下の通りmanifest管理方法の変遷を辿ってきました。 今回はフェーズ2 -> フェーズ3のお話になります。
- フェーズ1 : 生manifest×CIOps
- フェーズ2 : Kustomize×GitOps
- フェーズ3 : Helmfile×GitOps
移行の背景
フェーズ2の運用について
Kustomize は非常に便利なmanifestのカスタマイズツールで、NamespaceTransformer/ReplicaCountTransformer/ImageTagTransformer/PatchTransformer/ConfigMapGeneratorなど多くの機能が用意されています。
ディレクトリ構成は base + overlays を採用し、base配下にはユースケース毎に必要なリソース郡をまとめたテンプレートを定義していました。また、Kustomizeの Exec KRM functions を利用し、Shell Scriptによるmanifest出力を実現するカスタムプラグインを実装していました。Shell Script はbase配下のテンプレートにoverlaysから渡したパラメータを埋め込みmanifestを出力します。 以下のようなイメージになります。
ディレクトリ構成
root
├── base
│ └── components
│ └── java-app
│ ├── kustomization.yaml
│ └── config.yaml
│
└── overlays
└── staging
└── java-app-1
├── kustomization.yaml
└── generators.yaml
ファイル
# base/java-app/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - config.yaml --- # base/java-app/config.yaml apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: app resources : requests: cpu: < requests_cpu > memory: < requests_memory > # overlays/staging/java-app-1/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: staging-java-app-1 generators: - generators.yaml --- # overlays/staging/java-app-1/generators.yaml apiVersion: kustomize.asoview.com/v1alpha1 kind: ResourceGenerator metadata: name: resource annotations: config.kubernetes.io/function: | exec: { path: "resourcegenerator.sh" } spec: resources: - path: ../../../base/components/java-app replacements: requests_cpu: 128m requests_memory: 128Mi
フェーズ2における課題
カスタムプラグインは Shell Script で実装しており、機能としては置換と上書きのみでした。 条件分岐でリソースの有無を判定したり、新しいリソースを作成したりする処理や、ループによるリスト構造の表現はテンプレート化しにくいものでした。さらに Shell Script が年々複雑になり、テンプレートを運用するモチベーションも下がっていました。 テンプレート機能の柔軟性がないこと、テンプレートの運用モチベーション低下により課題が多く発生しました。
PatchTransformerの多用による視認性の低下
テンプレートの運用モチベーションが低下した結果、各アプリケーションがテンプレートの外側でPatchTransformerによるreplaceやdeleteを多用するようになり、manifestの見通しが悪化してしまいました。
認知負荷の高騰
Kubernetes のリソースには依存関係がある場合があります。しかし、テンプレート側では条件分岐を使って「リソースの有無を判定して作成する」といった処理を定義できません。そのためアプリケーション側が依存関係を把握して各テンプレートの参照定義する必要性がありました。SREチーム以外のメンバーによる新規アプリケーション作成・更新の難易度がかなり高くなっていました。
テンプレートの増加
上記のように、条件によって必要となるリソースが異なる場合があるため、その条件ごとにテンプレートを作成する必要がありました。その結果、テンプレートの数が徐々に増えていきました。
ツール検討
柔軟なテンプレート機能を実現するためにHelmを採用する方針は固まっていましたが、Helmの扱い方にいくつか選択肢がありました。 以下3つの候補からPros/Consを整理してSREチーム内で議論しました。
- Helm : Kubernetesにおけるパッケージマネージャ。運用実績が多い。
- Helmfile : Helm Chartを宣言的にデプロイできるCLI。
- kustomize built-in helm : KustomizeでHelmを部分的に利用できるビルトイン機能
以下の理由によりHelmfileに決定しました!
- 宣言的デプロイができる
- helmfile.yaml1つで複数リリースを一元管理できて視認性がよい
- テンプレート以外のリソースも作成できる
移行前後の比較
以下が移行前後のmanifestのイメージになります。
移行前
apiVersion: kustomize.asoview.com/v1alpha1 kind: ResourceGenerator metadata: name: resource annotations: config.kubernetes.io/function: | exec: { path: "resourcegenerator.sh" } spec: replacements: env: production service_name: java-app eks_cluster: production resources: - path: ../../../../base/components/app-java-grpc/v1 replacements: app_name: java-app requests_cpu: 1 requests_memory: 256Mi limits_cpu: 1 limits_memory: 256Mi env: ENV: production env_from: secret: - eso - path: ../../../../base/components/virtualservice/v1 replacements: app_name: java-app - path: ../../../../base/components/hpa/v1 replacements: app_name: java-app min_replicas: 1 max_replicas: 3 - path: ../../../../base/components/rollout-istio/v1 replacements: app_name: java-app canary_pause_duration: 600s - path: ../../../../base/components/external-secret/v1 replacements: app_name: java-app external_secret: parameters: HOGE: HOGE
移行後
namespace: production-java-app templates: default: values: - basename: java-app env: production service_name: java-app releases: - name: java-app chart: ../../../../helm/app-web/1.0.0 inherit: - template: default values: - values-ci.yaml - source: java deployment: requests: cpu: 1 memory: 256Mi limits: cpu: 1 memory: 256Mi env: ENV: production env_from: secret: - es hpa: enabled: true min_replicas: 1 max_replicas: 3 service: grpc: enabled: true virtualservice: enabled: true canary: enabled: true canary_pause_duration: 600s - name: es chart: ../../../../helm/externalsecret/1.0.0 inherit: - template: default values: - name: eso parameters: HOGE: HOGE
アプリケーション側
参照するテンプレートが減り、1つのテンプレートに対して設定値やフラグを渡すだけでよいので直感的にわかりやすくなりました。
テンプレート側
Helmテンプレートで作成したオールインワンテンプレートにより柔軟な設定が可能になりました。 以前は同じJavaのアプリケーションでもHTTPサーバで稼働する場合とgRPCサーバで稼働する場合で作成するリソースや設定が異なるため、似たような設定が存在するテンプレートが複数ありました。これらのメンテナンスコストも不要になり、また横串で全アプリケーションに設定の追加をしたい場合も非常に楽です。
移行プロセス
アプリケーションを1stパーティ(自前のアプリケーション) と3rdパーティ(主にOSS)に分類し、影響範囲がある程度把握できている1stパーティから移行作業を開始しました。 また今回の移行プロセスを円滑に進めるにあたっての要点は以下だったため、事前準備を一定実施して移行作業を開始しました。
- Kustomize と Helmfile における出力結果manifestに差分がないことを確認できること
- ArgoCDのプラグインをKustomize から Helmfile へ容易に切り替えられること
事前準備
- Helmテンプレート作成
- 共通的に必要なリソース及び条件分岐を定義して、移行作業プロセスの中でHelmテンプレートをブラッシュアップする方針としました
- 移行前後のmanifest出力結果の差分を容易に確認できる
- CIで出力結果の差分をPRにコメントします
- KustomizeとHelmfileを併用可能にする
- ArgoCDのサイドカープラグインにKustomizeとHelmfileを共存させつつ、デフォルトではKustomizeでデプロイします
- Kustomize -> Helmfile にmanifestを変換するスクリプト作成
- 既存の
kustomization.yamlとgenerators.yamlから、helmfile.yamlを作成するスクリプトを実装することで地道な変換作業の時間を大幅に短縮します
- 既存の
ステップ1 : [1stパーティ] 各アプリケーションのhelmfile.yamlを作成してリリースする
事前準備で作成した変換スクリプトを利用してhelmfile.yamlを作成します。出力結果のmanifestが既存のものと差分がない状態がゴールです。 PRコメントにmanifestの差分が通知されるので、レビューを円滑に進めることができました。
リリース時点ではKustomizeでデプロイされるため本番影響はありません。
ステップ2 : [1stパーティ] ArgoCDのプラグインをKustomizeからHelmfileに切り替える
アソビューではApplicationSetを利用して複数のアプリケーションを管理しています。
ApplicationSetではgeneratorsでプラグイン名を指定してtemplateにパラメータとして渡すことで、アプリケーション毎にプラグインを切り替えるようにしました。
これにより安全にロールバックも可能です。
ApplicationSetのmanifestは以下のようなイメージです。
apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: name: installs spec: generators: - merge: mergeKeys: - path.basename generators: - matrix: generators: - git: repoURL: hoge revision: main directories: - path: >- hoge/* - list: elements: - targetRevision: main plugin: kustomize-plugins - list: elements: - path.basename: helmfile-app plugin: helmfile-plugin template: metadata: name: '{{path.basename}}' spec: project: default source: path: '{{path}}' plugin: name: '{{plugin}}' # ここでpluginを変数にすることで容易に切り替え可能
ステップ3 : [3rdパーティ] 各アプリケーションのhelmfile.yamlを作成してリリースする
各OSSが提供しているHelm Chart を見て現状と差分が極力ないように values を定義していきます。 この辺は生成AIを利用しつつ、CIで差分を確認しながら正確性と速度のバランスを取りながら進めました。
ステップ4 : [3rdパーティ] ArgoCDのプラグインをKustomizeからHelmfileに切り替える
1stパーティ同様です。
ステップ5 : Kustomizeの関連リソース・ファイルのお掃除
以下を中心に削除することができました。
- ArgoCDのKustomizeプラグイン
- Kustomizeのmanifestファイル
- 独自実装のカスタムプラグイン
結果

移行によりテンプレート数は 83%削減、ファイル数は 58%削減、コード行数は 49%削減 されました。 また、移行後の構成については以下に統一しています。
helmfile.yaml: 参照するChart及びvaluesを定義values-ci.yaml: CIで更新するimageタグなどを定義(helmfile.yamlのGitのLogを見やすくするためCIで頻繁に更新されるフィールドだけファイルを分割しています)
よかったこと
manifestの視認性向上
helmfile.yamlを見るだけで構成を把握できるようになりました
テンプレートの運用モチベーション向上
既にテンプレートのアップデートが継続的に実施されています
3rdパーティアプリケーションの設定最適化
OSSが提供しているHelm Chartに対してvaluesを定義していくプロセスにおいて、OSSの各設定への理解が深まり、不要な設定の削除や、設定の最適化まで行うことができました
丁寧な事前準備によるPRレビュー効率化
特にCIによる差分通知はPRレビュー時間を大きく短縮させました。一目瞭然で問題ないことを確認できたので非常によかったです。
こうすればよかった
3rdパーティから移行するのが良かったと思います。3rdパーティが提供しているHelm Chart は整理されていて、自前のHelmテンプレート作成時に参考にすることで初版のクオリティがもっと上がったと思います。
まとめ
Helmfileに移行してよかったです! 組織規模やプロダクトのフェーズの変遷に伴い、都度最適なツールや手段の選定を柔軟に行うことで、よりよいプラットフォームの運用を実現できると思います。
他にもCIやモノレポ運用など最適化の余地があるので、引き続きSREチームがEmbedded SREやプロダクトチームと連携しながら施策を進めていく予定です。
最後に
アソビューではより良いプロダクトを世の中に届けられるよう共に挑戦していくエンジニアを募集しています。 カジュアル面談も行っておりますので、お気軽にエントリーください! お待ちしております。 www.asoview.co.jp