helm template: Helm Chart をマニフェストファイルとして展開する

## 概要

Helm はマニフェストファイルの集まりをパッケージとしてバージョンニングし管理できる Kubernetes のパッケージマネージャです。Helm は `helm` クライアントと `tiller` サーバの二つのコンポーネントから構成されており、Kubernetes クラスタ内で実行される `tiller` サーバが Helm のパッケージである Chart の管理、リリースを行ないます。

helm template は、Helm 1.7 で実装された helm クライアントのサブコマンドです。対象の Chart をマニフェスト形式で展開することができます。

```
$ helm template --help
Render chart templates locally and display the output.

This does not require Tiller. However, any values that would normally be
looked up or retrieved in-cluster will be faked locally. Additionally, none
of the server-side testing of chart validity (e.g. whether an API is supported)
is done.

To render just one template in a chart, use '-x':

        $ helm template mychart -x templates/deployment.yaml

Usage:
  helm template [flags] CHART

Flags:
  -x, --execute stringArray    only execute the given templates
      --kube-version string    override the Kubernetes version used as Capabilities.KubeVersion.Major/Minor (e.g. 1.7)
  -n, --name string            release name (default "RELEASE-NAME")
      --name-template string   specify template used to name the release
      --namespace string       namespace to install the release into
      --notes                  show the computed NOTES.txt file as well
      --set stringArray        set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)
  -f, --values valueFiles      specify values in a YAML file (can specify multiple) (default [])

Global Flags:
      --debug                     enable verbose output
      --home string               location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm")
      --host string               address of Tiller. Overrides $HELM_HOST
      --kube-context string       name of the kubeconfig context to use
      --tiller-namespace string   namespace of Tiller (default "kube-system")
```

*NOTE: helm template は Helm のプラグインとして提供されていた [technosophos/helm-template](https://github.com/technosophos/helm-template) を helm クライアント本体にビルトインしたものです。ビルトインされたことでプラグインの方はメンテナンスされなくなっているため、ビルトインの template サブコマンドを利用することを推奨します。*

## モチベーション

Chart のリリース管理を行う tiller サーバは、ServiceAccount の権限で Kubernetes apiserver にアクセスするため、"ユーザとしての" 権限でリリースを行うことができません。これはユーザの権限を RBAC で管理していたとしても、その権限を tiller サーバで利用することができないため、セキュリティ上の理由から tiller サーバによるリリースは利用できないケースがあります。しかし Helm 自体は利用したいモチベーションが存在します。例えば複数のサービスにおいて共通で利用されるミドルウェアなどは、マニフェストファイルを一箇所で管理したいが、ConfigMap などで変更できない `pod.spec.resources` などの設定項目を動的に変更したい場合に、マニフェストファイルを何かしらのテンプレーティングを行う必要があります。また複数バージョンの Kubernetes クラスタを管理する場合、クラスタ共通のアドオンのマニフェストファイルはバージョンにより apiVersion 等の設定が異なる場合がありここでもテンプレーティングが必要になってくるでしょう。

helm template は tiller サーバを一切利用せずに Chart からマニフェストファイルを展開することができる helm クライアントのサブコマンドです。テンプレーティングには他の選択肢もありますが、Helm のテンプレーティングを利用しておくことで将来的に上記の問題が解消された際は Helm によるリリースを利用することができますし、また Helm はデファクトになりつつあるため、コミュニティが活発であり多くのノウハウを利用することができます。

## helm template の使い方

Homebrew を利用している場合、下記のコマンドでインストールできます。利用していなければ[リリースページ](https://github.com/kubernetes/helm/releases)からダウンロードしてください。

```
$ brew install kubernetes-helm
```

次に Chart リポジトリの情報などをインストールするために下記のコマンドを実行します。今回 helm template のみの利用で tiller サーバは利用しないため、`--client-only` オプションを指定します。

```
$ helm init --client-only
```

次に利用する Chart をローカルに取得します。公式として提供されている Chart は https://kubeapps.com/ から探すことができます。今回は https://kubeapps.com/charts/stable/redis を利用します。

```
$ helm fetch stable/redis --version 1.0.2
$ file redis-1.0.2.tgz
```

最後に helm template でこの tgz ファイルを指定するとマニフェストファイルとして標準出力に展開されます。今回はアーカイブされたファイルを指定していますが、Chart が格納されたディレクトリでも問題ありません。

```
$ helm template redis-1.0.2.tgz
```

Helm はインストールするマニフェストの設定を動的に変更することができます。helm template でも install サブコマンドと同様に `--set` オプションを利用して設定することができます。また設定できる項目は helm inspect で確認することができます。

```
$ helm template redis-1.0.2.tgz --set imagePullPolicy=Always
$ helm inspect redis-1.0.2.tgz
```

### Kubernetes バージョンを明示的に指定する

Chart のマニフェストファイルでは `.Capabilities.KubeVersion` 変数に Kubernetes のバージョンが設定されており、テンプレーティングに利用することができます。通常 Kubernetes バージョンは tiller サーバがデプロイされているクラスタのバージョンを設定するため helm-template プラグインではこの値を利用できませんでしたが、ゼットラボ社内の要件で必要となったため、helm template で対象の Kubernetes バージョンを指定するための `--kube-version` オプションを実装し、Helm 1.7 から利用できるようになりました。

```
$ helm template redis-1.0.2.tgz --kube-version 1.8.3
```

このオプションを利用することで、Deployment の apiVersion を 1.8 向けには `apps/v1beta2`、それ未満なら `extensions/v1beta1` を使うようなことが実現できます。

```
# _helpers.tpl
{{/*
Return the appropriate apiVersion for deployment.
*/}}
{{- define "deployment.apiVersion" -}}
{{- if ge .Capabilities.KubeVersion.Minor "8" -}}
{{- print "apps/v1beta2" -}}
{{- else -}}
{{- print "extensions/v1beta1" -}}
{{- end -}}
{{- end -}}
```

```
# deployment.yaml
apiVersion: {{  template "deployment.apiVersion" .}}
kind: Deployment
metadata:
  name: heapster
  labels:
...
```

## まとめ

Helm は Kubernetes のパッケージマネージャとしてデファクトになりつつあり、セキュリティ上の理由から tiller サーバが利用できない場合においても、helm template を利用することでコミュニティの恩恵を得ることができます。`--kube-version` オプションも是非利用してみてください。

- https://github.com/kubernetes/helm

## おわりに

このエントリは、弊社 Z Lab のメンバーによる [Z Lab Advent Calendar 2017](https://qiita.com/advent-calendar/2017/zlab) の五日目として業務時間中に書きました。六日目は @summerwind による Prometheus のアラート管理についてです。