独自のHelm Chartの作成とGitHub Pagesでの公開


[ICPにMySQLに接続するLibertyアプリケーションをデプロイ](https://qiita.com/sotoiwa/items/8ff88d1742e2b6443835)

で作成した簡単なLiberty-MySQLのアプリケーションをHelmチャートにして公開する方法を確認。

## Helmの初期化

Helmを初期化する。

```
helm init
```

サーバー側のバージョンが取得出来ていればサーバー側（tiller）も稼働している。

```shell-session
$ helm version --tls
Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.9.1+icp", GitCommit:"843201eceab24e7102ebb87cb00d82bc973d84a7", GitTreeState:"clean"}
$
```

## マニフェストの確認

- [ICPにMySQLに接続するLibertyアプリケーションをデプロイ](https://qiita.com/sotoiwa/items/8ff88d1742e2b6443835)
- [Kubernetes JobでMySQLへデータをロードする](https://qiita.com/sotoiwa/items/6c80606366020b35bb8c)

上記記事で使用していたマニフェストは以下。

|ファイル名|説明|
|---|---|
|mysql-pv.yaml|MySQLコンテナが使用するPersistentVolume|
|mysql-pvc.yaml|MySQLコンテナが使用するPersistentVolumeClaim|
|mysql-secret.yaml|MySQLのrootパスワードを格納したSecret|
|mysql-deployment.yaml|MySQLのDeployment|
|mysql-service.yaml|MySQLのService|
|mysql-init-job.yaml|MySQLを初期化するJob|
|liberty-config.yaml|LibertyからMySQLへの接続情報を格納したConfigMap|
|liberty-secret.yaml|LibertyからMySQLへの接続のパスワードを格納したSecret|
|liberty-deployment.yaml|LibertyのDeployment|
|liberty-service.yaml|LibertyのService|
|liberty-ingress.yaml|LibertyにルーティングするIngress|

## 雑なチャートの作成

`helm create hellomysql`でチャートの雛形を作成することができるが、雛形からカスタマイズするのは難易度が高いように思う。まずは雑にチャートを作成する。

### チャートの構造

[Helmの公式サイト](https://docs.helm.sh/developing_charts/)によると、チャートの構造は以下の通り。

```
example/
├── Chart.yaml          # チャートの情報を含むyaml
├── LICENSE             # OPTIONAL: チャートのライセンス情報を含むprain text
├── README.md           # OPTIONAL: READMEファイル
├── charts              # このチャートが依存するチャートを格納するディレクトリー
├── requirements.yaml   # OPTIONAL: 依存するチャートをリストしたyaml
├── templates           # マニフェストのテンプレートを格納するディレクトリー
│   ├── NOTES.txt       # OPTIONAL: チャートの使用方法を記載したplain text
│   ├── _hogehoge.tpl   # OPTIONAL: アンダースコア始まりはマニフェストを含まないテンプレート
│   └── hogehoge.yaml   # マニフェストを含むテンプレート
└── values.yaml         # このチャートのデフォルト値を記載したyaml
```

上記でOPTIONALになっていないが、`values.yaml`や`charts`ディレクトリーもなくても大丈夫だったので、最低限必要なものは以下。

- Chart.yaml
- templates/*.yaml

### チャートの作成

全くパラメータなしでマニフェストをまとめただけのチャートを作成する。

はじめに`Chart.yaml`を作成する。このファイルにチャートの名前やバージョンなどを記載する。

```
vi hellomysql/Chart.yaml
```

```yaml:Chart.yaml
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: hellomysql
version: 0.1.0
```

templatesディレクトリーを作成し、このディレクトリーにマニフェストを全部入れる。

```
mkdir -p hellomysql/templates
```

ディレクトリー構造は以下のようになる。

```
$ tree hellomysql
hellomysql
├── Chart.yaml
└── templates
    ├── liberty-config.yaml
    ├── liberty-deployment.yaml
    ├── liberty-ingress.yaml
    ├── liberty-secret.yaml
    ├── liberty-service.yaml
    ├── mysql-deployment.yaml
    ├── mysql-init-job.yaml
    ├── mysql-pv.yaml
    ├── mysql-pvc.yaml
    ├── mysql-secret.yaml
    └── mysql-service.yaml

1 directory, 12 files
$
```

これで一応パッケージングできる。パッケージング前に`helm lint`コマンドで静的なチェックができる。

```shell-session
$ helm lint hellomysql
==> Linting hellomysql
[INFO] Chart.yaml: icon is recommended
[INFO] values.yaml: file does not exist

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

パッケージングを行う。

```shell-session
$ helm package hellomysql
Successfully packaged chart and saved it to: /home/ubuntu/hellomysql-chart/hellomysql-0.1.0.tgz
$
```

### デプロイ確認

PVやIngressが環境に依存するためデプロイできる環境は限られ、また変更できるパラメータもないが、このチャートでもデプロイできるのでデプロイしてみる。

既にアプリケーションがデプロイ済みの場合は削除する。

```
kubectl delete -f liberty-ingress.yaml -n sugi
kubectl delete -f liberty-service.yaml -n sugi
kubectl delete -f liberty-deployment.yaml -n sugi
kubectl delete -f liberty-config.yaml -n sugi
kubectl delete -f liberty-secret.yaml -n sugi
kubectl delete -f mysql-init-job.yaml -n sugi
kubectl delete -f mysql-service.yaml -n sugi
kubectl delete -f mysql-deployment.yaml -n sugi
kubectl delete -f mysql-secret.yaml -n sugi
kubectl delete -f mysql-pvc.yaml -n sugi
kubectl delete -f mysql-pv.yaml -n sugi
```

PVのディレクトリーも掃除する。

```
sudo rm -rf /data/mysql/*
```

デプロイしてみる。

```shell-session
$ helm install --name test --namespace sugi hellomysql-0.1.0.tgz --tls
NAME:   test
E1024 12:44:34.134084   19753 portforward.go:303] error copying from remote stream to local connection: readfrom tcp4 127.0.0.1:32823->127.0.0.1:53268: write tcp4 127.0.0.1:32823->127.0.0.1:53268: write: broken pipe
LAST DEPLOYED: Wed Oct 24 12:44:33 2018
NAMESPACE: sugi
STATUS: DEPLOYED

RESOURCES:
==> v1/Service
NAME     TYPE       CLUSTER-IP  EXTERNAL-IP  PORT(S)   AGE
liberty  ClusterIP  10.0.0.153  <none>       9080/TCP  1s
mysql    ClusterIP  10.0.0.167  <none>       3306/TCP  1s

==> v1/Deployment
NAME     DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
liberty  1        1        1           0          1s
mysql    1        1        1           0          1s

==> v1/Pod(related)
NAME                      READY  STATUS             RESTARTS  AGE
liberty-74fd798747-7gqsp  0/1    ContainerCreating  0         1s
mysql-7c47657796-8gj5x    0/1    Pending            0         1s
mysql-init-9cwl8          0/1    Pending            0         0s

==> v1/ConfigMap
NAME            DATA  AGE
liberty-config  3     1s

==> v1/PersistentVolume
NAME      CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS  CLAIM           STORAGECLASS  REASON  AGE
mysql-pv  10Gi      RWO           Retain          Bound   sugi/mysql-pvc  mysql         1s

==> v1/PersistentVolumeClaim
NAME       STATUS  VOLUME    CAPACITY  ACCESS MODES  STORAGECLASS  AGE
mysql-pvc  Bound   mysql-pv  10Gi      RWO           mysql         1s

==> v1/Secret
NAME            TYPE    DATA  AGE
liberty-secret  Opaque  1     1s
mysql-secret    Opaque  1     1s

==> v1/Job
NAME        DESIRED  SUCCESSFUL  AGE
mysql-init  1        0           1s

==> v1beta1/Ingress
NAME     HOSTS  ADDRESS  PORTS  AGE
liberty  *      80       0s


$
```

リリースを確認する。

```shell-session
$ helm ls --tls --namespace sugi
NAME	REVISION	UPDATED                 	STATUS  	CHART           	NAMESPACE
test	1       	Wed Oct 24 12:44:33 2018	DEPLOYED	hellomysql-0.1.0	sugi
ubuntu@icp31-single:~/hellomysql-chart$ helm status test --tls
LAST DEPLOYED: Wed Oct 24 12:44:33 2018
NAMESPACE: sugi
STATUS: DEPLOYED

RESOURCES:
==> v1/PersistentVolumeClaim
NAME       STATUS  VOLUME    CAPACITY  ACCESS MODES  STORAGECLASS  AGE
mysql-pvc  Bound   mysql-pv  10Gi      RWO           mysql         1m

==> v1/Service
NAME     TYPE       CLUSTER-IP  EXTERNAL-IP  PORT(S)   AGE
liberty  ClusterIP  10.0.0.153  <none>       9080/TCP  1m
mysql    ClusterIP  10.0.0.167  <none>       3306/TCP  1m

==> v1/Deployment
NAME     DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
liberty  1        1        1           1          1m
mysql    1        1        1           1          1m

==> v1beta1/Ingress
NAME     HOSTS  ADDRESS      PORTS  AGE
liberty  *      54.95.4.186  80     1m

==> v1/Secret
NAME            TYPE    DATA  AGE
liberty-secret  Opaque  1     1m
mysql-secret    Opaque  1     1m

==> v1/ConfigMap
NAME            DATA  AGE
liberty-config  3     1m

==> v1/PersistentVolume
NAME      CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS  CLAIM           STORAGECLASS  REASON  AGE
mysql-pv  10Gi      RWO           Retain          Bound   sugi/mysql-pvc  mysql         1m

==> v1/Job
NAME        DESIRED  SUCCESSFUL  AGE
mysql-init  1        1           1m

==> v1/Pod(related)
NAME                      READY  STATUS     RESTARTS  AGE
liberty-74fd798747-7gqsp  1/1    Running    0         1m
mysql-7c47657796-8gj5x    1/1    Running    0         1m
mysql-init-9cwl8          0/1    Completed  2         1m


$
```

アプリケーションにアクセスする。

```shell-session
$ curl -k https://mycluster.icp/liberty/hellomysql/HelloServlet
user1
user2
user3
user4
user5
user6
Served at: /hellomysql$
```

これで雑なチャートの作成は完了。

削除しておく。

```
helm delete --purge test --tls
sudo rm -rf /data/mysql/*
```

## GitHub Pagesでの公開

GitHubにはGitHub Pagesという静的サイトのホスティング機能があるので、この機能を使ってChartリポジトリを作成する。

GitHubでリポジトリを作成する。リポジトリ名は任意。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/86449/635eb438-92e2-1146-2e83-5cc05a81e27a.png)

作成したリポジトリをローカルにcloneし、`gh-pages`というブランチを作成する。このブランチの内容がGitHub Pagesとして公開される。

```shell-session
$ git clone git@github.com:sotoiwa/charts.git
Cloning into 'charts'...
warning: You appear to have cloned an empty repository.
$ cd charts/
$ git checkout -b gh-pages
Switched to a new branch 'gh-pages'
$
```

リポジトリの直下に`stable`ディレクトリを作成する。

```
mkdir stable
cd stable
```

試しに雛形のチャートを作り、そのままパッケージする。

```shell-session
$ helm create example
Creating example
$ helm package example
Successfully packaged chart and saved it to: /home/ubuntu/workspace/charts/stable/example-0.1.0.tgz
$
```

stableディレクトリで以下のコマンドを実行してインデックスファイルを作成する。

```
helm repo index .
```

stableディレクトリが以下の構造になっていればChartリポジトリとして成立する。

```shell-session
$ tree .
.
├── example
│   ├── Chart.yaml
│   ├── charts
│   ├── templates
│   │   ├── NOTES.txt
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── ingress.yaml
│   │   └── service.yaml
│   └── values.yaml
├── example-0.1.0.tgz
└── index.yaml

3 directories, 9 files
$
```

コミットする。

```
git add -A
git commit -m "example 0.1.0"
git push --set-upstream origin gh-pages
```

Liberty-MySQLのアプリケーションのチャートのディレクトリとアーカイブをstableディレクトリに追加する。

```shell-session
$ tree .
.
├── example
│   ├── Chart.yaml
│   ├── charts
│   ├── templates
│   │   ├── NOTES.txt
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── ingress.yaml
│   │   └── service.yaml
│   └── values.yaml
├── example-0.1.0.tgz
├── hellomysql
│   ├── Chart.yaml
│   └── templates
│       ├── liberty-config.yaml
│       ├── liberty-deployment.yaml
│       ├── liberty-ingress.yaml
│       ├── liberty-secret.yaml
│       ├── liberty-service.yaml
│       ├── mysql-deployment.yaml
│       ├── mysql-init-job.yaml
│       ├── mysql-pv.yaml
│       ├── mysql-pvc.yaml
│       ├── mysql-secret.yaml
│       └── mysql-service.yaml
├── hellomysql-0.1.0.tgz
└── index.yaml

5 directories, 22 files
$
```

インデックスを更新する。

```
helm repo index .
```

コミットする。

```
git add -A
git commit -m "hellomysql 0.1.0"
git push
```

リポジトリの設定ページを開き、gh-pagesブランチが公開されていることを確認する。他のブランチの内容を公開したい場合はここでソースを選ぶ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/86449/e4f146f9-2217-2b6f-87fb-de536346241e.png)

インデックスにアクセスできることを確認する。

```shell-session
$ curl https://sotoiwa.github.io/charts/stable/index.yaml
apiVersion: v1
entries:
  example:
  - apiVersion: v1
    appVersion: "1.0"
    created: 2018-10-24T13:49:48.400067463Z
    description: A Helm chart for Kubernetes
    digest: 12cdaf8b7c7fafe17632d770af40518ca5ab037df4e033e0ce276ac6eb6e5b98
    name: example
    urls:
    - example-0.1.0.tgz
    version: 0.1.0
  hellomysql:
  - apiVersion: v1
    appVersion: "1.0"
    created: 2018-10-24T13:49:48.400404953Z
    description: A Helm chart for Kubernetes
    digest: 30d09a58f79d57e0d775212c10b4baa386fa937a576b1867491e5cfb7bdd257b
    name: hellomysql
    urls:
    - hellomysql-0.1.0.tgz
    version: 0.1.0
generated: 2018-10-24T13:49:48.399585684Z
$
```

Chartリポジトリとして追加する。

```
helm repo add sotoiwa-stable https://sotoiwa.github.io/charts/stable
```

確認。

```shell-session
$ helm repo list
NAME          	URL
stable        	https://kubernetes-charts.storage.googleapis.com
local         	http://127.0.0.1:8879/charts
ibm-charts    	https://raw.githubusercontent.com/IBM/charts/master/repo/stable/
sotoiwa-stable	https://sotoiwa.github.io/charts/stable
$ helm search sotoiwa
NAME                     	CHART VERSION	APP VERSION	DESCRIPTION
sotoiwa-stable/example   	0.1.0        	1.0        	A Helm chart for Kubernetes
sotoiwa-stable/hellomysql	0.1.0        	1.0        	A Helm chart for Kubernetes
$
```

## マシなチャートの作成

雑なチャートはパラメータが何もなかったり、リソースの名前が固定だったり、Secretが含まれていたり問題があるので、もう少しマシなチャートを作成する。

### 参考資料

`helm create`コマンドが生成する雛形や、既成のチャートをダウンロードして中身を参考にしてチャートを作成しようとしたが、Helmの公式サイトに非常に分かりやすいドキュメント（チュートリアル）があるので、結局そちらを読んだほうが早い。チャートの記載に必要なGoテンプレート言語についてもだいたいわかる。

- [Charts](https://docs.helm.sh/developing_charts/)
- [The Chart Template Developer’s Guide](https://docs.helm.sh/chart_template_guide/)
- [The Chart Best Practices Guide](https://docs.helm.sh/chart_best_practices/)

helmコマンドで雛形を生成する場合は以下のようにする。

```
helm create hellomysql
```

既成のチャートをダウンロードして参考にする場合で、デフォルトのstableリポジトリではなくibm-chartsのリポジトリからチャートをダウンロードする場合は、まずリポジトリを追加する。

```
helm repo add ibm-charts https://raw.githubusercontent.com/IBM/charts/master/repo/stable/
```

チャートのダウンロードは以下のようにする。

```
helm fetch ibm-charts/ibm-websphere-liberty
helm fetch ibm-charts/ibm-microclimate
helm fetch ibm-charts/ibm-icplogging
```

あるいはGitリポジトリをクローンしてもよい。

https://github.com/IBM/charts

なお、Libertyのチャートはサブチャートを使うようになっている。ibm-websphere-libertyのチャートとibm-open-libertyのチャートがそれぞれサブチャートとしてibm-shared-libertyのチャートを含んでいる。

### オプションのファイルの検討

チャートのファイルのうち、今回どこまで作成するかを検討する。

|ファイル|説明|必須|今回の方針|
|---|---|---|---|
|Chart.yaml|チャートの情報を含むyaml|必須|作成する|
|LICENSE|チャートのライセンス情報を含むplain txt|オプション|作成しない|
|README.md|MarkDownで書かれたREADME|オプション|作成しない|
|requirements.yaml|サブチャートへの依存性をリストしたyaml|オプション|サブチャートは使わないので作成しない|
|values.yaml|チャートのデフォルト値|必須|作成する|
|charts/*|サブチャート|オプション|サブチャートは使わないので作成しない|
|templates/*|マニフェストテンプレート|必須|作成する|
|templates/NOTES.txt|オプション|チャートをinstallしたときに表示される説明を記載したprain txt|作成する|

### 含めるマニフェストの検討

PVについては環境に依存するためチャートには含めない方がよさそう。Secretもチャートには含めない方がよさそう。Ingressも環境に依存するのでON/OFFを切り替えられるようにした方がよさそうだが、ICP前提で今回はとりあえずそのままにする。

PVとSecretは事前に作成するように`README.md`でや`NOTES.txt`でガイドするべきだが今回は省略。

### パラメータの検討

それ以外にパラメータ化したいものを検討する。だいたい以下か？

- Dockerイメージ
- Libertyのレプリカ数
- パスワードを格納するSecret名
- PVのサイズ
- PVのストレージクラス

これらを`values.yaml`にする。

```yaml:values.yaml
mysql:
  repository: mysql
  tag: "5.7"
  pullPolicy: IfNotPresent
  secretName: mysql-secret
  persistence:
    size: 10Gi
    storageClassName: mysql

init:
  repository: sotoiwa540/hellomysql-init
  tag: "1.0"
  pullPolicy: Always

liberty:
  repository: sotoiwa540/hellomysql
  tag: "1.0"
  pullPolicy: Always
  replicaCount: 1
  secretName: liberty-secret
```

`tag`の`1.0`はクオートしないと数値の`1`として扱われてイメージのPullに失敗したので注意。

### テンプレートの検討

`helm create`コマンドが生成した雛形の`_helpers.tpl`を確認すると、このテンプレートは以下のNamed Templateを定義している。

|Named Template|説明|
|---|---|
|<チャート名>.name|Expand the name of the chart.|
|<チャート名>.fullname|Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name.|
|<チャート名>.chart|Create chart name and version as used by the chart label.|

`<チャート名>.name`は以下のようなテンプレート。

```golang
{{- define "example-v2.11.0.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
```

- `default .Chart.Name .Values.nameOverride`は、`.Values.nameOverride`が与えられなかったときデフォルト値`.Chart.Name`を返す
- `trunc 63`は63文字で切り捨て
- `trimSuffix "-"`は末尾の`-`を除去

つまり普通は`.Chart.Name`を返すが、オーバーライドを可能にしたり、もう少し細やかな対応をしてくれている。

`<チャート名>.fullname`は`.Chart.Name`と`.Release.Name`を元に少し複雑なことをやっているが、普通は`.Release.Name`と`.Chart.Name`をハイフンでつないだものを返す。

`<チャート名>.chart`も普通は`.Chart.Name`と`.Chart.Version`

今回はわかりやすさを優先し、上記を簡単にした以下のテンプレートを使う使わずに各リソースにそのまま`.Chart.Name`などを書くようにする。

なおテンプレートで使われている関数についてはGoテンプレート言語そのもののものもあるが、多くがsprigというテンプレートライブラリのもので、そちらのドキュメントから使い方を確認できる。

- [Go: text/template](https://godoc.org/text/template)
- [Sprig Function Documentation](http://masterminds.github.io/sprig/)

### リソース名の検討

雛形が生成した`deployment.yaml`では`<チャート名>.fullname`を使っている。

```
  name: {{ template "example-v2.9.1.fullname" . }}
```

これは普通は`<リリース名>-<チャート名>`だが、今回は、Deploymentリソースが複数あるので、この指定では重複してしまうので、`<リリース名>-<元々のリソース名>`にする。

### ラベルの検討

既存のibm-chartsのチャートをみるとリソースには以下のラベルがつくようになっている。

```yaml
  labels:
    app: {{ include "sch.names.fullName" (list .) }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
```

一方`helm create`で生成した雛形のDeployment定義をみると、このラベルはhelmクライアントのバージョンによって異なっていて、どこかのバージョンから標準ラベルが変わったようだ。

```yaml:v2.9.1
  labels:
    app: {{ template "example-v2.9.1.name" . }}
    chart: {{ template "example-v2.9.1.chart" . }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
```

```yaml:v2.11.0
  labels:
    app.kubernetes.io/name: {{ include "example-v2.11.0.name" . }}
    helm.sh/chart: {{ include "example-v2.11.0.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
```

このラベルについては[The Chart Best Practices Guide](https://docs.helm.sh/chart_best_practices/)のなかに[Standard Labels](https://docs.helm.sh/chart_best_practices/#standard-labels)として説明があり、Helm自体がこのラベルを使用するわけではなく、あくまでオプションであるという記載がある。また、v2.11.0のhelmコマンドが生成したラベルのほうが最新のラベルであることも確認できる。

今回はv2.9.1の方のラベルをつけることにする。
なお、`template`を使うか`include`を使うかも変わっている。`template`をより高機能にしたHelm専用の関数が`include`で、結果をパイプラインで他の関数に渡すことができる（`template`ではできない）。

### Lifecycle Hookの検討

フックを使ってインストール後にJobを実行させることができそうなので、MySQLのデータの初期化のJobはHookを使って実行してみる。

- [Hooks](https://docs.helm.sh/developing_charts/#hooks)

```yaml
metadata:
  annotations:
    "helm.sh/hook": "post-install"
```

フックを使うとリリースの管理下に入らないことに注意。

### デバッグ

チャートのデバックは以下のコマンドを利用すると便利。

静的なチェックは以下。

```
helm lint hellomysql
```

tillerでテンプレートのレンダリングを実行し、生成したマニフェストを表示する。

```
helm install --debug --dry-run hellomysql
```

### チャートの作成

上記をふまえて以下の作業を実施する。

- 各マニフェストに`chart`、`release`、`heritage`のラベルをつける
- Deploymentの`PodTemplate`と`selector`にも`chart`、`release`、`heritage`のラベルを追加する
- Serviceの`selector`で`chart`、`release`、`heritage`のラベルを指定する
- - 各マニフェストのnameに`{{ .Release.Name }}-`を追加する
- Ingressのバックエンドの`serviceName`を`{{ .Release.Name }}-liberty`に変える
- ConfigMapの`mysql-servername`を`"{{ .Release.Name }}-mysql"`に変える
- ConfigMapを参照しているDeploymentでConfigMap名を`{{ .Release.Name }}-liberty-config`に
- MySQLがマウントするPVCの`claimName`を`{{ .Release.Name }}-mysql`に変える
- `values.yaml`に定義したものをマニフェストで`.Values`から取得するようにする。
- JobにLifecycle Hookのアノテーションをつける
- Jobが接続するMySQLホスト名を`{{ .Release.Name }}-mysql`にする
- PVとsecretをマニフェストから除外する

最終的に作成したマニフェスト・テンプレートは以下。

```
$ tree hellomysql
hellomysql
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── liberty-config.yaml
│   ├── liberty-deployment.yaml
│   ├── liberty-ingress.yaml
│   ├── liberty-service.yaml
│   ├── mysql-deployment.yaml
│   ├── mysql-init-job.yaml
│   ├── mysql-pvc.yaml
│   └── mysql-service.yaml
└── values.yaml

2 directories, 11 files
$
```

```yaml:Charts.yaml
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: hellomysql
version: 0.1.1
```

```:NOTES.txt
Thank you for installing {{ .Chart.Name }}.

Your release is named {{ .Release.Name }}.

To learn more about the release, try:

  $ helm status {{ .Release.Name }}
  $ helm get {{ .Release.Name }}
```

```yaml:liberty-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-liberty-config
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
data:
  mysql-servername: "{{ .Release.Name }}-mysql"
  mysql-portnumber: "3306"
  mysql-user: "liberty"
```

```yaml:liberty-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-liberty
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  selector:
    matchLabels:
      app: liberty
      chart: {{ .Chart.Name }}-{{ .Chart.Version }}
      release: {{ .Release.Name }}
      heritage: {{ .Release.Service }}
  replicas: {{ .Values.liberty.replicaCount }}
  template:
    metadata:
      labels:
        app: liberty
        chart: {{ .Chart.Name }}-{{ .Chart.Version }}
        release: {{ .Release.Name }}
        heritage: {{ .Release.Service }}
    spec:
      containers:
      - name: liberty
        image: "{{ .Values.liberty.repository }}:{{ .Values.liberty.tag }}"
        imagePullPolicy: {{ .Values.liberty.pullPolicy }}
        ports:
        - containerPort: 9080
        env:
        - name: MYSQL_SERVERNAME
          valueFrom:
            configMapKeyRef:
              name: {{ .Release.Name }}-liberty-config
              key: mysql-servername
        - name: MYSQL_PORTNUMBER
          valueFrom:
            configMapKeyRef:
              name: {{ .Release.Name }}-liberty-config
              key: mysql-portnumber
        - name: MYSQL_USER
          valueFrom:
            configMapKeyRef:
              name: {{ .Release.Name }}-liberty-config
              key: mysql-user
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: liberty-secret
              key: liberty-password
```

```yaml:liberty-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: {{ .Release.Name }}-liberty
  annotations:
    ingress.kubernetes.io/rewrite-target: /
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  rules:
  - host:
    http:
      paths:
      - path: /liberty
        backend:
          serviceName: {{ .Release.Name }}-liberty
          servicePort: 9080
```

```yaml:liberty-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-liberty
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  type: ClusterIP
  selector:
    app: liberty
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
  ports:
  - protocol: TCP
    port: 9080
```

```yaml:mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-mysql
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
      chart: {{ .Chart.Name }}-{{ .Chart.Version }}
      release: {{ .Release.Name }}
      heritage: {{ .Release.Service }}
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
        chart: {{ .Chart.Name }}-{{ .Chart.Version }}
        release: {{ .Release.Name }}
        heritage: {{ .Release.Service }}
    spec:
      containers:
      - name: mysql
        image: "{{ .Values.mysql.repository }}:{{ .Values.mysql.tag }}"
        imagePullPolicy: {{ .Values.mysql.pullPolicy }}
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: {{ .Values.mysql.secretName }}
              key: root-password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: {{ .Release.Name }}-mysql-pvc
```

```yaml:mysql-init-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-mysql-init
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
  annotations:
    "helm.sh/hook": "post-install"
spec:
  template:
    spec:
      containers:
      - name: init-hellomysql
        image: "{{ .Values.init.repository }}:{{ .Values.init.tag }}"
        imagePullPolicy: {{ .Values.init.pullPolicy }}
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: {{ .Values.mysql.secretName }}
              key: root-password
        command: ["sh", "-c"]
        args: ["mysql -u root -p${MYSQL_ROOT_PASSWORD} -h {{ .Release.Name }}-mysql < init.sql"]
      restartPolicy: OnFailure
```

```yaml:mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: {{ .Release.Name }}-mysql-pvc
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  storageClassName: {{ .Values.mysql.persistence.storageClassName }}
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: {{ .Values.mysql.persistence.size }}
```

```yaml:mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-mysql
  labels:
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
    chart: {{ .Chart.Name }}-{{ .Chart.Version }}
    release: {{ .Release.Name }}
    heritage: {{ .Release.Service }}
```

ポート番号が固定なのに一部では環境変数を使っているなど中途半端なところがあるがとりあえずこれでよし。

### リリース確認

はじめにPVとsecret作成する。

```yaml:mysql-pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv
spec:
  storageClassName: mysql
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/data/mysql"
```

```
kubectl apply -f mysql-pv.yaml
```

```yaml:mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  root-password: cGFzc3dvcmQ=
```

```
kubectl apply -f mysql-secret.yaml -n sugi
```

```yaml:liberty-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: liberty-secret
type: Opaque
data:
  liberty-password: bGliZXJ0eQ==
```

```
kubectl apply -f liberty-secret.yaml -n sugi
```

チャートをリリースする。チャートはチャート格納ディレクトリーを直接指定。Liberyのレプリカ数をパラメータとして与えてみる。

```
helm install hellomysql --debug --dry-run --name test --namespace sugi --set liberty.replicaCount=2 --tls
helm install hellomysql --name test --namespace sugi --set liberty.replicaCount=2 --tls
```

```shell-session
ubuntu@icp31-single:~/workspace/charts/stable$ helm install hellomysql --name test --namespace sugi --set liberty.replicaCount=2 --tls
NAME:   test
LAST DEPLOYED: Mon Nov  5 01:23:12 2018
NAMESPACE: sugi
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                 DATA  AGE
test-liberty-config  3     35s

==> v1/PersistentVolumeClaim
NAME            STATUS  VOLUME    CAPACITY  ACCESS MODES  STORAGECLASS  AGE
test-mysql-pvc  Bound   mysql-pv  10Gi      RWO           mysql         35s

==> v1/Service
NAME          TYPE       CLUSTER-IP  EXTERNAL-IP  PORT(S)   AGE
test-liberty  ClusterIP  10.0.0.136  <none>       9080/TCP  35s
test-mysql    ClusterIP  10.0.0.71   <none>       3306/TCP  35s

==> v1/Deployment
NAME          DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
test-liberty  2        2        2           2          34s
test-mysql    1        1        1           1          33s

==> v1beta1/Ingress
NAME          HOSTS  ADDRESS      PORTS  AGE
test-liberty  *      54.95.4.186  80     33s

==> v1/Pod(related)
NAME                           READY  STATUS   RESTARTS  AGE
test-liberty-7d8f7497b9-czlk8  1/1    Running  0         33s
test-liberty-7d8f7497b9-xqgvh  1/1    Running  0         33s
test-mysql-cfc8c8c86-9rv7b     1/1    Running  0         33s


NOTES:
Thank you for installing hellomysql.

Your release is named test.

To learn more about the release, try:

  $ helm status test
  $ helm get test
ubuntu@icp31-single:~/workspace/charts/stable$
```

上記出力で、JobはLifecycleのため上記リリースに含まれていないことが確認できる。

ジョブが完了したことを確認する。

```shell
ubuntu@icp31-single:~/workspace/charts/stable$ kubectl get job -n sugi
NAME              DESIRED   SUCCESSFUL   AGE
test-mysql-init   1         1            6m
ubuntu@icp31-single:~/workspace/charts/stable$ kubectl get po -n sugi
NAME                            READY     STATUS      RESTARTS   AGE
test-liberty-7d8f7497b9-czlk8   1/1       Running     0          6m
test-liberty-7d8f7497b9-xqgvh   1/1       Running     0          6m
test-mysql-cfc8c8c86-9rv7b      1/1       Running     0          6m
test-mysql-init-7hbzf           0/1       Completed   1          6m
ubuntu@icp31-single:~/workspace/charts/stable$
```

アクセス確認。

```shell-session
ubuntu@icp31-single:~/workspace/charts/stable$ curl -k https://mycluster.icp/liberty/hellomysql/HelloServlet
user1
user2
user3
user4
user5
user6
Served at: /hellomysqlubuntu@icp31-single:~/workspace/charts/stable$
```

以上。

## 参考リンク

- [Helmの概要とChart(チャート)の作り方](https://qiita.com/thinksphere/items/5f3e918015cf4e63a0bc)