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
│   ├── [email protected]
│   └── [email protected]
└── 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/[email protected]
      - uses: helmwave/[email protected]
        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/[email protected] を使っている
    • helmwave っていらないんじゃないの!?と思った。ローカルでは必要なしだったのに。
    • Actions の環境とローカルとでは環境差分がある??
    • ローカルでも kind のような Kubernetes リソース を用意する必要がある

helm/[email protected] を使わない場合のワークフローの実行結果のログ抜粋

[🤬 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/[email protected] を使わない場合のワークフローの実行結果

※ Helmwave を使って Helm Charts をデプロイする前に helm/[email protected] でクラスターを立てておくと問題なくデプロイできた。

参考:helm/[email protected] を使う場合のワークフローの実行結果

5.Helmwave を触ってみた感想


6.更新履歴

2022/05/31

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

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

7.参考資料