えんじにあメモ

試してみた技術とか、たまに家電ネタ

k8sでConfigMap/Secretsの更新時に環境変数へ反映するデプロイフローについて

はじめに

k8sの CofnigMap/Secrets を環境変数に設定し、アプリケーション(Deployment)から利用するパターンはよくあると思います

デプロイ時にDeployment、CofnigMap/Secretsをまとめてデプロイすると思いますが、CofnigMap/Secrets のみの更新時にはDeploymentに自動で反映されません

これはアプリケーションが起動時に CofnigMap/Secrets の値を環境変数に設定するため、起動後に CofnigMap/Secrets が更新されても反映されないためです

アプリケーションにも環境変数の更新を反映をさせたい場合は、Deployment と CofnigMap/Secrets を一緒に更新すればよいわけです

そこで、CofnigMap/Secrets を更新した際に、Deploymentにローリングアップデートをかけるデプロイフローを考えてみます
(CI/CDフローでも使えるよう、デプロイ時にエディタを起動して…などは行わないフローにします)

ローリングアップデートについて

前述したとおり、CofnigMap/Secrets のみの更新ではDeploymentが更新されないため反映が行われません

一般的なデプロイフローでは、各種リソースのyamlファイルを用意してデプロイを行うと思いますが、反映が行われない際のデプロイでは以下のようになっているはずです

$ kubectl apply -f .
configmap/my-configmap configured
deployment.apps/my-deployment unchanged

ComfigMapのみがconfiguredとなっていて、Deploymentのほうはunchangedになってますね

では、特にアプリケーション側に変更が入っていない場合にどのようにローリングアップデートをかければよいのか🤔

qiita.com

上記の記事で、Deploymentのローリングアップデートが起こる条件がすごく分かりやすく説明されています

  • .spec.templateの情報に変更があったとき
    • 変更はどの値でもよいが、アノテーション(.spec.template.metadata.annotations)が一番影響が少ないと思われる

ローリングアップデートを行う

実際にDeploymentのローリングアップデートを行うデプロイフローをいくつかあげてみます

1. デプロイ時に常にローリングアップデート

CofnigMap/Secrets の更新時を問わず、常にローリングアップデートをかける方法です

$ kubectl apply -f .
$ kubectl rollout restart <target_deployment>

applyで更新が入る場合、無駄にローリングアップデートが2度行われるので微妙ですね 🤔

2. アノテーションを動的に変更する

ローリングアップデートが起こる条件に当てはまるよう、.spec.template.metadata.annotationsをデプロイのたびに書き換えるフローです

利用するyamlは動的に書き換えられるよう、deployment.yaml.templateなどとしておきます

deployment.yaml.template(抜粋)

spec:
  template:
    metadata:
      annotations:
        update_time: ${UPDATE_TIME}

デプロイ時の書き換えはenvsubstを使ってみます

$ UPDATE_TIME=$(date +%s) envsubst < deployment.yaml.template > deployment.yaml
$ kubectl apply -f .

↑の例ではデプロイ時にannotations.update_timeに現在時刻を入れてデプロイしてます

spec.template.annotations.update_time の値がデプロイのたびに書き換わるのでローリングアップデートが行われます 🎉

CofnigMap/Secrets の更新時のみでよければ CofnigMap/Secrets のハッシュ値などを設定しても良いかもしれません

3. Kustomizeを使う

3つ目の方法はKustomizeの configMapGenerator / secretGenerator を使うやりかたです

configMapGenerator/secretGenerator で作成される CofnigMap/Secrets には自動的にハッシュ値のsuffixがつき、参照側も動的に書き換えてくれます

kustomization.yaml

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

resources:
- ./deployment.yaml

configMapGenerator:
- name: my-env
  literals:
  - TEST_ENV=hogehoge

deployment.yaml(抜粋)

spec:
  template:
    spec:
      containers:
      - envFrom:
        - configMapRef:
            name: my-env

