🔖

Kustomizeのreplacementsを使ってマニフェストファイルをちょっとDRYに書く

2022/12/18に公開

はじめに

最近はECK (Elastic Cloud on Kubernetes) を使ってKubernetes上にElastic Stack環境を構築するお仕事をしています。
Elastic Stackのプロダクト群 (Elasticsearch, Kibana, Beats, Logstash) のバージョンは同一にするのが望ましいので各リソースのマニフェストファイルに同一バージョンを指定するわけですが、同じ記述を複数のマニフェストファイルに書かなければいけないため冗長です。

そこでKustomizeの出番なわけですが、ECKのようなCustom Resourceに対応するには一工夫が必要になります。
やり方はいくつかあると思いますが、今回はKustomizeのreplacementsという機能を使ってみたのでご紹介したいと思います。

replacementsとは

replacementsはソースに指定されたリソースの値をターゲットのフィールドにコピーする機能になります。
百聞は一見にしかずということで本家ドキュメントのExampleを例に説明します。
まずは以下のようなJob, Pod, Secretがあるとします。

job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    spec:
      containers:
        - image: myimage
          name: hello
          env:
            - name: SECRET_TOKEN
              value: SOME_SECRET_NAME
resources.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - image: busybox
    name: myapp-container
  restartPolicy: OnFailure
---
apiVersion: v1
kind: Secret
metadata:
  name: my-secret

そして、今回のお題は

  • JobにPodと同じspec.restartPolicyを設定する
  • Jobのenv(SECRET_TOKEN)のvalue(SOME_SECRET_NAME)をSecret名(my-secret)で置き換える

というものになります。

kutomization.yamlは以下のようになります。
replacementsの部分がreplacementの設定になります。pathを指定して外部ファイルに記述することもできますし、インラインで記述することもできます。
具体的に何をやっているかはコメントやドキュメントを参考にしてください。

kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- resources.yaml
- job.yaml

replacements:
# 外部ファイルに設定を記述
- path: my-replacement.yaml
# インラインで設定を記述
- source:                    # ソースの設定
    kind: Secret             # ソースのkindはSecret
    name: my-secret          # ソースのnameはmy-secret
                             # コピーする値を明示的に指定しなければ、デフォルトではmetadata.nameがコピー元の値になる
  targets:                   # ターゲットの設定 (複数可)
  - select:                  # ターゲット0の選択
      name: hello            # ターゲット0のnameはhello
      kind: Job              # ターゲット0のkindはJob
    fieldPaths:              # コピー先のフィールド (複数可)
    - spec.template.spec.containers.[name=hello].env.[name=SECRET_TOKEN].value  # コピー先のフィールドパス
replacement.yaml
source:                                # ソースの設定
  kind: Pod                            # ソースのkindはPod
  name: my-pod                         # ソースのnameはmy-pod
  fieldPath: spec.restartPolicy        # コピーする値はspec.restartPolicyの値
targets:                               # ターゲットの設定 (複数可)
- select:                              # ターゲット0の選択
    name: hello                        # ターゲット0のnameはhello
    kind: Job                          # ターゲット0のkindはJob
  fieldPaths:                          # コピー先のフィールド (複数可)
  - spec.template.spec.restartPolicy   # コピー先のフィールドパス
  options:                             # オプション
    create: true                       # リソースにフィールドがなければ作成

kustomizeすると以下のような結果になります。
Jobにreplacementsで設定された値がコピーおよび作成されていることを確認してください。

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
---
apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    spec:
      containers:
      - env:
        - name: SECRET_TOKEN
          value: my-secret # this value is copied from my-secret
        image: myimage
        name: hello
      restartPolicy: OnFailure # this value is copied from my-pod
---
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - image: busybox
    name: myapp-container
  restartPolicy: OnFailure

実践

それではECKの各リソースのバージョンをkustomizeのreplacementsを使ってDRYに書いてみたいと思います。
サンプルをGitHubリポジトリにあげていますので、興味のある方はご自身の環境で試してみてください。

今回はElastic Stackバージョン管理用のConfigMapを新規作成することにしました。
Elastic Stackの各マニフェストファイルは、このConfigMapの値をバージョンとして使用します。

stack_version.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: stack-version
data:
  version: 8.5.3

kustomize.yamlは以下のようになります。

kustomize.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: default

resources:
- stack_version.yaml
- elasticsearch.yaml
- kibana.yaml
- elasticsearch_monitoring.yaml
- kibana_monitoring.yaml
- filebeat.yaml
- metricbeat.yaml
- packetbeat.yaml
- stack_monitoring.yaml
- logstash.yaml

replacements:
- source:
    kind: ConfigMap
    name: stack-version
    fieldPath: data.version
  targets:
  - select:
      kind: Elasticsearch
    fieldPaths:
    - spec.version
    options:
      create: true
  - select:
      kind: Kibana
    fieldPaths:
    - spec.version
    options:
      create: true
  - select:
      kind: Beat
    fieldPaths:
    - spec.version
    options:
      create: true
  - select:
      kind: Deployment
      name: logstash
    fieldPaths:
    - spec.template.spec.containers.[name=logstash].image
    options:
      delimiter: ":"
      index: 1

logstashのみ書き方が異なります。
これはlogstashがECKのCustom Resourceで定義されていないので、普通のDeploymentとして作成しているためです。

logstash.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: logstash
spec:
  replicas: 1
  selector:
    matchLabels:
      app: logstash
  strategy: {}
  template:
    metadata:
      labels:
        app: logstash
    spec:
      containers:
      - image: docker.elastic.co/logstash/logstash:VERSION
        name: logstash
        resources: {}
        env:
        - name: XPACK_MONITORING_ELASTICSEARCH_HOSTS
          value: "[http://elasticsearch-es-http:9200]"

タグのバージョン番号に関してはreplacementsで置き換える前提なので、"VERSION"という文字列を設定しています。
そしてreplacementsが行っているのは、logstashのimageを:(コロン)で区切って、2番目の要素の値("VERSION")をConfigMapのバージョン番号("8.5.3")に置き換えるという意味になります。

おわりに

従来はバージョンアップグレードするのにすべてのマニフェストファイルを編集する必要がありましたが、kustomizeのreplacementsを使えばバージョン管理用のConfigMapのみを編集するだけでOKになりました。
ちょっとしたことではありますが、エンジニアたるものDRYに行きたいものです。
それでは、この記事がどなたかのお役に立てれば幸いです。

Discussion