1.この記事を書こうと思った背景

Helmwave なるものを Twitter で見かけた。

Helmwave の README.md にはこのように書かれている。

Helmwave is helm3-native tool for deploy your Helm Charts. HelmWave is like docker-compose for helm.

Helmwaveは、Helmチャートを展開するためのhelm3ネイティブツールです。 HelmWaveはdocker-composeのようなものですhelm用に作成します。

出所:helmwave/helmwave: 🌊 Helmwave is true release manager (邦訳はGoogle翻訳より)

よさそうなので、公式ドキュメントの Getting Started などを参考に Helmwave に入門する。

2.前提

2-1.ところで、Helm とは?

ぼくは、Helm 及び Helm Charts の理解も浅いのでこの場で深めておく。まず、Helm については公式ドキュメント にこのように書かれている。

The package manager for Kubernetes. Helm is the best way to find, share, and use software built for Kubernetes.

Kubernetesのパッケージマネージャー。 Helmは、Kubernetes用に構築されたソフトウェアを検索、共有、および使用するための最良の方法です。

パッケージマネージャー というと APT を想起するけれども、まさに Helm を APT や NPM などと対比して解説しているブログもある。(例:Helmの概要とChart(チャート)の作り方 - Qiita)

2-2.Helm Charts とは?

続いて、Helm Charts だが、これは Helm におけるパッケージ のことを指し、Helm Charts はひとつにかぎらず、複数の Kubernetes リソースをまとめることもあるという。

Helm uses a packaging format called charts. A chart is a collection of files that describe a related set of Kubernetes resources.

Helmは、チャートと呼ばれるパッケージ形式を使用しています。 グラフは、関連するKubernetesリソースのセットを説明するファイルのコレクションです。

出所:Helm | Charts

2-2.Helm Charts を宣言的に扱いたい

Kubernetes リソースを Helm Charts というパッケージ単位で操作できることはとても魅力的だが、いざ操作するとなると Imperative(命令的 ≒ コマンドを叩いていく) に扱わざるを得ないことに気がつく。たとえば、今回 Helmwave でデプロイする redis を helm コマンドで、my-namespace という namespace にデプロイする場合、このようになる。

$ kubectl create ns my-namespace
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install redis-a bitnami/redis -n my-namespace

参考:redis 16.9.3 · bitnami/bitnami

そういうわけで、Helm Charts を使う場合も Kubernetes リソースを Declarative(宣言的) に、言い換えるとするならば、できるだけリソース定義をコマンドベースではなく、yaml に寄せたいということで Helmfile や今回取り上げる Helmwave のようなデプロイツールが出てきたとみている。

※ Imperative(命令的) と Declarative(宣言的) の選択は二者択一というわけではない

両者をどちらかひとつだけ!というより、グラデーションがかっているように、どちらに比重を置くか?というかんじに考えるべきだと思っている。どちらがいい?わるい?もまたケースバイケースであり、ここでは、Helmwave を使って Helm Charts を Declarative(宣言的) にできるだけ扱ってみるぞ!というスタンスとしたい。

※ 手前味噌だが、Imperative(命令的) に Kubernetes リソースを操作する必要があったケースの一例

作成したKubernetesのNamespaceを削除しようとしてもTerminatingのまま削除できないのでkubectl replaceコマンドでImperative(命令)的に削除する | gkzz.dev

2-3.Helmfile と Helmwave の類似点と相違点

類似点

  • Helm Charts の宣言的なデプロイツール

相違点

  • Helmfile では必要とされていた kubectl や helm コマンドは Helmwave では「必須」ではない
    • kubectl logs のような kubectl コマンドからデプロイの様子を見たい場合は kubectl は必要
    • ただし、helmwave でも status コマンドや up コマンドの --kubedog というオプションでデプロイの様子を追うことは可能

それでは Helmwave をさわってみよう。


3.Helmwave をローカルでさわってみる

3-1.環境情報

$ grep VERSION= /etc/os-release 
VERSION="20.04.4 LTS (Focal Fossa)"
$ helmwave --version
helmwave version 0.19.3

3-2.Helmwave をインストールする

📥 Installation - Helmwave にしたがってやるだけ

