kustomizeのvarsを使って環境毎の変数をKubernetesの定義に埋め込んでみる

KubernetesのYAMLを環境毎のに分ける時にkubectlに標準で入っているkustomizeを使ってるのですが、サンプルなどに書かれている patchesStrategicMerge では以下のようなYAML内の値に変数を埋め込めないな。と考えていました。

kustomizeのリポジトリを見ていたら vars という設定を見つけたので、これをを上手く使えないか考えた方法を書いていきます。

github.com

利用ケース

例えば以下のCronJobのようなManifestがあった場合に $(ENV) などの変数を埋め込みたい場合に利用できます。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: test-cron-job
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Allow
  failedJobsHistoryLimit: 5
  successfulJobsHistoryLimit: 5
  startingDeadlineSeconds: 5
  suspend: false
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: test-job
              image: $(ENV)acr.azurecr.io/test-job-$(ENV)
              env:
                - name: ENV
                  value: $(ENV)
                - name: PROJECT
                  value: $(PROJECT)
                - name: LOCATION
                  value: $(LOCATION)

Varsの設定の読み方

これを実現するためにはkustomizeのvarsという設定を利用します。

具体的にはkustomization.ymlに以下のようにvarsの設定を書いていきます。

この読み方が最初わからなかったのですが、

name=test-cron-job が設定された batch/v1beta1.CronJob に対して、 metadata.labels.env の値を参照して、変数 ENV として設定する設定と理解しています。

日本語わかりにくいですね。実際にやってみます。

vars:
  - name: ENV
    objref:
      kind: CronJob
      name: test-cron-job
      apiVersion: batch/v1beta1
    fieldref:
      fieldpath: metadata.labels.env

ファイル構造

以下のkustomizeのサンプルなどに近い構造を作ってみます。

test-job/
├── base
│   ├── cronjob.yml
│   ├── kustomization.yml
│   └── varreference.yml
└── overlays
    ├── dev
    │   ├── cronjob_meta.yml
    │   └── kustomization.yml
    └── prod
        ├── cronjob_meta.yml
        └── kustomization.yml

ファイルの中身(overlays側)

この方式を採ると、環境毎に必用なoverlays側がシンプルになるのがメリットかと思っています。

overlays/${ENV}/cronjob_meta.yml

環境毎の設定はこのファイルに入れます。

labels の下に変数の設定をKeyValueで入れる方法にしました。かなりシンプルに設定書けます。

kindmetadata.name はbase側の定義との紐付けに利用します。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: test-cron-job
  labels:
    env: dev
    location: japan
    project: A

overlays/${ENV}/kustomization.yml

kustomizationはbase側を指定しているだけです。

もっと言えば、今回の設定ではbase側にLabelsを付与しているだけになります。

bases:
  - ../../base
patchesStrategicMerge:
  - cronjob_meta.yml

ファイルの中身(base側)

base側は共通の定義を作成します。

base/cronjob.yml

base/cronjob.yml にはは変数を $(VARIABLE) という形式で埋め込みます。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: test-cron-job
spec:
  schedule: "*/1 * * * *"
  concurrencyPolicy: Allow
  failedJobsHistoryLimit: 5
  successfulJobsHistoryLimit: 5
  startingDeadlineSeconds: 5
  suspend: false
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: test-job
              image: $(ENV)acr.azurecr.io/test-job-$(ENV)
              env:
                - name: ENV
                  value: $(ENV)
                - name: PROJECT
                  value: $(PROJECT)
                - name: LOCATION
                  value: $(LOCATION)

base/kustomization.yml

base/kustomizevarsconfigurations が設定されています。

namespace: otomo-test
resources:
  - cronjob.yml
configurations:
  - varreference.yml
vars:
  - name: ENV
    objref:
      kind: CronJob
      name: test-cron-job
      apiVersion: batch/v1beta1
    fieldref:
      fieldpath: metadata.labels.env
  - name: LOCATION
    objref:
      kind: CronJob
      name: test-cron-job
      apiVersion: batch/v1beta1
    fieldref:
      fieldpath: metadata.labels.location
  - name: PROJECT
    objref:
      kind: CronJob
      name: test-cron-job
      apiVersion: batch/v1beta1
    fieldref:
      fieldpath: metadata.labels.project

繰り返しになりますが、 vars の以下の設定は metadata.labels.location の値を、 $(LOCATION) に設定する意味になります。

  - name: LOCATION
    objref:
      kind: CronJob
      name: test-cron-job
      apiVersion: batch/v1beta1
    fieldref:
      fieldpath: metadata.labels.location

base/varreference.yml

最後に configurations で設定されていた varreference.yml についてです。

中身は以下になります。

varReference:
  - path: spec/jobTemplate/spec/template/spec/containers/image
    kind: CronJob

varsで設定した変数はどこにでも埋め込めるわけではなくて、以下のファイルに指定された要素にだけ埋め込みが可能です。

varReference を定義することでこれを拡張することができます。

kustomize/varreference.go at master · kubernetes-sigs/kustomize · GitHub

ちなみにvarreferenceの設定無しでkustomizeを実行すると、以下のように対応していないimageの要素だけが $(ENV) を埋められずに残ってしまいます。

          containers:
            name: test-job
            image: $(ENV)acr.azurecr.io/test-job-$(ENV)
          - env:
            - name: ENV
              value: prod
            - name: PROJECT
              value: Z
            - name: LOCATION
              value: us

実行結果

こちらのバージョンで実行してみます。

$ kubectl version --short
Client Version: v1.17.3
Server Version: v1.16.7

dev環境

まずはここまで書いてきたdev環境の実行結果です。ちゃんと設定が反映されています。

$ kubectl kustomize ./test-job/overlays/dev/
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  labels:
    env: dev
    location: japan
    project: A
  name: test-cron-job
  namespace: otomo-test
spec:
  concurrencyPolicy: Allow
  failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - env:
            - name: ENV
              value: dev
            - name: PROJECT
              value: A
            - name: LOCATION
              value: japan
            image: devacr.azurecr.io/test-job-dev
            name: test-job
          restartPolicy: Never
  schedule: '*/1 * * * *'
  startingDeadlineSeconds: 5
  successfulJobsHistoryLimit: 5
  suspend: false

prod環境

prod環境は overlays/prod/cronjob_meta.yml だけを修正します。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: test-cron-job
  labels:
    env: prod
    location: us
    project: Z

ちゃんとprod側の変数が反映されています。

$ kubectl kustomize ./test-job/overlays/prod/ 
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  labels:
    env: prod
    location: us
    project: Z
  name: test-cron-job
# --省略
          containers:
          - env:
            - name: ENV
              value: prod
            - name: PROJECT
              value: Z
            - name: LOCATION
              value: us
            image: prodacr.azurecr.io/test-job-prod
            name: test-job
# --省略