helmを使ったK8s用yamlのテンプレートの利用

helmを導入してK8syamlをテンプレート記載したい

  • helmとは
  • なぜhelmがほしいのか

helmとは

Kubernetes(K8s)に関する技術で、K8s用のyamlを記載しやすいようにするモノ、という理解
そもそもパッケージ管理ソフトなので、wordpressK8sで入れたい!とかいうときに世の中にあるパッケージを使ってインストールということも可能(ぽい)

なぜhelmがほしいのか

どういうシーンでほしいかというと
例えば、iamgeのpullするUrlを隠蔽したい場合、です。(もしくは環境毎にimageの情報が変わる、など)

  • imageはECRからpullしたい
    • 例: <i>aws_account_id</i>.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latestからpullしたい
  • でもGitHubのpublicなリポジトリに直接AWSアカウントをpushしたくない
    • 上記例でいうとaws_account_idの部分は秘匿情報なのでGitHubにPushしたくない
結論

結論はこちら

例えば、

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  labels:
    app: sample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: sample
        image: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest
        ports:
        - containerPort: 80

こんなふうに、直接imageを記載するのを避けたい。
aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latestを直接記載したくない)

version情報

$ helm version
version.BuildInfo{Version:"v3.3.3", GitCommit:"55e3ca022e40fe200fbc855938995f40b2a68ce0", GitTreeState:"dirty", GoVersion:"go1.15.2"}
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0", GitCommit:"70132b0f130acc0bed193d9ba59dd186f0e634cf", GitTreeState:"clean", BuildDate:"2019-12-13T11:52:32Z", GoVersion:"go1.13.4", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:18:29Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/amd64"}

helm

上記問題点をhelmを使うことによって解決できます

helmはyamlの一つの機能として、テンプレーティングを行えます。
上記sample-deploymentというdeploymentを例にすると

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  labels:
    app: sample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: sample
        image: {{ .Values.app.imageUrl }}
        ports:
        - containerPort: 80

このように{{ .Values.app.imageUrl }}と記載し、この部分をhelmに別ファイルで渡すことができます
そしてその別ファイルをGitHubにpushしない運用によって、秘匿情報を隠蔽できたり、環境毎にyamlを用意しなくても済む、というもの。

helmを使うと、K8syamlを直接記載せずに、
上記テンプレートだけをひたすら記載するような運用になる(と思います)

helm使い方

  • helmのインストール
$ brew install helm
  • helmの初期化(プロジェクト作成)
$ helm create mychart

mychartフォルダにサンプルとなるchart類が作成されます
フォルダ構成は以下の感じ

./mychart
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

基本的にいじるのが、templatesディレクトリの中身と、values.yamlの中身

最小限の構成への変更

シンプルな構成とするために、上記helm create mychartで作ったchartから、不要部分を削除します
以下のファイルを削除

  • mychart/charts/削除
  • mychart/templates/の中身削除
  • mychart/values.yamlの中身を全削除
mychart/values.yamlを記入
app:
  imageUrl: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest
mychart/tempates/deployment.yamlを記入
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  labels:
    app: sample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: sample
        image: {{ .Values.app.imageUrl }}
        ports:
        - containerPort: 80

上記2つのファイルを記載した状態でheml installしていきます

helm installのチェック
$ helm install --dry-run --debug --generate-name ./mychart

これで、このhelmを実行した際に何が起こるか(どのようなK8sリソースが作成されるか)がわかる

$ helm install --dry-run --debug --generate-name ./mychart

NAME: mychart-1600747915
LAST DEPLOYED: Tue Sep 22 13:11:56 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
app:
  imageUrl: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest

HOOKS:
MANIFEST:
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  labels:
    app: sample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: sample
        image: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest
        ports:
        - containerPort: 80

このように表示されます
肝心のimage部分image: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latestとなっているので想定通り

helm install

helm installにてリソースをK8sに反映していきます。
以下のようなコマンド構成です。

$ helm install <chart-name> <chart-directory>

chartに名前を付ける必要があります
(省略できるようですが、省略した場合ランダムな名前がつけられてめんどくさいので、原則名前つけないといけない)

$ helm install chartname ./mychart
NAME: chartname
LAST DEPLOYED: Tue Sep 22 13:20:16 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

このように出ます。
kubectlで確認

$ kubectl get all 
NAME                                    READY   STATUS         RESTARTS   AGE
pod/sample-deployment-b978cbc69-lczpd   0/1     ErrImagePull   0          32s
pod/sample-deployment-b978cbc69-tswdg   0/1     ErrImagePull   0          32s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   9d

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/sample-deployment   0/2     2            0           32s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/sample-deployment-b978cbc69   2         2         0       32s

imageがめちゃくちゃ適当な名前(aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest)なので起動は失敗していますが、なんかそれっぽい。

helm list
$ helm listNAME            NAMESPACE       REVISION        UPDATED                                     STATUS          CHART               APP VERSION
chartname       default         1               2020-09-22 13:20:16.082367 +0900 JST        deployed        mychart-0.1.0       1.16.0     

インストール済みのhelmのlistが出ます

helm upgradeの準備

インストール済みのchartを更新したい場合にはhelm upgradeです