$ export VERSION=0.19.3
$ wget -c https://github.com/helmwave/helmwave/releases/download/v$VERSION/helmwave_${VERSION}_linux_amd64.tar.gz -O - | tar -xz
$ sudo mv helmwave /usr/local/bin/
$ helmwave version
0.19.3

3-3.Helmwave をローカルで動かす

3-3-1.releases.yml と helmwave.yml.tpl の2つのファイルを用意

  • releases.yml
releases:
  - name: redis-a
  - name: redis-b
  • helmwave.yml.tpl
    • helmwave.yml.tpl は Templating(Go templates) を使うこともできる
version: 0.19.3

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami


.options: &options
  namespace: my-namespace
  wait: true # ハマリポイント
  timeout: 300s # ハマリポイント
  create_namespace: true

releases:
  - name: redis-a
    <<: *options
    chart:
      name: bitnami/redis
    namespace: my-namespace
  - name: redis-b
    <<: *options
    chart:
      name: bitnami/redis
    namespace: my-namespace
# Templating(Go templates)を使う場合
version: 0.19.3

repositories:
  - name: bitnami
    url: https://charts.bitnami.com/bitnami

.options: &options
  namespace: my-namespace
  wait: true
  timeout: 300s
  create_namespace: true

releases:
{{- with readFile "releases.yml" | fromYaml | get "releases" }}
{{ range $v := . }}
- name: {{ $v | get "name" }}
  tags: [{{ $v | get "name" }}]
  namespace: my-namespace
  chart:
     name: bitnami/redis
  <<: *options
{{ end }}
{{- end }}

3-3-2.helmwave.yml.tpl で helmwave.yml を生成する

$ helmwave yml
[🙃 aka INFO]: 📄 YML is ready!
	build plan with next command: helmwave build -f helmwave.yml

3-3-3.helmwave.yml を使って Helmwave が内部で使う planfile を生成する

  • 以下、3つの書き方はどれも同じことをやっている
$ helmwave build

–tpl オプションでテンプレートファイルを指定し、–file, -f オプションで出力するファイルを指定する。

$ helmwave build --tpl helmwave.yml.tpl --file helmwave.yml

–yml オプションは --tpl helmwave.yml.tpl --file helmwave.yml を指定してくれる。

$ helmwave build --yml
[🙃 aka INFO]: 📄 YML is ready!
	build plan with next command: helmwave build -f helmwave.yml
[🙃 aka INFO]: Building releases...
[🙃 aka INFO]: Building graphs...
[🙃 aka INFO]: Depends On:
┌────────────────┐
│ redis-a@my-... │
└────────────────┘
                  
                  
                  
┌────────────────┐
│ redis-b@my-... │
└────────────────┘

[🙃 aka INFO]: Building values...
[🙃 aka INFO]: no values provided
	release: redis-b@my-namespace
[🙃 aka INFO]: no values provided
	release: redis-a@my-namespace
[🙃 aka INFO]: Building repositories...
[🙃 aka INFO]: 🗄 repo has been added to the plan
	repository: bitnami
[🙃 aka INFO]: ❎ repository already exists with the same configuration, skipping
	repository: bitnami
[🙃 aka INFO]: Building registries...
[🙃 aka INFO]: Building manifests...
[🙃 aka INFO]: skipping updating dependencies for remote chart
	release: redis-b@my-namespace
[🙃 aka INFO]: skipping updating dependencies for remote chart
	release: redis-a@my-namespace
[🙃 aka INFO]: ✅ manifest done
	release: redis-a@my-namespace
[🙃 aka INFO]: ✅ manifest done
	release: redis-b@my-namespace
[🙃 aka INFO]: 🏗 Plan
	releases: 
	  - redis-a@my-namespace
	  - redis-b@my-namespace
	repositories: 
	  - bitnami
	registries: 
	  - 
[🙃 aka INFO]: 🆚 Diff manifests in the kubernetes cluster
[🙈 aka WARNING]: I cant get release from k8s: failed to get release redis-a@my-namespace: release: not found
[🙈 aka WARNING]: I cant get release from k8s: failed to get release redis-b@my-namespace: release: not found
[🙃 aka INFO]: 🏗 Planfile is ready!
	deploy it with next command: helmwave up --plandir .helmwave/

