環境変数とボリュームで渡す ConfigMap の違い
v1.37 時点(2026年10月25日 確認)
目次
この記事では、機密ではない設定のデータをキーと値の組で持ち、Pod に渡すためのリソースである ConfigMap の仕組みを、図で説明します。ConfigMap の値をコンテナに渡す方法のうち、環境変数とボリュームの 2 つの違いがわかります。ConfigMap を書き換えたとき、ボリュームのファイルは後から更新されるのに、環境変数は自動では更新されず、新しい値にするには Pod を作り直す必要がある理由もわかります。後半では kind のクラスタで、1 つの ConfigMap を両方の方法で渡した Pod を動かし、ConfigMap を書き換えて違いを確かめる手順を示します。
Deployment と、ひな形を変えたときに Pod を入れ替える仕組みを扱った第12回の内容を前提にしています。まだの方は先にこちらをどうぞ。
Deployment とローリングアップデートの仕組みReplicaSet を持ち、Pod を宣言的に更新する Deployment の仕組みを図で説明します。ひな形を変えたときに新しい ReplicaSet へ Pod を少しずつ移すローリングアップデート、pod-template-hash の役割、リビジョンと切り戻しを押さえ、kind のクラスタでイメージを更新して rollout undo で戻す手順を紹介します。以下の図は sport という名前の ConfigMap の値を、1 つの Pod のコンテナに環境変数とボリュームの 2 つの方法で渡している様子を表しています。後半の「手元で試す」で作る構成です。
同じ ConfigMap の同じキーでも、渡し方によって値が届く時期が違います。公式ドキュメントによると、kubelet はコンテナを起動するときに ConfigMap のデータを使い、環境変数で渡した値は ConfigMap を書き換えても自動では更新されません。ボリュームで渡したファイルは、kubelet(第4回)が定期的に確かめ、最終的に新しい値に更新されます。ボリューム config-volume はコンテナの /etc/config にマウントしているので、ボリュームの中のファイル sport はコンテナからは /etc/config/sport として見えます。図ではボリュームとコンテナを結ぶ線を省き、このファイルをコンテナの箱の中に書いています。破線の「持つ」は第6回と第12回の図と同じ意味です。Deployment と Pod の間の ReplicaSet は省いています。
ConfigMap とは
ConfigMap は 機密ではないデータを、キーと値の組で保存するための API のオブジェクト です。公式ドキュメントによると、Pod は ConfigMap を、環境変数、コマンドラインの引数、ボリュームの中の設定ファイルとして使えます。ConfigMap を使うと、環境(開発用の手元のマシンや本番のクラウドなど、アプリケーションを動かす場所)ごとに違う設定をコンテナイメージから切り離せるので、アプリケーションを別の環境へ持ち運びやすくなります。
公式ドキュメントは ConfigMap は秘密を守ったり、暗号化したりするものではない と注意しています。保存したいデータが機密なら、ConfigMap ではなく Secret を使うか、データを守るための別の(サードパーティの)ツールを使うよう書かれています。Secret は第18回で扱います。
ConfigMap は大きなデータを持つためのものではありません。公式ドキュメントによると、ConfigMap に保存するデータは 1 MiB を超えられません。MiB はデータの大きさの単位で、一般に 1 MiB は 1,048,576 バイトです。これより大きな設定を持ちたいときは、ボリュームをマウントするか、別のデータベースやファイルのサービスを使うことを考えるよう書かれています。ここでのボリュームはこの回で扱う ConfigMap のボリュームではなく、データを置いておくためのボリュームのことだと考えられます。ボリュームの種類は第26回、データを長く残すためのストレージは第27回で扱います。
これまでの回で扱った Deployment などのオブジェクトには、望ましい状態を書く spec がありました。公式ドキュメントによると、ConfigMap には spec が無く、代わりに data と binaryData のフィールドがあります。ConfigMap はほかのオブジェクトが使う設定を保存しておくもので、それ自体が何かを動かすわけではないからだと考えられます。
なぜ ConfigMap が必要なのか
公式ドキュメントは設定のデータをアプリケーションのコードと分けて置くために ConfigMap を使う、と説明しています。挙げられている例は次のようなものです。アプリケーションを、開発のために自分のコンピューターでも、実際の通信を受けるためにクラウドでも動かすとします。コードは DATABASE_HOST という名前の環境変数を見るように書いておきます。自分のコンピューターではこの環境変数を localhost にし、クラウドではデータベースを公開している Service(第21回)を指すようにします。こうすると、クラウドで動いているのとまったく同じコンテナイメージを、必要なら手元で動かして調べられます。
環境変数は一般に、動いているプログラム(プロセス)に名前と値の組で渡す設定のことです。プログラムは名前で値を読み出します。Kubernetes では Pod の spec(Deployment ならひな形)の env に、名前と値を直接書いてコンテナに渡すこともできます。ただ、値を Pod のひな形に直接書くと、環境ごとに値を変えるたびに、Deployment のマニフェストも環境ごとに書き分けることになると考えられます。ConfigMap に値を置けば、コンテナイメージと Pod のひな形はそのままにして、ConfigMap だけを環境ごとに用意できると考えられます。公式ドキュメントによると、複数の Pod が同じ ConfigMap を参照することもできます。
参考:Define Environment Variables for a Container | Kubernetes、ConfigMaps | Kubernetes
ConfigMap の仕組み
ConfigMap のマニフェスト
以下は公式ドキュメントにある ConfigMap の例です。1 つの値だけを持つキーと、設定ファイルの断片のような値を持つキーが入っています。
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# property-like keys; each key maps to a simple value
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# file-like keys
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true前半の 2 つのキーは、1 つのキーに 1 つの短い値が対応する形です。後半の 2 つのキーは、値が複数行の設定ファイルのようになっています。| は一般に、続く複数行の文字列を改行も含めてそのまま書く YAML の書き方です。公式ドキュメントによると、ConfigMap は 1 行の値と、ファイルのような複数行の値を区別しません。Pod などがその値をどう使うかが大事です。
ConfigMap のフィールドを、v1.37 時点の公式ドキュメントと API リファレンスに沿って次の表にまとめます。
| フィールド | 内容 |
|---|---|
data | 設定のデータ。UTF-8 の文字列を入れる。UTF-8 でないバイトの並びを含む値は binaryData に入れる |
binaryData | バイナリのデータ(一般に、画像のような文字ではないデータ)。base64 でエンコードした文字列で書く。UTF-8 の範囲に無いバイトの並びも入れられる |
immutable | true にすると、data と binaryData を書き換えられなくなる(「書き換えられない ConfigMap」で説明) |
参考:ConfigMaps | Kubernetes、ConfigMap | Kubernetes API Reference
base64 はバイナリのデータを文字だけで表す方式の 1 つで、第18回で扱います。data と binaryData はどちらも書かなくてもかまいません。例の player_initial_lives の値を "3" と引用符で囲んでいるのは、data が文字列を入れるフィールドなので、数ではなく文字列として書くためだと考えられます。
名前とキーには、次の決まりがあります。
- ConfigMap の名前は DNS のサブドメイン名(英小文字、数字、
-、.で作り、英小文字か数字で始まって英小文字か数字で終わる、253 文字までの名前)として正しい形にする dataとbinaryDataのキーには英数字、-、_、.だけを使うdataのキーとbinaryDataのキーを重ねない
参考:ConfigMaps | Kubernetes、Object Names and IDs | Kubernetes
Pod に渡す 4 つの方法
公式ドキュメントは ConfigMap を使って Pod の中のコンテナを設定する方法を 4 つ挙げています。この回で扱うかどうかも添えて、次の表にまとめます。
| 公式ドキュメントが挙げる方法 | この回での扱い |
|---|---|
コンテナの command と args の中で使う | 扱わない。コマンドの引数は第19回 |
| コンテナの環境変数にする | 扱う |
| 読み取り専用のボリュームにファイルとして置き、アプリケーションに読ませる | 扱う |
| Pod の中で動くコードを書き、Kubernetes の API で ConfigMap を読む | 更新の反映のされ方だけ扱う |
公式ドキュメントによると、はじめの 3 つの方法では、kubelet が Pod のコンテナを起動するときに ConfigMap のデータを使います。4 つ目の方法では ConfigMap とそのデータを読むコードを自分で書く必要があります。そのかわり Kubernetes の API を直接使うので、ConfigMap が変わるたびに知らせを受けて、それに応じて動くようにアプリケーションを作れます。別の Namespace の ConfigMap も読めます。
はじめの 3 つの方法のように、Pod の spec から ConfigMap を参照するときには、次の決まりがあります。
- Pod と ConfigMap は同じ Namespace(第37回)に置く
- static Pod(第4回)の
specからは ConfigMap を参照できない - 参照する ConfigMap は Pod より先に作っておく(あとで説明する
optionalを付けた場合を除く)
参考:ConfigMaps | Kubernetes、Configure a Pod to Use a ConfigMap | Kubernetes
環境変数で渡す
ConfigMap の値を環境変数にするフィールドは、次の 2 つです。どちらも Pod の spec.containers[*] の下(Deployment ならひな形の spec.template.spec.containers[*] の下)に、コンテナごとに書きます。
| フィールド | 渡し方 |
|---|---|
env[*].valueFrom.configMapKeyRef | ConfigMap のキーを 1 つ選び、env[*].name に書いた名前の環境変数にする。環境変数の名前はキーと違ってもよい |
envFrom[*].configMapRef | ConfigMap のすべてのキーと値の組を、まとめて環境変数にする。envFrom[*].prefix を書くと、各環境変数の名前の前にその文字列を付ける |
参考:ConfigMaps | Kubernetes、Define Environment Variables for a Container | Kubernetes、Pod | Kubernetes API Reference
以下は公式ドキュメントにある、envFrom で ConfigMap myconfigmap のキーをすべて環境変数にする Pod の例です。
apiVersion: v1
kind: Pod
metadata:
name: env-configmap
spec:
containers:
- name: app
command: ["/bin/sh", "-c", "printenv"]
image: busybox:latest
envFrom:
- configMapRef:
name: myconfigmap同じページの myconfigmap には username と access_level の 2 つのキーがあります。printenv は一般に、環境変数を表示するコマンドです。公式ドキュメントではこの Pod のログ(kubectl logs pod/env-configmap)で、ConfigMap の 2 つのキーと値の組が環境変数になったことを確かめています。
configMapKeyRef と configMapRef で使うフィールドを、API リファレンスに沿って次の表にまとめます。
| フィールド | 内容 |
|---|---|
configMapKeyRef.name | 参照する ConfigMap の名前 |
configMapKeyRef.key | ConfigMap の data から選ぶキー。binaryData のキーは、今のところ環境変数には渡されない |
configMapKeyRef.optional | ConfigMap やそのキーが無くてもよいかどうか |
configMapRef.name | 参照する ConfigMap の名前 |
configMapRef.optional | ConfigMap が無くてもよいかどうか |
参考:Pod | Kubernetes API Reference、Pod | Kubernetes API Reference
公式ドキュメントによると、参照した ConfigMap が無く、optional を付けていなければ、Pod は起動しません。ConfigMap にあるはずのキーが無い場合も、optional を付けていなければ同じく起動しません。
環境変数の名前と、値がぶつかったときの決まりは、次のとおりです。
| 場面 | 決まり |
|---|---|
| 環境変数の名前に使える文字 | v1.37 時点の公式ドキュメントでは、= を除く印字可能な ASCII 文字 |
envFrom で名前として正しくないキーがあったとき | そのキーは飛ばされ、コンテナには渡されない。Pod は起動し、飛ばしたキーはイベントとして記録される |
同じキーが envFrom の複数の参照先にあるとき | 最後の参照先の値が使われる |
同じ名前が env と envFrom の両方にあるとき | env の値が使われる |
| コンテナイメージに同じ名前の環境変数があるとき | env か envFrom で設定した値が使われる |
参考:Define Environment Variables for a Container | Kubernetes、Configure a Pod to Use a ConfigMap | Kubernetes、Pod | Kubernetes API Reference
表の最後の行のとおり、コンテナイメージの中にも環境変数が指定されていることがあります。公式ドキュメントによると、env か envFrom で設定した環境変数は、イメージで指定された同じ名前の環境変数より優先されます。
参考:Define Environment Variables for a Container | Kubernetes
環境変数の名前に使える文字が広がったのは、機能ゲート(Kubernetes の機能ごとに、有効か無効かを切り替える設定)RelaxedEnvironmentVariableValidation によるもので、この機能は v1.34 から Stable です。ConfigMap を使う Pod の設定の手順のページには、envFrom で 1badkey や 2alsobad のような数字で始まるキーが飛ばされる例があります。v1.37 時点の規則(= を除く印字可能な ASCII 文字)では、これらも正しい名前にあたります。
参考:Feature Gates | Kubernetes、Configure a Pod to Use a ConfigMap | Kubernetes、Define Environment Variables for a Container | Kubernetes
ボリュームで渡す
第10回で見たとおり、ボリュームは Pod の中のコンテナから使えるディレクトリで、コンテナの中の決まったパスにマウントして使います。公式ドキュメントによると、ConfigMap をボリュームとして使う手順は次の 4 つです。
- ConfigMap を作るか、すでにあるものを使う
- Pod の
spec.volumes[*]にボリュームを足し、configMap.nameに ConfigMap の名前を書く - ConfigMap を使うコンテナごとに
spec.containers[*].volumeMounts[*]を足し、readOnly: trueと、ファイルを置くディレクトリ(mountPath)を書く - そのディレクトリのファイルを読むように、イメージかコマンドラインを変える
4 つ目は、アプリケーションがそのディレクトリのファイルを読むように、イメージを作り直すか、コンテナに渡すコマンドを変える、という意味だと考えられます。
公式ドキュメントによると、ConfigMap の data の各キーが、mountPath の下のファイルの名前になります。ファイルの中身はキーの値です。ボリュームの種類のページによると、ConfigMap はいつも読み取り専用でマウントされます。
参考:ConfigMaps | Kubernetes、Volumes | Kubernetes、Pod | Kubernetes API Reference
ボリュームの configMap に書けるフィールドを、v1.37 時点の API リファレンスに沿って次の表にまとめます。
| フィールド | 内容 |
|---|---|
name | ボリュームにする ConfigMap の名前 |
items | 書かなければ、data のすべてのキーがファイルになる。書くと、挙げたキーだけが、指定したパス(path)のファイルになる。ConfigMap に無いキーを挙げると、optional でない限りボリュームの準備が失敗する |
defaultMode | 作るファイルの権限(読み書きを許す範囲を表す数)。既定は 0644 |
optional | ConfigMap やそのキーが無くてもよいかどうか |
公式ドキュメントの game-demo の例では、ConfigMap を使う Pod の volumes が次のように書かれています。
volumes:
# You set volumes at the Pod level, then mount them into containers inside that Pod
- name: config
configMap:
# Provide the name of the ConfigMap you want to mount.
name: game-demo
# An array of keys from the ConfigMap to create as files
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"この Pod は items で game.properties と user-interface.properties の 2 つのキーを挙げ、コンテナ demo の /config にマウントしています。そのため、ConfigMap にキーが 4 つあっても、/config にできるファイルは /config/game.properties と /config/user-interface.properties の 2 つです。items を書かなければ、4 つのキーがすべて同じ名前のファイルになり、ファイルは 4 つになります。
コンテナの volumeMounts[*] のフィールドのうち、この回にかかわるものを、API リファレンスに沿って次の表にまとめます。どれも Pod の spec.containers[*].volumeMounts[*] の下に書きます。
| フィールド | 内容 | 既定値 |
|---|---|---|
name | マウントするボリュームの名前。spec.volumes[*] の name と合わせる。必須 | なし |
mountPath | コンテナの中で、ボリュームをマウントするパス。必須 | なし |
readOnly | true なら読み取り専用でマウントする | false |
subPath | ボリュームの根元ではなく、ボリュームの中のこのパスをマウントする | ""(ボリュームの根元) |
subPath を使うと、ボリュームの中の 1 つのファイルだけをマウントするような使い方ができると考えられます。ただし、subPath でマウントした ConfigMap には更新が届きません(「更新が反映されるタイミング」で説明します)。ボリュームの種類は第26回で扱います。
環境変数とボリュームの両方で渡すマニフェスト
以下は後半の「手元で試す」で使う ConfigMap のマニフェストです。キー sport の値が football の、キーが 1 つだけの ConfigMap です。名前と値は、公式のチュートリアル(Updating Configuration via a ConfigMap)の例に合わせています。
apiVersion: v1
kind: ConfigMap
metadata:
name: sport
data:
sport: football以下はこの ConfigMap の同じキー sport を、環境変数 SPORT と、ボリュームのファイル /etc/config/sport の 2 つの方法で受け取る Deployment のマニフェストです。チュートリアルには ConfigMap をボリュームで渡す Deployment の例と、環境変数で渡す Deployment の例が別々にあります。この回では違いを 1 つの Pod で見比べるため、2 つの例を 1 つにまとめました。Pod の数は 1 つにしています。ボリュームの readOnly: true は ConfigMap の概念のページの手順に沿って足しました。
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-demo
labels:
app: configmap-demo
spec:
replicas: 1
selector:
matchLabels:
app: configmap-demo
template:
metadata:
labels:
app: configmap-demo
spec:
containers:
- name: alpine
image: alpine:3
command:
- /bin/sh
- -c
- while true; do echo "$(date) env=$SPORT file=$(cat /etc/config/sport)"; sleep 10; done;
env:
- name: SPORT
valueFrom:
configMapKeyRef:
name: sport
key: sport
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: sport参考:Updating Configuration via a ConfigMap | Kubernetes、ConfigMaps | Kubernetes、Pod | Kubernetes API Reference
イメージの alpine:3 はチュートリアルの例と同じものです。Alpine は一般に、小さな Linux の環境を収めたイメージで、チュートリアルの例のように sh、date、cat などのコマンドを使えます。command の 3 行で、シェル(/bin/sh)に 1 行のコマンドを渡しています(第10回)。シェルでは一般に、$SPORT は環境変数 SPORT の値に、$(コマンド) はそのコマンドの出力に置き換わります。このコマンドは 10 秒ごとに次の 3 つを 1 行にまとめて表示し続けます。
$(date):今の日時env=$SPORT:環境変数SPORTの値file=$(cat /etc/config/sport):ボリュームのファイル/etc/config/sportの中身
表示はコンテナのログになるので、kubectl logs(第10回)で読めます。ファイルは 10 秒ごとに cat で読み直すので、ファイルの中身が変われば、次の行から新しい値が表示されるはずです。
なお API リファレンスによると、command の中の $(変数の名前) の形は、シェルより先に Kubernetes が環境変数の値に置き換え、置き換えられなければ書いたまま残します。date などの名前の環境変数は無いので、$(date) などはそのままシェルに渡ると考えられます。チュートリアルの例も同じ書き方です。
参考:Pod | Kubernetes API Reference、Updating Configuration via a ConfigMap | Kubernetes
更新が反映されるタイミング
ConfigMap を書き換えたとき、すでに動いている Pod に新しい値が届くかどうかは、渡し方によって違います。v1.37 時点の公式ドキュメントの説明を次の表にまとめます。
| 渡し方 | ConfigMap を書き換えたとき |
|---|---|
環境変数(env、envFrom) | 自動では更新されない。Pod の再起動(pod restart)が必要 |
ボリューム(subPath を使わない) | ボリュームの中のファイルも、最終的に(eventually)更新される |
ボリューム(subPath でマウント) | 更新が届かない |
| Kubernetes の API で読む | ConfigMap が変わるたびに知らせを受けて、それに応じて動くようにアプリケーションを作れる |
表の「Pod の再起動」は公式ドキュメントの「require a pod restart」の訳です。チュートリアルは、新しい値にするには今の Pod を Kubernetes に置き換えさせる必要があると説明しています。そのため、ここでの再起動は Pod を新しい Pod に置き換えることと読めます。同じ Pod の中でコンテナが再起動したとき(第9回)に新しい値になるかは、公式ドキュメントには書かれていません。あとで使う kubectl rollout restart も、名前は restart ですが、Pod を作り直します。
参考:ConfigMaps | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes
環境変数が変わらないのは、はじめに見たとおり、kubelet がコンテナを起動するときに ConfigMap のデータを使うからだと考えられます。チュートリアルも、Pod の中で動くプロセスの環境変数は、もとのデータが変わっても更新されない、と説明しています。更新したいなら、Kubernetes に今の Pod を置き換えさせる必要があり、新しい Pod が新しい値で動きます。なお API リファレンスによると、コンテナの env と envFrom は Pod を作ったあとで書き換えられません(Cannot be updated)。そのため、動いている Pod の env を書き換えて新しい値を渡すこともできません。
参考:ConfigMaps | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes、Pod | Kubernetes API Reference
ボリュームのファイルは kubelet が更新します。公式ドキュメントによると、kubelet は定期的な同期のたびに、マウントした ConfigMap が新しいかどうかを確かめます。ただし、ConfigMap の今の値は、kubelet が手元に持つキャッシュ(一度取ってきたデータを手元に置いておく仕組み)から取ります。そのため、ConfigMap を書き換えてから、新しい値(や足したキー)がファイルとして Pod に届くまでの遅れは、最大で「kubelet の同期の間隔」と「キャッシュに伝わるまでの遅れ」を足した長さになり得ます。
この 2 つの長さにかかわる設定は kubelet の設定(KubeletConfiguration)にあります。概念のページの「定期的な同期」が kubelet の設定のどの項目を指すかは、直接は書かれていません。kubelet の設定のリファレンスの syncFrequency は、動いているコンテナと設定を同期する(synchronizing running containers and config)間隔の最大で、これがこの同期の間隔にあたると考えられます。v1.37 時点の公式ドキュメントと kubelet の設定のリファレンスに沿って、次の表にまとめます。
| 設定 | 内容 | 既定値 |
|---|---|---|
syncFrequency | 動いているコンテナと設定を同期する間隔の最大 | 1m(1 分) |
configMapAndSecretChangeDetectionStrategy | ConfigMap と Secret の変化を見つける方式。Get(毎回 API サーバーから直接取る)、Cache(TTL のキャッシュを使う)、Watch(watch で変化を見る)のどれか | Watch |
参考:Kubelet Configuration (v1beta1) | Kubernetes、ConfigMaps | Kubernetes
TTL は一般に、キャッシュを使ってよい期限のことです。watch は第3回の --watch と同じように、API から変化を受け取り続ける仕組みだと考えられます。公式ドキュメントによると、キャッシュに伝わるまでの遅れは方式によって決まり、Watch なら watch で伝わるまでの遅れ、Cache ならキャッシュの TTL、Get なら 0 です。既定の Watch のときの遅れが何秒になるかは、公式ドキュメントには書かれていません。公式ドキュメントはボリュームのファイルが何秒で更新されるかを約束していません。同期の間隔が syncFrequency(既定 1 分)にあたるなら、足し算の片方だけで 1 分あるので、遅れの最大は 1 分以上になり得ると考えられます。
参考:ConfigMaps | Kubernetes、Kubelet Configuration (v1beta1) | Kubernetes
ファイルが更新されても、アプリケーションが新しい値を使うとは限りません。チュートリアルによると、アプリケーションが変化に気付くのは、ファイルを繰り返し読み直すか、ファイルの更新を見張るように作られている場合だけです。起動したときに一度だけ設定を読むアプリケーションは、変化に気付きません。この回のマニフェストは 10 秒ごとに cat でファイルを読み直すので、変化に気付けます。
以下の図は ConfigMap の値を football から cricket に書き換えたあとの Pod の様子を、上下の 2 段で表しています。上の段は、Pod はそのままで、kubelet の同期のあとの様子です。下の段は Pod を作り直したあとの様子です。
上の段では、同じ Pod の中で、ファイルは新しい値、環境変数は古い値になっています。古い値は異常ではなく、環境変数は自動では更新されないという公式ドキュメントの説明どおりの状態です。下の段では、新しい Pod のコンテナが起動するときに新しい値を使うので、どちらも新しい値になっています。
Pod を作り直すには、Deployment の場合は kubectl rollout restart を使えます。第12回で見たとおり、公式ドキュメントによると、Deployment の展開が始まるのは Pod のひな形(spec.template)が変わったときだけです。ConfigMap を書き換えても Deployment のひな形は変わらないので、展開は始まりません。kubectl rollout restart は、Deployment の Pod のひな形のメタデータに kubectl.kubernetes.io/restartedAt というアノテーションを書き込みます。値は再起動を指示した日時です。ひな形が変わるので、チュートリアルの説明のとおり、Deployment は新しい ReplicaSet を作り、古い Pod は終了させられて新しい Pod が作られます。
参考:kubectl rollout restart | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes、Deployments | Kubernetes
チュートリアルは Deployment の Pod の数を増やした(スケールした)ときのように、展開以外の理由で Pod が作られると、新しい Pod も最新の値を使う、とも書いています。展開を始めないままだと、古い値の環境変数と新しい値の環境変数で動く Pod が混ざることがあります。
書き換えられない ConfigMap
ConfigMap の immutable を true にすると、その ConfigMap は書き換えられなくなります。この機能は v1.21 から stable です。公式ドキュメントによると、ConfigMap を大量に使うクラスタ(ConfigMap と Pod のマウントの組み合わせが少なくとも数万あるクラスタ)では、データの書き換えを防ぐことに次の 2 つの利点があります。
- アプリケーションの障害(停止)につながりかねない、うっかりした(または望まない)書き換えを防げる
- 書き換えられない ConfigMap への watch を閉じるので、kube-apiserver の負荷が大きく減り、クラスタの性能が上がる
書き換えられない ConfigMap は、次のように immutable: true を書いて作ります。
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
immutable: true公式ドキュメントによると、一度 immutable にした ConfigMap は、元に戻せません。data と binaryData の中身も変えられず、できるのは ConfigMap を消して作り直すことだけです。ConfigMap を消しても、すでにある Pod は、消した ConfigMap を参照していたボリュームをマウントしたままになります。そのため、それらの Pod も作り直すよう勧められています。API リファレンスによると、immutable が true でも、オブジェクトのメタデータ(ラベルなど)は変えられます。
参考:ConfigMaps | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes、ConfigMap | Kubernetes API Reference
チュートリアルによると、書き換えられない ConfigMap は特に、一定で、時間がたっても変わらないと見込まれる設定に使われます。変える必要が出たときは、別の名前で新しい ConfigMap を作り、新しい名前を参照する Pod に切り替えるなどの方法を前もって考えておくべき(should plan to)だ、と書いています。チュートリアルの例では新しい名前の書き換えられない ConfigMap を作り、Deployment のひな形のボリュームが新しい ConfigMap を参照するように書き換えています。ひな形が変わるので、展開が始まります。すべての Deployment が新しい ConfigMap に移ったら、古い ConfigMap は消すよう勧めています。
手元で試す
ここまでの内容を手元の kind のクラスタで試す手順を示します。ConfigMap と、その値を環境変数とボリュームの両方で受け取る Deployment を作り、コンテナのログで 2 つの値を見比べます。続けて ConfigMap の値を書き換え、ボリュームのファイルだけが後から新しい値になり、環境変数は古い値のままになることを確かめます。最後に kubectl rollout restart で Pod を作り直し、環境変数も新しい値になることを見ます。コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| kind | v0.33.0 |
| Kubernetes | v1.37.0(kind v0.33.0 の既定のノードイメージ) |
| ノード構成 | シングルノード(クラスタ名 kind):コントロールプレーン 1 |
| 必要なもの | Docker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)2 つ |
参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes
kind でクラスタを作る手順は、以下の記事で解説しています。この回ではシングルノードのクラスタを使います。この回で見る ConfigMap の値の届き方は、ノードが 1 つでも確かめられるからです。
kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。シングルノードのクラスタを用意する
まず、前の回のクラスタが残っていないかを確かめます。
$ kind get clusters
kind が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。前の回の multi-node が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name multi-node で消してから進めてください。
$ kind create cluster
$ kind get clusters
kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。
続けて、ノードを一覧します。
$ kubectl get nodes --context kind-kind
ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。
ConfigMap と Deployment を作る
「環境変数とボリュームの両方で渡すマニフェスト」の 2 つのマニフェストを、コマンドを実行するディレクトリに保存します。ConfigMap は sport-configmap.yaml、Deployment は configmap-demo.yaml という名前にします。sport-configmap.yaml はあとでエディタで書き換えます。
公式のチュートリアルでは ConfigMap を kubectl create configmap で作り、kubectl edit で書き換えています。この回では例から変えて、マニフェストを kubectl apply で渡し、書き換えるときもマニフェストを直して kubectl apply します。第7回で見たとおり、公式ドキュメントは 1 つのオブジェクトを 1 つの管理の方法だけで扱うよう警告しているからです。
参考:Updating Configuration via a ConfigMap | Kubernetes、Kubernetes Object Management | Kubernetes
先に ConfigMap を作ります。公式ドキュメントによると、Pod が参照する ConfigMap は、Pod より先に作っておく必要があります。optional を付けずに無い ConfigMap を参照すると、Pod は起動しません。
$ kubectl apply -f sport-configmap.yaml --context kind-kind
configmap/sport が作られたことを知らせる 1 行が出るはずです。ConfigMap の data の sport の値を取り出して確かめます。第5回で見た -o jsonpath を使います。
$ kubectl get configmap sport -o jsonpath='{.data.sport}{"\n"}' --context kind-kind
参考:JSONPath Support | Kubernetes、ConfigMap | Kubernetes API Reference
football と表示されるはずです。
続けて Deployment を作り、展開が終わるまで見ます。
$ kubectl apply -f configmap-demo.yaml --context kind-kind
$ kubectl rollout status deployment/configmap-demo --context kind-kind
第12回と同じく、最後に successfully rolled out で終わる行が出て、コマンドが終われば、Pod が動いています。クラスタを作ったばかりなら、alpine:3 のイメージの取得に時間がかかるので、しばらく止まって見えることがあります(第9回)。
環境変数とファイルの値をログで見る
ここからは端末を 2 つ使います。第11回・第13回・第14回・第15回と同じく、1 つ目の端末で変化を追い続け、2 つ目の端末でマニフェストを渡したり、値を確かめたりします。この回で 1 つ目の端末が追うのは、Pod のコンテナのログです。2 つ目の端末を開いたら、cd でマニフェストを保存したディレクトリに移動しておきます。
1 つ目の端末で、Deployment の Pod のログを追います。deployment/configmap-demo は「種類/名前」の形で、configmap-demo という Deployment を指します。--follow を付けると、新しい行が出るたびに表示し続けます(第10回の -f と同じです)。
$ kubectl logs deployment/configmap-demo --follow --context kind-kind
参考:kubectl logs | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes
10 秒ごとに、日時のあとに env=football file=football が続く行が書き足されていくはずです。環境変数とファイルの両方に、ConfigMap の値 football が届いています。チュートリアルの例では 1 行目に Found 3 pods, using pod/... という、どの Pod のログかを知らせる行が出ています。手元でこのような行が出ても問題ありません。
ConfigMap を書き換える
2 つ目の端末で、sport-configmap.yaml をテキストエディタで開き、sport: の値を football から cricket に書き換えて保存します。チュートリアルと同じ書き換えです。書き換えたあとのファイルは次のとおりです。
apiVersion: v1
kind: ConfigMap
metadata:
name: sport
data:
sport: cricket書き換えたマニフェストを渡し、ConfigMap の値をもう一度取り出します。
$ kubectl apply -f sport-configmap.yaml --context kind-kind
$ kubectl get configmap sport -o jsonpath='{.data.sport}{"\n"}' --context kind-kind
kubectl apply のあとには configured で終わる 1 行が出て、ConfigMap の値は cricket と表示されるはずです。クラスタの中の ConfigMap は、もう新しい値になっています。
1 つ目の端末を見ると、しばらくは env=football file=football の行が続くはずです。やがて、file= の後ろだけが cricket に変わり、env=football file=cricket の行が書き足されていくはずです。env= の後ろは football のまま変わらないはずです。
チュートリアルでボリュームで渡した例を追ったときのログは、次のとおりです。例のコンテナはファイルの中身を My preferred sport is の後ろに表示しています。
例の My preferred sport is の後ろの値が、手元のログの file= の後ろの値にあたります。例の 4 行目で football が cricket に変わっているのが、手元で file=football が file=cricket に変わることにあたります。例のコンテナは環境変数を表示しないので、手元の env= にあたる部分は例にはありません。日時も手元の値になります。
チュートリアルは追い始めて数秒後(After few seconds)にこのように変わる例を示しています。チュートリアルによると、ボリュームでマウントしている場合、動いている Pod は ConfigMap の更新をほぼすぐに(almost immediately)受け取り、ボリュームで渡した ConfigMap の変更は、次の kubelet の同期のあとに使えるようになります(available seamlessly after the subsequent kubelet sync)。一方、概念のページによると、ファイルに届くまでの遅れは最大で kubelet の同期の間隔とキャッシュに伝わるまでの遅れを足した長さになり得ます(「更新が反映されるタイミング」)。すぐに変わらなくても、しばらく待ってみてください。待っても file= の後ろが変わらないときは、次の 2 つを確かめます。
- 2 つ目の端末で、さきほどの
kubectl get configmap sport -o jsonpath=...を実行し、ConfigMap の値がcricketになっているか configmap-demo.yamlのvolumeMountsにsubPathを書いていないか(subPathでマウントした ConfigMap には更新が届かない)
参考:ConfigMaps | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes
env= の後ろが変わらないのは、kubelet がコンテナを起動するときに ConfigMap のデータを使い、環境変数は ConfigMap を書き換えても自動では更新されないからです。チュートリアルで環境変数で渡した例でも、ConfigMap を書き換えたあとのログは変わらないままです。
参考:ConfigMaps | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes
Pod を作り直して環境変数に反映する
env=football file=cricket の行が出たのを確かめたら、2 つ目の端末で、Deployment の Pod を作り直します。チュートリアルと同じく kubectl rollout restart を使い、続けて展開が終わるまで見ます。
$ kubectl rollout restart deployment/configmap-demo --context kind-kind
$ kubectl rollout status deployment/configmap-demo --context kind-kind
参考:kubectl rollout restart | Kubernetes、Updating Configuration via a ConfigMap | Kubernetes
最後に successfully rolled out で終わる行が出るはずです。kubectl rollout restart がアノテーションを書き込むのは、クラスタの中の Deployment のひな形だけです。手元の configmap-demo.yaml は書き換わりません。
1 つ目の端末では、ログを追っていた Pod が終了させられるので、kubectl logs の表示が止まるか、コマンドが終わると考えられます。表示が止まっただけなら、Ctrl+C で止めてから次へ進みます。
2 つ目の端末で、Pod を一覧します。
$ kubectl get pods -l app=configmap-demo --context kind-kind
configmap-demo- で始まる名前の Pod が、ログを追っていた Pod とは違う名前で 1 つ表示され、STATUS 列が Running になっているはずです。名前が変わったのは、新しい ReplicaSet が新しい Pod を作ったからです(第12回)。古い Pod が Terminating で残っていたら、消えきるまで待ってからもう一度実行します。チュートリアルも、古い Pod が終了しきるのを待ってから次の手順に進むよう注意しています。
参考:Updating Configuration via a ConfigMap | Kubernetes、Deployments | Kubernetes
続けて、Deployment のひな形のアノテーションを表示します。
$ kubectl get deployment configmap-demo -o jsonpath='{.spec.template.metadata.annotations}{"\n"}' --context kind-kind
kubectl.kubernetes.io/restartedAt のキーと、kubectl rollout restart を実行した日時の値が表示されるはずです。ひな形にこのアノテーションが加わったので、展開が始まったことになります。
1 つ目の端末で、もう一度ログを追います。
$ kubectl logs deployment/configmap-demo --follow --context kind-kind
今度は env=cricket file=cricket の行が書き足されていくはずです。新しい Pod のコンテナは起動するときに ConfigMap の新しい値を使ったので、環境変数も cricket になっています。チュートリアルの環境変数の例(ConfigMap fruits の値を apples から mangoes に書き換える例)でも、作り直したあとの Pod のログでは、環境変数の値が新しい値の mangoes になっています。
参考:Updating Configuration via a ConfigMap | Kubernetes、ConfigMaps | Kubernetes
片付け
1 つ目の端末で Ctrl+C を押して、ログを追うのを止めます。
2 つ目の端末で、作った Deployment と ConfigMap を、マニフェストを指定して削除します。第6回で見たとおり、Deployment を消すと、既定ではその ReplicaSet と Pod も消えます。
$ kubectl delete -f configmap-demo.yaml --context kind-kind
$ kubectl delete -f sport-configmap.yaml --context kind-kind
参考:kubectl delete | Kubernetes、Garbage Collection | Kubernetes
少し待ってから、残っていないかを確かめます。
$ kubectl get deployments,pods --context kind-kind
$ kubectl get configmap sport --context kind-kind
Pod が Terminating で表示されていたら、少し待ってからもう一度実行します。1 つ目のコマンドで configmap-demo の Deployment と Pod が表示されず、2 つ目のコマンドで sport が見つからないことを知らせるエラーの 1 行が出れば、削除できています。
最後に、クラスタを削除し、消えたことを確かめます。
$ kind delete cluster
$ kind get clusters
kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。
注意点
ConfigMap でつまずきやすい点と、後から変えられない設定を、次の表にまとめます。
| 項目 | 内容 |
|---|---|
| ConfigMap は機密を守らない | 秘密を守ることも暗号化もしない。機密のデータには Secret(第18回)か、データを守る別のツールを使う |
| 大きさは 1 MiB まで | ConfigMap に保存するデータは 1 MiB を超えられない |
| 環境変数は自動では更新されない | ConfigMap を書き換えても、環境変数は変わらない。Pod の作り直し(新しい Pod への置き換え)が必要 |
| ボリュームのファイルの更新には遅れがある | 最終的に更新されるが、最大で kubelet の同期の間隔とキャッシュに伝わるまでの遅れを足した長さになり得る |
subPath でマウントすると更新されない | subPath でマウントした ConfigMap には、更新が届かない |
| 参照する ConfigMap を先に作る | optional を付けずに無い ConfigMap やキーを参照すると、Pod は起動しない |
| 同じ Namespace の ConfigMap だけを参照できる | Pod が参照できるのは、Pod と同じ Namespace の ConfigMap だけ。static Pod からは参照できない |
immutable は元に戻せない | 一度 true にすると戻せず、data と binaryData も変えられない。変えるには消して作り直す |
参考:ConfigMaps | Kubernetes、Configure a Pod to Use a ConfigMap | Kubernetes、Volumes | Kubernetes
ConfigMap を書き換えて新しい値を使わせたいときは、渡し方ごとに、何をすれば新しい値が届くかを決めておく必要があると考えられます。環境変数で渡しているときは、Deployment なら kubectl rollout restart などで Pod を作り直します。ボリュームで渡しているなら、ファイルは kubelet が更新しますが、アプリケーションがファイルを読み直すように作られていなければ、新しい値は使われません。
関連記事
機密のデータを扱う Secret と、Secret の値が base64 でエンコードされているだけで暗号化ではないことについては、以下の記事で解説しています。
Secret の仕組みと安全な扱い方パスワードやトークンなどの機密のデータを Pod に渡す Secret の仕組みを図で説明します。ConfigMap との違い、base64 はエンコードであって暗号化ではないこと、etcd での保存時の暗号化や RBAC による読み取りの制限などの安全な扱い方を押さえ、kind のクラスタで Secret の値をデコードして確かめる手順を紹介します。コンテナに渡すコマンドの引数と環境変数、Pod 自身の情報をコンテナに渡す Downward API については、以下の記事で解説しています。
Downward API で Pod の情報を読むPod の名前や IP アドレス、コンテナの CPU とメモリの値を、コンテナの中のプログラムに渡す Downward API の仕組みを図で説明します。コンテナに渡すコマンドと引数、環境変数の書き方を押さえ、環境変数と downwardAPI ボリュームで渡せる情報の違いを、kind のクラスタで確かめる手順を紹介します。emptyDir や hostPath などのボリュームの種類については、以下の記事で解説しています。