$ helm upgrade <chart-name>

今回以下の内容を変更します

  • deploymentのコンテナ名称をmyappにする
  • imageをnginx:latestにする
values.yamlの変更
app:
  imageUrl: nginx:latest
  appName: myapp

このように変更しました

deployment.yamlを変更
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  labels:
    app: sample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: {{ .Values.app.appName }}
        image: {{ .Values.app.imageUrl }}
        ports:
        - containerPort: 80

{{ .Values.app.appName }}を追記

helm upgradeの実行

helm upgradeによってchartが更新できるわけですが、その前に内容を確認したいです。
その場合には--dry-runを使います。
ついでに--debug表示もします

$ helm upgrade --dry-run --debug chartname ./mychart
upgrade.go:121: [debug] preparing upgrade for chartname
upgrade.go:129: [debug] performing update for chartname
upgrade.go:287: [debug] dry run for chartname
Release "chartname" has been upgraded. Happy Helming!
NAME: chartname
LAST DEPLOYED: Tue Sep 22 13:29:21 2020
NAMESPACE: default
STATUS: pending-upgrade
REVISION: 2
TEST SUITE: None
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
app:
  appName: myapp
  imageUrl: nginx:latest

HOOKS:
MANIFEST:
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
  labels:
    app: sample
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: myapp
        image: nginx:latest
        ports:
        - containerPort: 80

これで結果を確認できます(まだ適応されてはおらず、結果を表示しただけです)

pluginについて

helm upgradeを実行する前に、diffというプラグインを使ってみます
helmの差分を表示できるpluginのようです github.com

hlem用のプラグインが多く開発されており、いろいろなものがあるようです。

  • diff pluginのinstall
$ helm plugin install https://github.com/databus23/helm-diff
diffの確認

--valuesでvalues.yamlを指定しています(これしないとうまく差分が出ない)

helm diff upgrade chartname ./mychart --values ./mychart/values.yaml
default, sample-deployment, Deployment (apps) has changed:
  # Source: mychart/templates/deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: sample-deployment
    labels:
      app: sample
  spec:
    replicas: 2
    selector:
      matchLabels:
        app: sample
    template:
      metadata:
        labels:
          app: sample
      spec:
        containers:
-       - name: sample
-         image: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest
+       - name: myapp
+         image: nginx:latest
          ports:
          - containerPort: 80

内容は問題なさそうなので、helm upgradeをやっていきます

helm upgradeの実行

helm upgradeの実行をします
コマンドの実行結果は以下のような感じでした。

-values./mychart/values.yamlを指定しています(diffのコマンドからdiff部分を削除しただけ)

$ helm upgrade chartname ./mychart --values ./mychart/values.yaml 
Release "chartname" has been upgraded. Happy Helming!
NAME: chartname
LAST DEPLOYED: Tue Sep 22 13:58:40 2020
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
upgradeの結果の確認
$ kubectl get all
NAME                                     READY   STATUS    RESTARTS   AGE
pod/sample-deployment-787c75d69d-99gk7   1/1     Running   0          112s
pod/sample-deployment-787c75d69d-x6krf   1/1     Running   0          106s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   9d

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/sample-deployment   2/2     2            2           4m7s

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/sample-deployment-787c75d69d   2         2         2       112s
replicaset.apps/sample-deployment-b978cbc69    0         0         0       4m7s

imageをnginxにしたので動き出しました。

一部設定の隠蔽

今回の目的はGitHubにPushしたくないことでした。

以下のような方針で秘匿情報を隠蔽します

  • values.yamlにはデフォルト値を書いておく
  • override.yamlというファイルを作り、ここに秘匿情報を書く
    • 秘匿にしたい情報のみこのファイルに書く
    • ファイル名はなんでもOKです
  • override.yaml.gitignore登録しておく
override.yaml

以下のように記載します(今回はapp.imageUrlを隠蔽したい)

app:
  imageUrl: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest

app.imageUrlのみを上書きします

この状態でdiffしてみます
--valuesコマンドを2つ以上指定することができます

helm diff upgrade chartname ./mychart --values ./mychart/values.yaml --values ./mychart/override.yaml 
default, sample-deployment, Deployment (apps) has changed:
  # Source: mychart/templates/deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: sample-deployment
    labels:
      app: sample
  spec:
    replicas: 2
    selector:
      matchLabels:
        app: sample
    template:
      metadata:
        labels:
          app: sample
      spec:
        containers:
        - name: myapp
-         image: nginx:latest
+         image: aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest
          ports:
          - containerPort: 80

こうなります。
想定通り。

upgradeします
$ helm upgrade chartname ./mychart --values ./mychart/values.yaml --values ./mychart/override.yaml 

無事にupgradeが完了

helm delete

最後は削除します

$ helm delete chartname

まとめ

  • K8syamlをGit管理する際に秘匿情報の管理、もしくは環境毎に変化する情報の管理、というつらみがある
  • 一つの解決策として、helmのテンプレート機能による値の注入が可能
  • values.yamlにデフォルトの情報を記載し、別のyaml(override.yaml等)に可変情報を記載する
    • override.yamlは必要に応じて.gitignoreする