helmfileの構成と利用について

helm
helmfile
kubernetes

2024-09-16

#目次

#はじめに

今回は、 helmfileの構成と利用について紹介します。
helmfileの構成はどのようにするのが良いでしょうか。
helmfileの公式サイトにベストプラクティスの例がありますが、いろんな書き方ができてしまうため、悩みどころでした。

いくつかのプロジェクトでhelmfileを使ってきた経験から、公式のベストプラクティスを参考に、より使いやすい構成を考えてみました。

また、よく利用するhelmfileの機能についても説明します。

#helmとは

Helm は、Kubernetes アプリケーションの管理を支援します。Helm チャートは、最も複雑な Kubernetes アプリケーションの定義、インストール、およびアップグレードを支援します。

チャートは簡単に作成、バージョン管理、共有、公開できるので、Helm の使用を開始し、コピー & ペーストはやめましょう。

Helm は CNCF の Graduated プロジェクトであり、 Helm コミュニティによって管理されています。

出典:Helm

公式サイトの説明の通りですが、Kubernetesを使う上で、helmは非常に便利なツールです。

helmのチャートは、公開されているものが多く、Artifact Hubなどで検索することができます。

postgres, redis, nginx, prometheus, grafanaなど、よく使うアプリケーションのチャートが公開されているため、自分でマニフェストを一から用意する必要がありませんので、非常に便利です。

helmをこれから使い始めようという方は、さくらインターネットさんの以下の記事が参考になります。

事実上の標準ツールとなっているKubernetes向けデプロイツール「Helm」入門 | さくらのナレッジ

#helmfileとは

Helmfile is a declarative spec for deploying helm charts. It lets you...

  • Keep a directory of chart value files and maintain changes in version control.
  • Apply CI/CD to configuration changes.
  • Periodically sync to avoid skew in environments.

To avoid upgrades for each iteration of helm, the helmfile executable delegates to helm - as a result, helm must be installed.

出典:GitHub - helmfile

公式の説明では、helmfileはhelm chartをデプロイするためのdeclarative specであると説明されています。
declarative specというのを、日本語でどう訳すかは難しいですが、helmではどのバージョンのチャートを使うか、どのようなvaluesを指定するかなど、細かく指定していく必要があります。helmfileでは、それらをファイルに宣言して、helmfileコマンドでデプロイすることができます。terraformでも宣言的という言葉が使われますが、terraformがインフラを宣言するのに対して、helm,helmfileはk8sのリソースを宣言するという風に捉えると理解しやすいかもしれません。

helmfileで宣言した情報はファイルに保存できるので、バージョン管理がしやすく、CI/CDにも適しています。Gitで管理して、チームのメンバーと共有することができます。

また、開発環境や本番環境など、環境ごとに異なるvaluesを指定したい場合にも、helmfileは有効です。

#なぜhelmを使うのか

  • 公開されているhelm chartを使いたい
  • secretの管理が楽
  • マニフェストをapplyする前に差分チェックしたい

#なぜhelmfileを使うのか

  • k8s全体のリソースを宣言的に管理したい
  • 環境ごとに異なる設定を管理するため
    • kubeContextの切り替え
    • values, secretsの切り替え
    • secret keyの切り替え
    • values, secretsからテンプレートを使って最終的なvaluesを生成する

似たようなツールに、kustomizeがあります。kustomizeは、主にk8sのマニフェストを管理するためのツールですが、helm chartは扱えません。kustomizeを以前のプロジェクトで使っていましたが、CRDのリソースを扱うのが難しかったり、helm chartは別で管理する必要があり、課題がありました。
ただ、kustomizeは、オーバーレイしてマニフェストを上書きしていくので、helmのようなテンプレートよりは理解・習得しやすいように思います。

#helmfileの構成

それでは、helmfileの構成について説明します。