3-3-4.helmwave build コマンドを実行すると .helmwave ディレクトリ配下に planfile と manifest が生成されることが確認できる

$ tree .helmwave/
.helmwave/
├── manifest
│   ├── redis-a@my-namespace.yml
│   └── redis-b@my-namespace.yml
└── planfile

1 directory, 3 files

3-3-5.いよいよ、リリース(Helm Charts のインスタンス)をデプロイ

$ helmwave up --plandir .helmwave/
[🙃 aka INFO]: 🏗 Plan
	registries: 
	  - 
	releases: 
	  - redis-a@my-namespace
	  - redis-b@my-namespace
	repositories: 
	  - bitnami
[🙃 aka INFO]: 🗄 Sync repositories...
[🙃 aka INFO]: ❎ repository already exists with the same configuration, skipping
	repository: bitnami
[🙃 aka INFO]: 🗄 Sync registries...
[🙃 aka INFO]: 🛥 Sync releases...
[🙃 aka INFO]: 🛥 deploying... 
	release: redis-b@my-namespace
[🙃 aka INFO]: 🛥 deploying... 
	release: redis-a@my-namespace
[🙃 aka INFO]: ✅
	release: redis-a@my-namespace
[🙃 aka INFO]: ✅
	release: redis-b@my-namespace
[🙃 aka INFO]: Success 2 / 2

3-3-6.リリース(Helm Charts のインスタンス)がデプロイできた

$ helmwave list
[🙃 aka INFO]: Should be 2 releases
   NAME   |  NAMESPACE   | REVISION |            UPDATED             |  STATUS  | CHART | VERSION
----------+--------------+----------+--------------------------------+----------+-------+----------
  redis-a | my-namespace |        1 | 2022-05-13 20:23:15.966148     | deployed | redis | 16.9.3
          |              |          | +0900 JST                      |          |       |
  redis-b | my-namespace |        1 | 2022-05-13 20:23:15.966021     | deployed | redis | 16.9.3
          |              |          | +0900 JST                      |          |       |
$ helmwave ls
[🙃 aka INFO]: Should be 2 releases
   NAME   |  NAMESPACE   | REVISION |            UPDATED             |  STATUS  | CHART | VERSION
----------+--------------+----------+--------------------------------+----------+-------+----------
  redis-a | my-namespace |        1 | 2022-05-13 20:23:15.966148     | deployed | redis | 16.9.3
          |              |          | +0900 JST                      |          |       |
  redis-b | my-namespace |        1 | 2022-05-13 20:23:15.966021     | deployed | redis | 16.9.3
          |              |          | +0900 JST                      |          |       |
$ helmwave status
[🙃 aka INFO]: General status of redis-a@my-namespace
	status: deployed
	revision: 1
	name: redis-a
	namespace: my-namespace
	chart: redis-16.9.3
	last deployed: 2022-05-13 20:23:15.966148 +0900 JST
[🙃 aka INFO]: General status of redis-b@my-namespace
	name: redis-b
	namespace: my-namespace
	chart: redis-16.9.3
	last deployed: 2022-05-13 20:23:15.966021 +0900 JST
	status: deployed
	revision: 1

3-3-7.あとかたづけ

$ helmwave down
[🙃 aka INFO]: ✅ redis-a@my-namespace uninstalled!
[🙃 aka INFO]: ✅ redis-b@my-namespace uninstalled!
$ helmwave ls
[🙃 aka INFO]: Should be 2 releases
[💩 aka ERROR]: Failed to list. Skipping.
	release: redis-a@my-namespace
	error: release: not found
[💩 aka ERROR]: Failed to list. Skipping.
	release: redis-b@my-namespace
	error: release: not found
  NAME | NAMESPACE | REVISION | UPDATED | STATUS | CHART | VERSION
-------+-----------+----------+---------+--------+-------+----------
$ helmwave status
[💩 aka ERROR]: Failed to get status: failed to get status of release redis-a@my-namespace: release: not found
	release: redis-a@my-namespace
[💩 aka ERROR]: Failed to get status: failed to get status of release redis-b@my-namespace: release: not found
	release: redis-b@my-namespace

