KubernetesのYAML管理ツールkustomizeを試してみた

# [kustomize](https://github.com/kubernetes-sigs/kustomize)とは
* kubernetes(以下、k8sと表記)のYAML設定ファイルをパッケージングするツール
* 基盤となるbase構成から、カスタマイズを加えてパッケージングすることができる
* 出力は単一のYAMLファイルになる
* あくまでもパッケージングのためのツールなので、出来上がったYAMLをどのようにk8sに適用するかまでは面倒を見てくれない
* [Helm](https://helm.sh/)と競合しそうだが、以下の点で差別化できる模様
    * Helm: 設定値をテンプレートエンジンで変更可能にしておく必要がある
        * ==> カスタマイズされる元ファイル + カスタマイズのためのファイルについて学習が必要
    * kustomize: あくまでも入力・出力共に`kubectl apply`できるYAMLを扱う
        * ==> カスタマイズのためのファイル書式のみ学習すればOK

## 気に入ったところ
* configmap名の末尾にハッシュ値を付与することにより、強制的な設定値の再読込が可能
* とにかくひとつのYAMLで出力されること。シンプル！

## 好きじゃないところ
* configmapの名前を自動で変更してしまうので、古い設定値が残り続ける(強制的な設定値の再読込とトレードオフだが。。。)

# kustomizeを試してみる

## インストール
公式のインストールガイドを参照してください
https://github.com/kubernetes-sigs/kustomize/blob/master/INSTALL.md

## kustomization.yaml
* `kustomization.yaml`とは、kustomizeの設定ファイルです。詳しくは[公式ドキュメント](https://github.com/kubernetes-sigs/kustomize/blob/master/docs/glossary.md#kustomization)参照
* [公式の例](https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/docs/kustomization.yaml)からざっくり意訳しました

```yaml:kustomization.yaml
# すべてのリソースに設定するnamespace
namespace: my-namespace

# リソース名のprefix
namePrefix: alices-

# すべてのリソースに追加するlabelとselector
commonLabels:
  someName: someValue
  owner: alice
  app: bingo

# すべてのリソースに追加するAnnotation
commonAnnotations:
  oncallPager: 800-555-1212

# kustomizeでビルドする対象ファイル
resources:
- some-service.yaml
- ../some-dir/some-deployment.yaml

# ファイルからconfigmapを生成する設定
configMapGenerator:
- name: myJavaServerProps
  files:
  - application.properties
  - more.properties

# コマンドからsecretを生成する設定
secretGenerator:
- name: app-tls
  commands:
    tls.crt: "cat secret/tls.cert"
    tls.key: "cat secret/tls.key"
  type: "kubernetes.io/tls"
- name: downloaded_secret
  commands:
    username: "curl -s https://path/to/secrets/username.yaml"
    password: "curl -s https://path/to/secrets/password.yaml"
  type: Opaque

# baseとなるkustomization.yamlを含むディレクトリ
bases:
- ../../base

# patchを当てるためのファイルを指定する
patches:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml

# カスタムリソースの定義
crds:
- crds/typeA.yaml
- crds/typeB.yaml

# kustomize内で使用できるプレースホルダの設定。 $(HOGEHOGE)の形式で参照できる
vars:
- name: SOME_SECRET_NAME
  objref:
    kind: Secret
    name: my-secret
    apiVersion: v1
- name: MY_SERVICE_NAME
  objref:
    kind: Service
    name: my-service
    apiVersion: v1
  fieldref:
    fieldpath: metadata.name
- name: ANOTHER_DEPLOYMENTS_POD_RESTART_POLICY
  objref:
    kind: Deployment
    name: my-deployment
    apiVersion: apps/v1
  fieldref:
    fieldpath: spec.template.spec.restartPolicy
```

表にまとめると以下のとおりです。 
Demo=`-`の部分は暇があれば追加するかもしれません。

| 項目               | 概要                                                                       | Demo |
| ------------------ | -------------------------------------------------------------------------- | ---- |
| namespace          | すべてのリソースに設定するnamespace                                        | -    |
| namePrefix         | リソース名のprefix                                                         | -    |
| commonLabels       | すべてのリソースに追加するlabelとselector                                  | -    |
| commonAnnotations  | すべてのリソースに追加するAnnotation                                       | -    |
| resources          | kustomizeでビルドする対象ファイル                                          | [Demo1](#demo1パッケージング)|
| configMapGenerator | ファイルからconfigmapを生成する設定                                        | -    |
| secretGenerator    | コマンドからsecretを生成する設定                                           | -    |
| bases              | baseとなるkustomization.yamlを含むディレクトリ                             | [Demo2](#demo2設定値の上書き)|
| patches            | patchを当てるためのファイルを指定する                                      | [Demo2](#demo2設定値の上書き)|
| crds               | カスタムリソースの定義                                                     | -    |
| vars               | kustomize内で使用できるプレースホルダの設定。$(HOGEHOGE)の形式で参照できる | -    |

## Demo1(パッケージング)

configmapで出力を設定できるnginxを用意しました。
deployment, configmap, serviceで3つのYAMLファイルに分かれています。
さらにこれらをkustomizeでひとまとめにするための設定を`kustomization.yaml`に記述しました。

```bash:ファイル一覧
└── demo1
    ├── configmap.yaml
    ├── deployment.yaml
    ├── kustomization.yaml #kustomize用の設定ファイル
    └── service.yaml
```

```yaml:kustomization.yaml
resources: #今回は取り上げていませんが、resourcesの設定に`path/to/yamls/*` などとワイルドカード指定が使えます
- deployment.yaml
- configmap.yaml
- service.yaml
```

```yaml:deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - name: nginx-test
        image: nginx:alpine
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config
          mountPath: /usr/share/nginx/html
      volumes:
      - name: config
        configMap:
          name: nginx-test
```

```yaml:configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-test
data:
  index.html: hello kustomize
```

```yaml:service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-test
spec:
  type: NodePort
  selector:
    app: nginx-test
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 8080
```

これをkustomizeでパッケージングしてみます

```bash:コマンド
kustomize build demo1
```

結果は標準出力に表示されます。
configmapの名前がnginx-test-tkhfbfb9ffに変わっていることに注目してください。
末尾にハッシュ値が追加され。参照している側のdeploymentでも自動的に設定されています。
configmapのみの変更をk8sにデプロイしたら、Podがホットリロードに対応しておらず設定変更されない！ということがありましたが、
kustomizeならばdeploymentの設定値が変更されることにより、Podが自動的に再作成されて設定値が読み込まれます。

```yaml:出力
apiVersion: v1
data:
  index.html: hello kustomize
kind: ConfigMap
metadata:
  name: nginx-test-tkhfbfb9ff # configmapの名前が自動的に変更されている
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-test
spec:
  ports:
  - name: http
    nodePort: 8080
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx-test
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - image: nginx:alpine
        name: nginx-test
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: config
      volumes:
      - configMap:
          name: nginx-test-tkhfbfb9ff # 参照側も変わる
        name: config
```

一気にデプロイまでしたい場合は、以下のようなコマンドを実行するとよいでしょう。

```bash
kustomize build demo1 | kubectl apply -f -
```

きちんとデプロイされた場合、以下のようになるはずです
![hello-kustomize-demo1](https://qiita-image-store.s3.amazonaws.com/0/148893/8c8f2824-759d-9bf6-a9c9-b621c133bf44.png)

## Demo2(設定値の上書き)
Demo1で実施した内容を、部分的に書き換えてみます。(configmapの内容を変更し、レプリカ数を2に変更します)
変えたい部分だけのYAMLを作成し、ほかはdemo1を参照させます。
patch用のYAMLは、最低限apiVersion, kind, metadata.nameがあれば動くようです。
ここでは取り上げていませんが、baseは複数あっても動作します。

```bah:ファイル一覧
├── demo1
〜〜〜〜〜〜
└── demo2
    ├── configmap.yaml
    ├── deployment.yaml
    └── kustomization.yaml
```

```yaml:kustomization.yaml
bases:
- ../demo1
patches:
- deployment.yaml
- configmap.yaml
```

```yaml:deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
spec:
  replicas: 2
```

```yaml:configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-test
data:
  index.html: hello kustomize demo2
```

```bash:コマンド
kustomize build demo2
```

```yaml:出力
apiVersion: v1
data:
  index.html: hello kustomize demo2
kind: ConfigMap
metadata:
  name: nginx-test-9gb64hc8d7
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-test
spec:
  ports:
  - name: http
    nodePort: 8080
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx-test
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - image: nginx:alpine
        name: nginx-test
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: config
      volumes:
      - configMap:
          name: nginx-test-9gb64hc8d7
        name: config
```

デプロイするともちろん内容が変わります
![hello-kustomize-demo2](https://qiita-image-store.s3.amazonaws.com/0/148893/9bd1ec45-c6dd-dde5-4b65-5346f881db40.png)

# その他

## 検証環境
* k8s: v1.11.0
* kubectl: v1.11.0
* kustomize: v1.0.3

## 参考
* https://github.com/kubernetes-sigs/kustomize
* https://deeeet.com/writing/2018/07/10/kustomize/
* https://kubernetes.io/blog/2018/05/29/introducing-kustomize-template-free-configuration-customization-for-kubernetes/