上記のファイルを用意し、kubectl kustomize . を実行すると、

# 抜粋
apiVersion: v1
data:
  TEST_ENV: hogehoge
kind: ConfigMap
metadata:
  name: my-env-c7d2869f5d
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - envFrom:
        - configMapRef:
            name: my-env-c7d2869f5d

用意したmy-envに -c7d2869f5d というsuffixが自動的に付いています

実際にデプロイするには

$ kubectl apply -k .

を実行すれば、上記のyamlの内容がデプロイされます

また、configMapGeneratorの内容を変更し、

@@ -7,4 +7,4 @@
 configMapGenerator:
 - name: my-env
   literals:
-  - TEST_ENV=hogehoge
+  - TEST_ENV=fugafuga

再度 kubectl kustomize . を実行すると

apiVersion: v1
data:
  TEST_ENV: fugafuga
kind: ConfigMap
metadata:
  name: my-env-9c976df2hh
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - envFrom:
        - configMapRef:
            name: my-env-9c976df2hh

-c7d2869f5d だったsuffixが -9c976df2hh に変わっています

spec.template.spec.containers が更新されているため、デプロイ時にローリングアップデートが行われます 🎉

✋注意
Kustomizeでのsuffix付きのリソースはそれぞれ別のリソースとしてデプロイされるため、古くなった CofnigMap/Secrets 自体は残ることになります
--prune を利用したデプロイを行うようにしたり、利用しなくなった CofnigMap/Secrets の定期的な削除ができるようにしておきましょう

4. Helmを使う

Helmを使っている場合は、公式でローリングアップデートを行うためのTipsが公開されています

Helm | Chart Development Tips and Tricks

この方式は 2. アノテーションを動的に変更する にも記載した、アノテーションに CofnigMap/Secrets のハッシュ値を利用するパターンです

すごくシンプルな以下の構成を例とします

mychart
├── Chart.yaml
└── templates
    ├── configmap.yaml
    └── deployment.yaml

mychart/templates/configmap.yaml

apiVersion: v1
data:
  TEST_ENV: hogehoge
kind: ConfigMap
metadata:
  name: my-env

mychart/templates/deployment.yaml(抜粋)

spec:
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
    spec:
      containers:
      - envFrom:
        - configMapRef:
            name: my-env

annotations.checksum/config に configmap.yaml のsha256のハッシュ値を設定しています

この状態でドライランをすると

$ helm install my-chart ./mychart --dry-run
# 抜粋
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    metadata:
      annotations:
        checksum/config: 3b57b72332cd09882b34fd55bbedec6ccf3ccdf77f8a70fdc41ab518c8ac01d0

checksum/configハッシュ値が設定されています

デプロイ後に前回同様 TEST_ENV の値をhogehoge -> fugafugaに変更してdiffを見ると

$ helm diff upgrade my-chart ./mychart
# 抜粋
mychart, my-env, ConfigMap (v1) has changed:
  # Source: mychart/templates/configmap.yaml
  apiVersion: v1
  data:
-   TEST_ENV: hogehoge
+   TEST_ENV: fugafuga
...
mychart, mydeploy, Deployment (apps) has changed:
  spec:
    template:
        annotations:
-         checksum/config: 3b57b72332cd09882b34fd55bbedec6ccf3ccdf77f8a70fdc41ab518c8ac01d0
+         checksum/config: f62c80497f97a4b4877958249a1206a3b38bdcb5d5f498df1358ebd3a169c2b7

spec.template.annotations.checksum/config の値が書き換わっているため、デプロイ時にローリングアップデートが行われます 🎉

まとめ

CofnigMap/Secrets を環境変数に設定して利用する際の、Deploymentのローリングアップデートを行うデプロイフローをいくつかあげてみました

個人的には Kustomize や Helm を利用し、必要に応じて動的にローリングアップデートが行われるフローにするのがよいかと思っています ✋