いくつか要件があります。

  • できるだけDRY(Don't Repeat Yourself)にしたい
  • values, secretsを環境ごとに分けられる
  • values, secretsを1箇所にまとめて見通しを良くしたい
  • values, secretsを相対パスで管理したい

以下のような構成にしました。後述にて、各ファイルの説明をします。

├── charts
│   └── <your app>
├── bases.yaml
├── environments.yaml
├── templates.yaml
├── helmfile.yaml
└── releases
    └── <your app>
        ├── config
        │   ├── default-secrets.yaml
        │   ├── default-values.yaml
        │   ├── stage-secrets.yaml
        │   ├── stage-values.yaml
        │   ├── prod-secrets.yaml
        │   ├── prod-values.yaml
        │   └── values.yaml.gotmpl
        └── helmfile.yaml

<your app>は、placeholderです。適宜変更してください。

#各ファイルの説明

  • charts/

    自分で作るhelm chartを管理するディレクトリです。
    以下のコマンドでhelmのchartを作成します。

helm create <your app>
  • bases.yaml
bases:
  - ../../environments.yaml
  - ../../templates.yaml

basesでは、environments.yamlとtemplates.yamlを読み込むようにしています。

これらは、すべてのリリースで共通の設定です。

./releases/**/helmfile.yamlから読み込むため、相対パスであることに注意してください。

  • environments.yaml

環境ごとの定義を記述します。

environments:
  default:
    kubeContext: default
  stage:
    kubeContext: stage-ctx
  prod:
    kubeContext: prod-ctx
---
environments:
  {{ .Environment.Name }}:
    kubeContext: "{{ .Environment.KubeContext }}"
    missingFileHandler: Debug
    secrets:
      - ./config/{{ .Environment.Name }}-secrets.yaml
    values:
      - ./config/{{ .Environment.Name }}-values.yaml

この記述はenvironmentsが複数あり、少し奇妙に見えるかもしれませんが、yamlを---で分割しているため、それぞれ別々のyamlとして読み込まれます。
helmfileは複数のyamlを段階的に読み込むので、最初のenvironmentsを読み込んだ後、次のenvironmentsを読み込んでいます。
そのため、後続では、Environment.Nameなどを参照可能となり、environmentsの上書きをしています。
この動作は、helmfileを実行するときに、--debugをつけるとどのようにyamlが階層化して読み込まれているか理解できると思います。

この程度であれば、ベタ書きで環境ごとに定義しても良いかもしれませんが、helmfile のベストプラクティスガイドで指摘されているように、できるだけDRY(Don't Repeat Yourself)にすることが望ましいです。繰り返しの記述が増えると、書き損じなどによってミスが発生しやすくなります。

values, secretsの指定は、./configから始まっていますが、これは./releases/**/helmfile.yamlからの相対パスであることに注意してください。

  • helmfile.yaml
bases:
  - ./environments.yaml
  - ./templates.yaml
---
helmfiles:
  - ./releases/**/helmfile.yaml

このhelmfile.yamlはエントリーポイントとなるファイルです。
helmfileで-fオプションをつけずに実行すると、このファイルが読み込まれます。
helmfilesでglobパターンを指定しているので、すべてのhelmfile.yamlが読み込まれます。

  • templates.yaml
templates:
  default:
    # This prevents helmfile exiting when it encounters a missing file
    # Valid values are "Error", "Warn", "Info", "Debug". The default is "Error"
    # Use "Debug" to make missing files errors invisible at the default log level(--log-level=INFO)
    missingFileHandler: Debug
    valuesTemplate:
      - ./config/values.yaml.gotmpl

defaultテンプレートを定義します。

  • releases/

k8sにインストールするリリースを管理するディレクトリです。

  • releases//helmfile.yaml
{{ readFile "../../bases.yaml" }}
---
releases:
  - name: <your app>
    chart: ../../charts/<your app>
    namespace: <your namespace>
    version: 0.1.0
    inherit:
      - template: default

readFileを使って、bases.yamlを読み込んでいます。
また、defaultのテンプレートを継承することでテンプレートが実行されるようになります。

  • releases/<your app>/config

values, secretsを管理するディレクトリです。

  • {{環境名}}-secrets.yaml

    環境ごとの秘密情報を記述します。たとえば、DBのパスワードなどです。
    helm secrets encryptで暗号化して管理します。

  • {{環境名}}-values.yaml

    環境ごとのvaluesを記述します。

  • values.yaml.gotmpl

    valuesをテンプレートとして記述します。
    {{環境名}}-secrets.yaml, {{環境名}}-values.yamlを入力として、valuesを生成します。
    ここで生成されたvaluesとchartのvaluesがマージされて、最終的なvaluesが生成されます。

#helmfileの主な利用方法

#helmfileのインストール

asdfまたは、miseをつかってhelmfileをインストールします。
.tool-versionsに以下のように記述しておくと、プロジェクトごとにhelmfileのバージョンを固定できます。また、周辺のツールも同様にインストールしておくと良いでしょう。

  • .tool-versions
helm            3.14
helmfile        0.164.0
sops            3.8.1
age             1.1.1
kubectl         1.28
kubectx         0.9
k9s             0.27
jq              1.7.1
direnv          2.34.0

miseを使ってインストールします。

mise install

#helmfileの初期化

初期化するとhelm plugin(diff, secrets, etc...)がインストールされます。

helmfile init

#helmfileの実行

  • 差分チェック
helmfile diff
  • 同期
helmfile sync

syncを実行すると、helmfile.yamlに記述されているリソース全てが同期されます。後述のapplyとは異なり、差分の有無をチェックしません。
たいていの場合は、applyを使うことが多いです。
syncを利用するシーンとしては、CRDを使ったリソースを作成するときなど、applyだとCRDが存在しないとエラーになるため、syncを使います。
具体的には、kube-prometheus-stackでは初回にsyncを使います。

  • Apply

差分を検出して、差分のある部分だけを適用します。

helmfile apply
  • リリースの削除
helmfile destroy

個別のリリースを指定して実行することもできます。

helmfile -f releases/<your app>/helmfile.yaml destroy
  • 環境を指定する

環境を指定して実行することもできます。未指定だとdefaultが適用されます。

helmfile -e prod apply
  • build

buildは、helmfileのテンプレートで生成されるhelmに渡す前の情報が出力されます。
主にデバッグ用途で使います。意図したvaluesになっているかなどを確認するときに使います。
--debugオプションをつけると、各段階ごとのビルド情報が出力されるのでデバッグに役立ちます。

helmfile build

buildで出力される情報の例を以下に示します。

filepath: helmfile.yaml
helmBinary: helm
kustomizeBinary: kustomize
environments:
  prod:
    values:
      - ./config/prod-values.yaml
    secrets:
      - ./config/prod-secrets.yaml
    kubeContext: <kube context>
    missingFileHandler: Debug
bases:
  - ../../environments.yaml
  - ../../templates.yaml
releases:
  - chart: ../../charts/<your app>
    version: 0.1.0
    missingFileHandler: Debug
    name: <your app>
    namespace: <your namespace>
    values:
      - ./config/values.yaml.gotmpl
    valuesTemplate:
      - ./config/values.yaml.gotmpl
templates:
  default:
    missingFileHandler: Debug
    valuesTemplate:
      - ./config/values.yaml.gotmpl
renderedvalues:
  image:
    pullPolicy: IfNotPresent
    repository: nginx
    tag: latest
  • template

templateは、helmfileが生成する最終的なマニフェストを出力します。実際にどのようなマニフェストが全体的に生成されるか確認するときに使ったりします。

helmfile template

主な利用方法はこれくらいですが、他にもコマンドやオプションがあるので、helmfileの公式サイトを参照してください。

#秘密情報の管理について

この記事では扱いませんが、helm secretsを使って、秘密情報を暗号化して管理することができます。
helm secretsについては、別記事で紹介しようと思います。

#参考リンク

#まとめ

今回は、helmfileの構成と利用について紹介しました。

helmfileの構成についてベストプラクティスを参考に、より使いやすい構成を考えてみました。

また主な利用方法についても説明しました。

秘密情報の扱いは別の記事で紹介できればと思います。