Certified Kubernetes Application Developer (CKAD) #13 ConfigMap と Secret を深掘り: volume vs env、自動更新
#12 Observability で動いているアプリを覗き込むツールを身につけたなら、次はそのアプリが どんな設定で動くのか を扱う番です。データベースのアドレス・機能フラグ・API キー・パスワードをマニフェストやイメージの中に埋め込むと、環境ごとにイメージを新しくビルドする必要が生じ、機密情報がコードリポジトリに露出します。Kubernetes はこの設定値をコードの外に追い出すために ConfigMap (一般設定) と Secret (機密情報) を提供します。
CKAD で ConfigMap と Secret は最大のドメインである Application Environment, Configuration and Security (25%) の中心であり、単に作るだけにとどまらず env で注入するか volume で注入するか、そして 値が変わったときいつ反映されるか まで問います。基礎概念は K8s 実務トラック #6 で扱ったので、本記事は試験ですぐ使う実技パターンに集中します。
ConfigMap を作る #
ConfigMap はキー・値のペアで設定を収めるオブジェクトです。試験では手で YAML を書くより、kubectl create configmap の 3 つのソースオプションで即座に作る方が速いです。
# 1) リテラル: キー=値を直接指定
k create configmap app-config \
--from-literal=APP_COLOR=blue \
--from-literal=APP_MODE=prod
# 2) ファイル: ファイル 1 つがキー 1 つになる (キー=ファイル名、値=ファイルの内容)
k create configmap nginx-conf --from-file=nginx.conf
# 3) env ファイル: KEY=VALUE の行をまとめてキー・値に
k create configmap app-env --from-env-file=app.properties3 つのオプションは結果の構造が違います。--from-literal と --from-env-file は各行が個別のキーになりますが、--from-file=nginx.conf はファイル名がキーで、ファイル全体の内容が 1 つの値になります。この違いは、後で volume としてマウントするとき ファイルが何個に展開されるか を決めるので、正確に区別しなければなりません。
作った ConfigMap を k get configmap app-config -o yaml で確認すると、data の下に APP_COLOR: blue、APP_MODE: prod がキー・値として入っています。
Secret を作る #
Secret は ConfigMap とほぼ同じ構造ですが、パスワード・トークン・証明書のような 機密情報 を収めます。タイプに応じて 3 つの generator をよく使います。
# generic: 一般的なキー・値 (パスワード、API キーなど)
k create secret generic db-secret \
--from-literal=DB_USER=admin \
--from-literal=DB_PASS=s3cr3t
# docker-registry: プライベートレジストリの認証 (imagePullSecrets で使用)
k create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=ci \
--docker-password=token123
# tls: 証明書とキーのペア (Ingress TLS など)
k create secret tls web-tls \
--cert=tls.crt --key=tls.keybase64 は暗号化ではありません #
Secret を k get secret db-secret -o yaml で照会すると、値が DB_PASS: czNjcjN0 のように base64 でエンコードされて見えます。ここで必ず押さえるべき点は、base64 は暗号化ではなくエンコード だという事実です。echo czNjcjN0 | base64 -d だけで誰でも元の値を復元できます。base64 はバイナリデータをテキストとして運ぶための形式にすぎず、セキュリティを保証しません。実際の保存時に暗号化するには etcd encryption at rest や外部の機密管理ツールが必要です。試験では「base64 は暗号化ではない」という概念を問う問題がよく出ます。
YAML を手で書くとき base64 エンコードが面倒なら、data の代わりに stringData を使うと平文で書けて、Kubernetes が保存時にエンコードします。
注入方式 1: env で個別のキー #
ConfigMap や Secret の 特定のキー 1 つ を環境変数として注入するときは valueFrom を使います。ConfigMap は configMapKeyRef、Secret は secretKeyRef を使います。
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx
env:
- name: APP_COLOR # コンテナ内の環境変数名
valueFrom:
configMapKeyRef:
name: app-config # ConfigMap 名
key: APP_COLOR # その中のキー
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASS環境変数名 (name) とソースのキー (key) は別の名前にできます。コンテナ内では APP_COLOR として読みますが、ConfigMap のキー名は別物だという点を意識しておくと混乱しません。
注入方式 2: envFrom で全体を env に #
キーが複数あるとき 1 つずつ valueFrom を書くと長くなります。ConfigMap や Secret の すべてのキーをまとめて 環境変数に展開するには envFrom を使います。
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx
envFrom:
- configMapRef:
name: app-config # すべてのキーがそのまま env 名になる
- secretRef:
name: db-secretこの場合 ConfigMap のキー名がそのまま環境変数名になるので、キー名は APP_COLOR のように環境変数として使える形式でなければなりません。接頭辞を付けたければ項目に prefix: CONFIG_ を追加します。envFrom は configMapRef · secretRef、valueFrom は configMapKeyRef · secretKeyRef という名前の違いを正確に覚える必要があります。
注入方式 3: volume でファイルマウント #
env ではなく ファイルとして 設定を受け取る必要がある場合があります。nginx の設定ファイルや証明書のように、アプリケーションがパスから直接読む値がそうです。このときは ConfigMap や Secret を volume としてマウントします。
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: config-vol
mountPath: /etc/app # このディレクトリにキーごとにファイルが生成
volumes:
- name: config-vol
configMap:
name: app-configこうマウントすると /etc/app/ の下に ConfigMap のキーごとにファイルが 1 つずつできます。つまり /etc/app/APP_COLOR (内容 blue)、/etc/app/APP_MODE (内容 prod) が作られます。Secret も configMap の代わりに secret (フィールドは secretName) で同じ方式でマウントします。
volumes:
- name: secret-vol
secret:
secretName: db-secretsubPath: 単一ファイルだけマウント #
volume を丸ごとマウントすると、mountPath ディレクトリにあった既存のファイルが隠れます。たとえば /etc/nginx に設定ディレクトリをマウントすると、その中の他のファイルがすべて消えたように見えます。ファイル 1 つだけ を特定のパスに挿し込み、残りのディレクトリはそのまま残すには subPath を使います。
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/nginx.conf # ファイルパスまで指定
subPath: nginx.conf # このキー 1 つだけをこのパスにマウントsubPath でマウントすると ConfigMap の nginx.conf キーだけがそのファイルパスに入り、ディレクトリの他のファイルは保存されます。ただし subPath でマウントしたファイルは 自動更新されない という制約があり、この違いはすぐ次の節で扱います。
env vs volume: 自動更新 (試験の常連) #
同じ ConfigMap を env で注入したものと volume でマウントしたものは、値が変わったときの動作が違います。この違いが CKAD で繰り返し出題されます。
| 注入方式 | ConfigMap/Secret 修正時の反映 |
|---|---|
| env (valueFrom / envFrom) | 反映されない。Pod 再起動まで古い値を維持 |
| volume マウント | 一定時間 (kubelet sync) 後にファイル内容が自動更新 |
| volume + subPath | 自動更新されない (全体マウントと違う) |
env で注入した値は、コンテナが起動するときにプロセス環境へ一度埋め込まれてそれっきりです。そのため ConfigMap を修正しても、すでに動いているコンテナの環境変数は変わらず、新しい値を見るには Pod を作り直す必要があります (kubectl rollout restart で Deployment を再起動)。
一方で volume でマウントしたファイルは kubelet が定期的に同期するので、ConfigMap を修正すると一定時間後にマウントされたファイル内容が自動的に変わります。ただしアプリケーションがそのファイルを読み直さないと新しい値は適用されず (ファイルは更新されてもプロセスが再ロードしなければ意味がない)、先に見た subPath マウントはこの自動更新の対象から外れます。「ConfigMap を変えたのになぜ変わらないのか」という落とし穴は、ほぼ常に env 注入か subPath マウントです。
immutable と optional #
immutable: 不変の ConfigMap/Secret #
設定が頻繁に変わらないなら immutable: true を置いて修正そのものを防げます。不変と表示された ConfigMap や Secret はデータを変えられず (削除後の再生成のみ可能)、その代わり kubelet が変更を監視する必要がなくなり、クラスタの性能に利点があります。
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_MODE: prod
immutable: trueoptional: なくても起動するように #
デフォルトでは参照した ConfigMap やキーがないと Pod が起動できず待機します。ないかもしれない値なら optional: true を置いて、ないときにスキップさせます。
env:
- name: FEATURE_FLAG
valueFrom:
configMapKeyRef:
name: app-config
key: FEATURE_FLAG
optional: true # キーがなくても起動downwardAPI を一行で #
env や volume で注入する値は ConfigMap · Secret だけではありません。Pod 自身のメタデータ (名前・ネームスペース・ラベル・ノード名・リソース上限など) をコンテナに注入する downwardAPI もあります。外部設定ではなく Pod の情報をアプリが知る必要があるときに使います。詳細は #17 Volumes で projected volume と一緒に扱います。
試験ポイント #
- ConfigMap · Secret generator の 3 つのソースを区別します。
--from-literal(キー=値)、--from-file(ファイル名=キー)、--from-env-file(複数行をまとめて) です。 - Secret タイプは
generic·docker-registry·tlsの 3 つを手に馴染ませます。base64 は暗号化ではなくエンコード だという概念を問う問題がよく出ます。 - env 注入の 2 方式のフィールド名を正確に書きます。個別のキーは
valueFromのconfigMapKeyRef·secretKeyRef、全体はenvFromのconfigMapRef·secretRefです。 - volume マウントはキーごとにファイルが生成され、
subPathは単一ファイルだけを挿し込んでディレクトリの残りを保存します。 - 自動更新 が最大の常連です。env は Pod 再起動まで変わらず、volume は一定時間後に更新され、subPath マウントは更新されません。
- 素早い検証は
kubectl execで行います。k exec app -- envで環境変数を、k exec app -- cat /etc/app/APP_COLORでマウントされたファイルを確認します。
まとめ #
この記事で押さえたこと:
- 設定と機密情報をコードの外へ。ConfigMap (一般) と Secret (機密) に分け、Secret の base64 が暗号化ではないことを認識します。
- 作る。generator の 3 つのソース (
--from-literal·--from-file·--from-env-file) と Secret の 3 タイプ (generic·docker-registry·tls) です。 - 注入の 3 方式。個別キー env (
valueFrom)、全体 env (envFrom)、ファイルマウント (volume · subPath) です。 - 自動更新の違い。env は再起動まで固定、volume は自動更新、subPath は更新の対象外です。
- immutable · optional。不変の設定と、なくても起動する任意参照です。
次へ — ServiceAccount と RBAC #
設定と機密情報をコンテナの中に入れる方法を身につけました。次はそのアプリが クラスタ API に対して何ができるのか、つまり権限を扱う番です。
#14 ServiceAccount と RBAC (アプリ視点) では、Pod がどの ServiceAccount で動作するか、Role と RoleBinding でネームスペース内の権限を付与する方法、ClusterRole との違い、そして kubectl auth can-i で権限を検証する実技パターンを自分で作りながら整理します。