コンテナ型仮想化技術 Study09 / Helm

# はじめに
今回はHelmです。

##関連記事
[コンテナ型仮想化技術 Study01 / Docker基礎](https://qiita.com/tomotagwork/items/34b4505b90b71dcfa8ca)
[コンテナ型仮想化技術 Study02 / Docker レジストリ](https://qiita.com/tomotagwork/items/bb1df5232dec5ea7adb7)
[コンテナ型仮想化技術 Study03 / Docker Compose](https://qiita.com/tomotagwork/items/af802c3e25f594cb4a8a)
[コンテナ型仮想化技術 Study04 / Minikube & kubectl簡易操作](https://qiita.com/tomotagwork/items/79527a132d343ab8d534)
[コンテナ型仮想化技術 Study05 / Pod操作](https://qiita.com/tomotagwork/items/7b16bde0529c052a9540)
[コンテナ型仮想化技術 Study06 / ReplicaSet, Deployment, Service](https://qiita.com/tomotagwork/items/2eb36f84c7f396f51e56)
[コンテナ型仮想化技術 Study06' / Kubernetesネットワーク問題判別](https://qiita.com/tomotagwork/items/f1145f3f5656a5cd51c3)
[コンテナ型仮想化技術 Study07 / ストレージ](https://qiita.com/tomotagwork/items/eacf8629bd4889f80672)
[コンテナ型仮想化技術 Study08 / Statefulset, Ingress](https://qiita.com/tomotagwork/items/3a5589031854c702fcec)
[コンテナ型仮想化技術 Study09 / Helm](https://qiita.com/tomotagwork/items/f46a26e3437c716881dc)

# 参考情報

[Helmの概要とChart(チャート)の作り方](https://qiita.com/thinksphere/items/5f3e918015cf4e63a0bc)
[Helm Documentation(V3)](https://helm.sh/docs/)
[Helm Documentation(V2)](https://v2.helm.sh/docs/)
[Helm 3.0 のお試しレポート](https://qiita.com/MahoTakara/items/e078fb1a5e6106b3db74)
[Get Started with Kubernetes using Minikube](https://docs.bitnami.com/kubernetes/get-started-kubernetes/)
[chartを作りながらhelmの概要を理解する](https://qiita.com/shoichiimamura/items/af881f5aa7802517020f)

[Helm Templateについて色々説明してみる](https://qiita.com/keiSunagawa/items/db0db26579d918c81457)
[[Helm入門] Templateで使える関数の実行例 ～ 文字列操作編 ～](https://www.skyarch.net/blog/?p=16660)

※Helmの全体像を理解するには、上の一番目の記事が秀逸で分かりやすい。
HelmはGo言語で実装されているらしく、Helm ChartのテンプレートもGo言語の作法に準拠している所があるようです。Go言語にはあまり馴染みがないせいか、出来合いのHelm Chart見ても、結構直感的に分かりにくい！ Helm Chartを読んで理解するためには、公式ドキュメントの以下の章を一通り目を通すべし。
 [The Chart Template Developer’s Guide](https://v2.helm.sh/docs/chart_template_guide/#the-chart-template-developer-s-guide)


# 用語の整理

- **Chart(Helm Chart)**: Kubernetesクラスター上に作成する一連のリソース定義のテンプレートをパッケージングしたもの
- **Repository**: Helm Chartを管理する場所/サービス (Dockerイメージを管理するDockerレジストリとは全く別物)
- **Release**: Kubernetesクラスター上にデプロイされたHelm Charのインスタンス

※Helmのリポジトリは必ずしも必要では無いようです。Helm Chartは最終的にはテンプレート等のファイル群を.tgzとしてまとめたアーカイブファイルとして作成されることになりますが、そのファイルを直接指定してkubernetesクラスターに適用させることも可能ですし、アーカイブ前のディレクトリを指定することも可能です。限定的な利用とかテスト中はこれで充分な場合もありそうです。


# Helmセットアップ
参考: [Installing Helm](https://v2.helm.sh/docs/install/#installing-helm)

直近でHelmV3がリリースされたようですが、結構変更が入っていそうなのと情報が少ないので、一旦Helm V2.16.1を使うことにします。
Kubernetesクラスターとしては、Windows VirtualBox上のUbuntu上にあるminikubeを使います。
kubectlが実行できる環境にHelm本体(クライアント)をインストールし、Kubernetesクラスター側にはhelmからの指示を受けて各種操作を行うサーバー側のモジュール(Tiller)をセットアップすることになります。(ちなみにHelm V3ではTillerのセットアップは不要らしい)
ここではminikubeが乗っているUbuntu上にインストールします。


## Helm本体のインストール

以下からV2.16.1のLinux amd64版のバイナリを入手します。
https://github.com/helm/helm/releases

```
vagrant@minikube:~/helm$ wget https://get.helm.sh/helm-v2.16.1-linux-amd64.tar.gz
--2019-11-30 23:45:56--  https://get.helm.sh/helm-v2.16.1-linux-amd64.tar.gz
Resolving get.helm.sh (get.helm.sh)... 152.199.39.108, 2606:2800:247:1cb7:261b:1f9c:2074:3c
Connecting to get.helm.sh (get.helm.sh)|152.199.39.108|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 25262108 (24M) [application/x-tar]
Saving to: ‘helm-v2.16.1-linux-amd64.tar.gz’

helm-v2.16.1-linux-amd64.tar.gz        100%[============================================================================>]  24.09M  4.65MB/s    in 5.3s

2019-11-30 23:46:01 (4.55 MB/s) - ‘helm-v2.16.1-linux-amd64.tar.gz’ saved [25262108/25262108]
```

解凍してhelmのモジュールをPATHが通っているディレクトリ(/usr/local/bin/)に配置

```
vagrant@minikube:~/helm$ tar -zxvf helm-v2.16.1-linux-amd64.tar.gz
linux-amd64/
linux-amd64/helm
linux-amd64/LICENSE
linux-amd64/tiller
linux-amd64/README.md

vagrant@minikube:~/helm$ ls -l linux-amd64/
total 79692
-rwxr-xr-x 1 vagrant vagrant 40460288 Nov 12 18:35 helm
-rw-r--r-- 1 vagrant vagrant    11343 Nov 12 18:37 LICENSE
-rw-r--r-- 1 vagrant vagrant     3444 Nov 12 18:37 README.md
-rwxr-xr-x 1 vagrant vagrant 41127936 Nov 12 18:37 tiller

vagrant@minikube:~/helm$ sudo mv linux-amd64/helm /usr/local/bin/
```

これでhelmコマンドが使えるようになりました。

```
vagrant@minikube:~$ helm version
Client: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}
Error: could not find tiller
```

## Tillerセットアップ
※Helm V3だとこのステップが不要になっている！
次に、Kubernetesクラスター側にTillerというサーバーサイドのモジュールをセットアップしますが、これはHelmをインストールしたノード(=kubectlが入っているノード)から```helm init```コマンドで実施します。

```
vagrant@minikube:~$ helm init
Creating /home/vagrant/.helm
Creating /home/vagrant/.helm/repository
Creating /home/vagrant/.helm/repository/cache
Creating /home/vagrant/.helm/repository/local
Creating /home/vagrant/.helm/plugins
Creating /home/vagrant/.helm/starters
Creating /home/vagrant/.helm/cache/archive
Creating /home/vagrant/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /home/vagrant/.helm.

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation
```

```helm init```では、デフォルトで、その環境のkubectlのデフォルトの接続先kubenetesに対してkube-systemネームスペースに必要なリソースを定義してくれるようです。

## 確認

むむ?? ```helm version```コマンドがエラーになる。

```
vagrant@minikube:~$ helm version
Client: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}
E1201 00:06:00.424955    7038 portforward.go:400] an error occurred forwarding 32784 -> 44134: error forwarding port 44134 to pod 335e7345d4361233ebb592cff534be3c9adb2317e436260cbe1917f76a2478a9, uid : unable to do port forwarding: socat not found
E1201 00:06:01.428787    7038 portforward.go:400] an error occurred forwarding 32784 -> 44134: error forwarding port 44134 to pod 335e7345d4361233ebb592cff534be3c9adb2317e436260cbe1917f76a2478a9, uid : unable to do port forwarding: socat not found
E1201 00:06:03.291790    7038 portforward.go:400] an error occurred forwarding 32784 -> 44134: error forwarding port 44134 to pod 335e7345d4361233ebb592cff534be3c9adb2317e436260cbe1917f76a2478a9, uid : unable to do port forwarding: socat not found
E1201 00:06:36.327123    7038 portforward.go:340] error creating error stream for port 32784 -> 44134: Timeout occured
E1201 00:07:00.058581    7038 portforward.go:362] error creating forwarding stream for port 32784 -> 44134: Timeout occured
E1201 00:07:27.722853    7038 portforward.go:362] error creating forwarding stream for port 32784 -> 44134: Timeout occured
E1201 00:07:58.293685    7038 portforward.go:340] error creating error stream for port 32784 -> 44134: Timeout occured
E1201 00:08:37.563451    7038 portforward.go:362] error creating forwarding stream for port 32784 -> 44134: Timeout occured
E1201 00:09:27.977955    7038 portforward.go:362] error creating forwarding stream for port 32784 -> 44134: Timeout occured
E1201 00:10:40.394108    7038 portforward.go:340] error creating error stream for port 32784 -> 44134: Timeout occured
Error: cannot connect to Tiller
```

ちょっと古いが以下のようなIssueがあったので、これを参考に```sudo apt install socat```で socatパッケージをインストールしたら上のエラーは解消しました。
参考: https://github.com/helm/helm/issues/1371

```
vagrant@minikube:~$ helm version
Client: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.16.1", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}
```

# Helm操作例

## Helmチャートを使ったインストール例 / redis
参考: [Get Started with Kubernetes using Minikube - Step 5: Install an application using a Helm Chart](https://docs.bitnami.com/kubernetes/get-started-kubernetes/#step-5-install-an-application-using-a-helm-chart)

### リポジトリ確認

helm chartを管理するリポジトリ情報を事前に設定しておく必要があります。デフォルトでは以下のリポジトリが設定されています。今回は、この公式のリポジトリ使うので、このままでOK。

```
vagrant@minikube:~$ helm repo list
NAME    URL
stable  https://kubernetes-charts.storage.googleapis.com
local   http://127.0.0.1:8879/charts
```

### インストール
使用するHelm Chartはこちら。
[Kubeapps Hub - Redis](https://hub.kubeapps.com/charts/stable/redis)

```
vagrant@minikube:~$ helm install stable/redis
NAME:   truculent-buffalo
LAST DEPLOYED: Sun Dec  1 01:21:06 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                            AGE
truculent-buffalo-redis         1s
truculent-buffalo-redis-health  1s

==> v1/Pod(related)
NAME                              AGE
truculent-buffalo-redis-master-0  0s
truculent-buffalo-redis-slave-0   0s

==> v1/Secret
NAME                     AGE
truculent-buffalo-redis  1s

==> v1/Service
NAME                              AGE
truculent-buffalo-redis-headless  1s
truculent-buffalo-redis-master    0s
truculent-buffalo-redis-slave     0s

==> v1/StatefulSet
NAME                            AGE
truculent-buffalo-redis-master  0s
truculent-buffalo-redis-slave   0s


NOTES:
** Please be patient while the chart is being deployed **
Redis can be accessed via port 6379 on the following DNS names from within your cluster:

truculent-buffalo-redis-master.default.svc.cluster.local for read/write operations
truculent-buffalo-redis-slave.default.svc.cluster.local for read-only operations


To get your password run:

    export REDIS_PASSWORD=$(kubectl get secret --namespace default truculent-buffalo-redis -o jsonpath="{.data.redis-password}" | base64 --decode)

To connect to your Redis server:

1. Run a Redis pod that you can use as a client:

   kubectl run --namespace default truculent-buffalo-redis-client --rm --tty -i --restart='Never' \
    --env REDIS_PASSWORD=$REDIS_PASSWORD \
   --image docker.io/bitnami/redis:5.0.7-debian-9-r0 -- bash

2. Connect using the Redis CLI:
   redis-cli -h truculent-buffalo-redis-master -a $REDIS_PASSWORD
   redis-cli -h truculent-buffalo-redis-slave -a $REDIS_PASSWORD

To connect to your database from outside the cluster execute the following commands:

    kubectl port-forward --namespace default svc/truculent-buffalo-redis-master 6379:6379 &
    redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD
```

redis関連のpod, service, statefulsetが作成されました！

### 確認

kubectlで作成されたリソースを確認

```
vagrant@minikube:~$ kubectl get all -o wide | grep redis
pod/truculent-buffalo-redis-master-0   1/1     Running   0          20m     172.17.0.11   minikube   <none>           <none>
pod/truculent-buffalo-redis-slave-0    1/1     Running   0          20m     172.17.0.10   minikube   <none>           <none>
pod/truculent-buffalo-redis-slave-1    1/1     Running   0          19m     172.17.0.12   minikube   <none>           <none>

service/truculent-buffalo-redis-headless   ClusterIP   None           <none>        6379/TCP   20m   app=redis,release=truculent-buffalo
service/truculent-buffalo-redis-master     ClusterIP   10.97.52.153   <none>        6379/TCP   20m   app=redis,release=truculent-buffalo,role=master
service/truculent-buffalo-redis-slave      ClusterIP   10.102.26.9    <none>        6379/TCP   20m   app=redis,release=truculent-buffalo,role=slave

statefulset.apps/truculent-buffalo-redis-master   1/1     20m   truculent-buffalo-redis   docker.io/bitnami/redis:5.0.7-debian-9-r0
statefulset.apps/truculent-buffalo-redis-slave    2/2     20m   truculent-buffalo-redis   docker.io/bitnami/redis:5.0.7-debian-9-r0
```

helmコマンドでの状態確認

```
vagrant@minikube:~$ helm ls
NAME                    REVISION        UPDATED                         STATUS          CHART           APP VERSION     NAMESPACE
truculent-buffalo       1               Sun Dec  1 01:21:06 2019        DEPLOYED        redis-10.1.0    5.0.7           default
```

### 削除

```
vagrant@minikube:~$ helm delete truculent-buffalo
release "truculent-buffalo" deleted
```

リソース一式削除されます。

```
vagrant@minikube:~$ helm ls

vagrant@minikube:~$ kubectl get all -o wide | grep redis

```

## Helm Chartを作成してみる
参考: [The Chart Template Developer’s Guide](https://v2.helm.sh/docs/chart_template_guide/#the-chart-template-developer-s-guide)


### 雛形作成

```helm create```コマンドで、helmチャートとして必要なディレクトリ構造の雛形が出来上がります。

```
vagrant@minikube:~/helmchart$ helm create mychar01
Creating mychar01

vagrant@minikube:~/helmchart$ tree .
.
`-- mychar01
    |-- Chart.yaml
    |-- charts
    |-- templates
    |   |-- NOTES.txt
    |   |-- _helpers.tpl
    |   |-- deployment.yaml
    |   |-- ingress.yaml
    |   |-- service.yaml
    |   |-- serviceaccount.yaml
    |   `-- tests
    |       `-- test-connection.yaml
    `-- values.yaml

4 directories, 9 files
```

### Simple Chart / Deployment

templatesディレクトリ下に、雛形となるサンプルファイルが各種できあがっていますが、これらは使わないので一旦削除します。

```
vagrant@minikube:~/helmchart/mychar01$ rm -rf templates/*
```

templatesディレクトリ下に、kubernetesクラスタに定義したいマニフェストファイルを配置します。※本当は"テンプレート"なのでマニフェストの中身に変数とか埋め込めますが、まずは単純なケースを試すため、出来合いのマニフェストを配置して試します。

Deploymentのテストで使用した、以下のマニフェストファイルを、上のtemplatesディレクトリ下にそのまま配置します。
https://github.com/takara9/codes_for_lessons/blob/master/step08/deployment1.yml

```
vagrant@minikube:~/helmchart/mychar01$ tree .
.
|-- Chart.yaml
|-- charts
|-- templates
|   `-- deployment1.yml
`-- values.yaml

2 directories, 3 files
```

```helm lint```コマンドで、Chartの文法チェックを行うことができるようです。

```
vagrant@minikube:~/helmchart$ helm lint mychar01/
==> Linting mychar01/
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, no failures
```

OKっぽいので、このChartを適用してみます。リポジトリに登録する場合は、```helm package```でパッケージングすることになると思いますが、このままでも適用はできるのでそのままやってみます。

```helm install```でインストールしますが、この時、```--debug --dry-run```で、デプロイ前に確認できます。まぁ今回は変数とか使ってないのであまり関係無いですが。

```
vagrant@minikube:~/helmchart$ helm install --debug --dry-run --namespace test ./mychar01
[debug] Created tunnel using local port: '44871'

[debug] SERVER: "127.0.0.1:44871"

[debug] Original chart version: ""
[debug] CHART PATH: /home/vagrant/helmchart/mychar01

NAME:   foppish-uakari
REVISION: 1
RELEASED: Tue Dec  3 05:19:10 2019
CHART: mychar01-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
fullnameOverride: ""
image:
  pullPolicy: IfNotPresent
  repository: nginx
  tag: stable
imagePullSecrets: []
ingress:
  annotations: {}
  enabled: false
  hosts:
  - host: chart-example.local
    paths: []
  tls: []
nameOverride: ""
nodeSelector: {}
podSecurityContext: {}
replicaCount: 1
resources: {}
securityContext: {}
service:
  port: 80
  type: ClusterIP
serviceAccount:
  create: true
  name: null
tolerations: []

HOOKS:
MANIFEST:

---
# Source: mychar01/templates/deployment1.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.16
```

では適用してみます。

```
vagrant@minikube:~/helmchart$ helm install --namespace test ./mychar01
NAME:   queenly-pig
LAST DEPLOYED: Tue Dec  3 05:21:49 2019
NAMESPACE: test
STATUS: DEPLOYED

RESOURCES:
==> v1/Deployment
NAME        AGE
web-deploy  0s

==> v1/Pod(related)
NAME                         AGE
web-deploy-866f97c649-55vnj  0s
web-deploy-866f97c649-7tv9n  0s
web-deploy-866f97c649-kmjhz  0s
```

ステータスを確認してみます。

```
vagrant@minikube:~/helmchart$ helm list
NAME            REVISION        UPDATED                         STATUS          CHART           APP VERSION     NAMESPACE
queenly-pig     1               Tue Dec  3 05:21:49 2019        DEPLOYED        mychar01-0.1.0  1.0             test


vagrant@minikube:~/helmchart$ kubectl -n test get all -o wide
NAME                              READY   STATUS    RESTARTS   AGE   IP            NODE       NOMINATED NODE   READINESS GATES
pod/web-deploy-866f97c649-55vnj   1/1     Running   0          62s   172.17.0.10   minikube   <none>           <none>
pod/web-deploy-866f97c649-7tv9n   1/1     Running   0          62s   172.17.0.12   minikube   <none>           <none>
pod/web-deploy-866f97c649-kmjhz   1/1     Running   0          62s   172.17.0.11   minikube   <none>           <none>

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES       SELECTOR
deployment.apps/web-deploy   3/3     3            3           62s   nginx        nginx:1.16   app=web

NAME                                    DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES       SELECTOR
replicaset.apps/web-deploy-866f97c649   3         3         3       62s   nginx        nginx:1.16   app=web,pod-template-hash=866f97c649
```

namespace "test"に、Deployment関連リソース(deployment, replicaset, pod)が意図した通りに作成されました！

削除するには、作成されたhelmリリース名を指定してhelm deleteコマンドを実行します。

```
vagrant@minikube:~/helmchart$ helm delete queenly-pig
release "queenly-pig" deleted
```

これで、作成されたリソース一式が削除されます。










#memo

https://v2.helm.sh/docs/chart_template_guide/#accessing-files-inside-templates

>It is okay to add extra files to your Helm chart. These files will be bundled and sent to Tiller. Be careful, though. Charts must be smaller than 1M because of the storage limitations of Kubernetes objects.