ずぼらなHelmチャートの使い方メモ

K8sを使って時間が経ってくると、マニフェストが氾濫して、整理ができなくなってくる。幾つかのマニフェストを組み合わせたい場合や、少しづつ変更して再利用したい場合など、いろいろと不便を感じるようになる。そこで「Helmを勉強しないで、Helmのメリットを享受したい」と作ったメモである。


## とりあえずマニフェストを再利用したければ、ココに置け

テンプレートのチャートを作って、templateのフォルダへ必要なマニフェストで置き換える。そのあとは、helm install でデプロイ完了となる。


テンプレート・チャートを作成

~~~
$ helm create my-apl
Creating my-apl
~~~

テンプレートのチャートを削除して、利用したいマニフェストで置き換える

~~~
$ rm -fr my-apl/templates/*
$ cp my-manifest/* my-apl/template
~~~

Helmを使ってインストール -n は、リリース名を明示的に付与するもので、省略すると適当にリリース名が付与される。--namespace <名前空間名> を付与した場合、既存になければ新しい名前空間を作成して、その下にアプリケーションをデプロイしてくれる。

~~~
$ helm install -n test1 ./my-apl
~~~

実行中のHelmチャートを確認は、ls または list 

~~~
$ helm ls
NAME  	REVISION	UPDATED                 	STATUS  	CHART              	APP VERSION	NAMESPACE
test1 	1       	Wed Aug 14 22:16:35 2019	DEPLOYED	my-apl-0.1.0       	1.0        	default  
~~~

実行中のHelmチャートを削除する場合、--purgeを忘れないようにする。

~~~
$ helm delete mini1 --purge
release "mini1" deleted
~~~


## 複数のリリースに対応したければ {{ .Release.Name }} を利用

Helmはリリースによって、同じ名前空間に複数のオブジェクトをデプロイできる。この機能を活かすために、
templatesにおくマニフェストに、幾つかの変更を加える。


この機能を有効に使うには、重複が許されないオブジェクト名やラベルに、{{ .Release.Name }}を追加する。

サービスをデプロイするマニフェストでは、metadata.name の値、selector.appの値に追加して、同じ名前空間内で重複がないようにする。

~~~file:service.yml 
apiVersion: v1
kind: Service
metadata:
  name: web-{{ .Release.Name }}
spec:
  selector:
    app: web-{{ .Release.Name }}
  ports:
  - protocol: TCP
    port: 80
~~~

ポッドをデプロイするためのデプロイメント・コントローラーのマニフェストも同じで、サービスやデプロイメント・コントローラーとポッドを繋ぐための
レベルとデプロイメントのオブジェクト名に「-{{ .Release.Name }}」を追加する。

~~~file:deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-{{ .Release.Name }}
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-{{ .Release.Name }}
  template:
    metadata:
      labels:
        app: web-{{ .Release.Name }}
    spec:
      containers:
      - name: nginx
        image: nginx:latest
~~~

上記の変更を加えたチャートを使って、
リリース名として、test2, test3 を付与してインストールする。

この変更を加えなければ、オブジェクト名重複するために、デプロイ時にエラーが発生する。また、マッチ・ラベルに{{ .Release.Name }}を加えなければ、
サービスが、リクエストを転送する先のポッドが、test1〜test3で混ざってしまい、想定外の動作をする。


~~~terminal:
$ helm install -n test2 ./my-apl
$ helm install -n test3 ./my-apl

$ helm ls
NAME  	REVISION	UPDATED                 	STATUS  	CHART              	APP VERSION	NAMESPACE
test1 	1       	Wed Aug 14 22:16:35 2019	DEPLOYED	my-apl-0.1.0       	1.0        	default  
test2 	1       	Wed Aug 14 22:27:17 2019	DEPLOYED	my-apl-0.1.0       	1.0        	default  
test3 	1       	Wed Aug 14 22:27:23 2019	DEPLOYED	my-apl-0.1.0       	1.0        	default  
~~~

これで、セレクターとラベルの対応が、混ざっていないことを確認できる。

~~~terminal:
$ kubectl get svc -o wide
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE    SELECTOR
web           ClusterIP   172.21.53.158    <none>        80/TCP    23m    app=web
web-test2     ClusterIP   172.21.148.177   <none>        80/TCP    12m    app=web-test2
web-test3     ClusterIP   172.21.61.96     <none>        80/TCP    12m    app=web-test3

$ kubectl get po --show-labels
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
web-d97869bd8-d67vt          1/1     Running   0          12m   app=web,pod-template-hash=d97869bd8
web-d97869bd8-kw8v9          1/1     Running   0          12m   app=web,pod-template-hash=d97869bd8
web-d97869bd8-tbqpr          1/1     Running   0          12m   app=web,pod-template-hash=d97869bd8
web-test2-78d55dc8b4-2jscx   1/1     Running   0          97s   app=web-test2,pod-template-hash=78d55dc8b4
web-test2-78d55dc8b4-7qxg6   1/1     Running   0          97s   app=web-test2,pod-template-hash=78d55dc8b4
web-test2-78d55dc8b4-m5qwt   1/1     Running   0          97s   app=web-test2,pod-template-hash=78d55dc8b4
web-test3-79d548cc9f-pvgzc   1/1     Running   0          92s   app=web-test3,pod-template-hash=79d548cc9f
web-test3-79d548cc9f-zk7rq   1/1     Running   0          92s   app=web-test3,pod-template-hash=79d548cc9f
web-test3-79d548cc9f-zpzdr   1/1     Running   0          92s   app=web-test3,pod-template-hash=79d548cc9f
~~~

## デプロイの前提となるマニフェストは、ここへ置け

~~~terminal:
$ tree my-apl/
my-apl/
├── Chart.yaml
├── charts       <-- ここに置けば、前提のHelmチャートとして、実行される。
├── templates
│   ├── deployment.yml
│   └── service.yml
└── values.yaml
~~~

Redis Cluster でノードのストレージを localとして利用する際に、ストレージクラス local のセットアップと永続ボリュームのセットアップに利用している例である。

~~~terminal:
$ tree redis-cluster/
redis-cluster/
├── Chart.yaml
├── README.md
├── charts             <-- このチャートのテンプレートが最初に実行
│   └── setup-local-pv
│       ├── Chart.yaml
│       ├── charts     <-- さらにその前のチャートは、ここに置く
│       ├── templates
│       │   ├── create-pv-zone-A.yml
│       │   ├── create-pv-zone-B.yml
│       │   ├── create-pv-zone-C.yml
│       │   ├── sc.yml
│       │   ├── setup-local-zone-A.yml
│       │   ├── setup-local-zone-B.yml
│       │   └── setup-local-zone-C.yml
│       └── values.yaml
├── templates
│   ├── redis-cluster-on-zone-A.yml
│   ├── redis-cluster-on-zone-B.yml
│   ├── redis-cluster-on-zone-C.yml
│   ├── redis-configmap.yml
│   └── redis-service.yml
└── values.yaml
~~~

## マニフェストに変数を埋め込んで再利用性を高めよ

テンプレートのマニフェストに、変数を置いて、デプロイ時に置き換えるには、values.yamlに変数の値を書いて、マニフェストから参照する。

次の例は、グローバル変数を参照する例です。グローバルに、つまり、前提とするチャートからも参照したい時は、globalの下に変数名と値を記述する。ローカルのテンプレートで良い場合は、最初のレベルに記述する。

~~~file:values.yaml
global:
  region: jp-tok
  zoneA: tok02
  zoneB: tok04
  zoneC: tok05
~~~

変数の値で置き換えたいマニフェストからは、{{ .Vaules.global.zoneA}}を置くことで、最終的にデプロイされる時には、変数の値の文字列で置き換られる。

~~~file:redis-cluster-on-zone-A.yml
    spec:
      # ゾーンを指定
      nodeSelector:
        failure-domain.beta.kubernetes.io/zone: {{ .Values.global.zoneA | quote }}
~~~

上記の例では、パイプ記号で繋いで、quoteが書かれている。こうすることで、置き換えられた文字鉄の前後を"で囲むことができる。

## 利用方法などを伝えるには、NOTES.txtを活用せよ

チャートのディレクトリ templatesの下に、NOTES.txtを置いて、伝達事項を記述することができる。このファイルも、ユーザー変数やビルドイン変数で置き換えられるので、リリースに応じて変化する項目などを親切に表示できる。それに、日本語も記述できた。

## 最後に

ここに挙げた以上のことを使いたくなったら、参考資料を見ましょう。

## 参考資料

* https://github.com/helm/helm
* https://helm.sh/docs/
* Helmの使い方を3分で理解する、https://qiita.com/MahoTakara/items/3c509235cc18bd407f07
* カスタムなHELMチャートを開発してローカル・リポジトリでテストする方法のメモ、https://qiita.com/MahoTakara/items/8d8e8b45ef91341c3ee2