3-4.Helmwave をローカルで動かす上での個人的なハマりポイント

3-4-1.ローカルで helmwave build コマンドを実行したら、“failed to install “@”: context deadline exceeded” というエラーメッセージが出た

エラーログの抜粋は以下のとおり。

[🤬 aka FATAL]: one of goroutines in waitgroup sent error: 2 errors occurred:
	* failed to install "redis-b@my-namespace": context deadline exceeded
	* failed to install "redis-a@my-namespace": context deadline exceeded

kubectl get events コマンドでもエラーログを追うことができる

$ kubectl get ev -A --watch

# エラーログ抜粋

my-namespace   36m         Warning   Unhealthy          pod/redis-a-replicas-2         Readiness probe errored: rpc error: code = NotFound desc = failed to exec in container: failed to find container "略" in store: not found
my-namespace   36m         Warning   Unhealthy          pod/redis-a-replicas-2         Liveness probe errored: rpc error: code = NotFound desc = failed to exec in container: failed to find container "略" in store: not found
my-namespace   24m         Normal    Scheduled          pod/redis-a-replicas-2         Successfully assigned my-namespace/redis-a-replicas-2 to kind-control-plane

解決策は、helmwave.yml.tpl“wait” オプションと “timeout” オプション を書くということだった。どうも Helmwave 側で build コマンドの処理を待つ必要があるらしい。

3-4-2.helmwave build コマンドを実行したら “I cant get release from k8s: failed to get release <リリース名>: release: not found” という WARNING が出た

[🙈 aka WARNING]: I cant get release from k8s: failed to get release redis-a@my-namespace: release: not found
[🙈 aka WARNING]: I cant get release from k8s: failed to get release redis-b@my-namespace: release: not found

すでにリリースがある状態で build すると WARNING が表示されなかったので、リリースがない初回のみ表示される?


4.Helmwave を GitHub Actions 上でさわってみる

今度は Actions 上でさわってみた。使い方はローカルの場合とだいたい同じようなかんじ

name: Playground

on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Create k8s Kind Cluster # kind を使う必要がある
        uses: helm/kind-action@v1.2.0
      - uses: helmwave/setup-action@v0.2.0
        name: Install helmwave
        with:
          version: '0.19.3'

      - name: templating helmwave.yml.tpl
        run: helmwave yml

      - name: plan
        #run: helmwave build --yml
        run: helmwave build --yml --diff-mode=local
      - name: deploy
        run: helmwave up --plandir .helmwave/ --kubedog

4-1.Helmwave を GitHub Actions 上でさわってみた際のハマリポイント

  • ハマりポイントは、kind のような Actions 上で Kubernetes リソースにアクセス できるようにするという点
    • 以下のワークフローでは、helm/kind-action@v1.2.0 を使っている
    • helmwave っていらないんじゃないの!?と思った。ローカルでは必要なしだったのに。
    • Actions の環境とローカルとでは環境差分がある??
    • ローカルでも kind のような Kubernetes リソース を用意する必要がある

helm/kind-action@v1.2.0 を使わない場合のワークフローの実行結果のログ抜粋

[🤬 aka FATAL]: I can't check if release is installed
	release: redis-b@my-namespace
	error: Kubernetes cluster unreachable: Get "http://localhost:8080/version": dial tcp [::1]:8080: connect: connection refused

参考:helm/kind-action@v1.2.0 を使わない場合のワークフローの実行結果

※ Helmwave を使って Helm Charts をデプロイする前に helm/kind-action@v1.2.0 でクラスターを立てておくと問題なくデプロイできた。

参考:helm/kind-action@v1.2.0 を使う場合のワークフローの実行結果

5.Helmwave を触ってみた感想


6.更新履歴

2022/05/31

ローカルでは kind のような Helm Charts のデプロイ先としての Kubernetes クラスターを立てておく必要はないと思っていたが、それは間違いだった。むしろ、Helm Charts のデプロイ先として、kind のような Kubernetes クラスターを立てておく必要があった。

したがって、4-1.Helmwave を GitHub Actions 上でさわってみた際のハマリポイント にて、ローカルでも kind のような Kubernetes リソース を用意する必要がある であると更新した。

7.参考資料