zaki work log

作業ログやら生活ログやらなんやら

[Kubernetes] ConfigMapのおさらい (作成・環境変数・ボリュームマウント)

Kubernetes基本機能の一つであるConfigMapのおさらい。

ConfigMapを作る

key=valueの形式をCLIから

--from-literalでkey=valueを指定する。
複数設定可能。

$ kubectl create configmap key-value-sample -n configmap-example \
  --from-literal=SAMPLE_HOST='10.0.0.2' \
  --from-literal=SMAPLE_PORT='25'

--dry-run -o yamlで実行すると以下のConfigMapが生成されることがわかる。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap key-value-sample -n configmap-example \
>   --from-literal=SAMPLE_HOST='10.0.0.2' \
>   --from-literal=SMAPLE_PORT='25' \
>   --dry-run -o yaml
W1227 10:41:53.955673   60645 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  SAMPLE_HOST: 10.0.0.2
  SMAPLE_PORT: "25"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: key-value-sample
  namespace: configmap-example

key=value形式のファイルから

HOST=10.0.0.2
PORT=25

こういうkey=valueという(デリミタが=になっている)形式になっているファイルであれば、--from-env-fileで指定してConfigMapリソースを作成することができる。

上記の内容のsample.iniファイルがあるとして、以下を実行する。

$ kubectl create configmap key-value-sample -n configmap-example \
  --from-env-file=sample.ini

--dry-runすると以下の通り。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap sample --from-env-file=sample.ini --dry-run -o yaml
W1227 16:17:56.885645  115674 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  HOST: 10.0.0.2
  PORT: "25"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: sample

以下の形式のファイルだと無効

KEY: VALUE

また、iniファイルのようにセクション名が入っていても無効。

[smtp]
HOST=10.0.0.2
PORT=25

#によるコメントは無視されるので期待通りに処理される。(;によるコメントは無効)

#[config]
HOST=10.0.0.2
PORT=25

ファイルの埋め込み

--from-fileでファイルを指定する。
複数指定可能。

$ kubectl create configmap file-sample -n configmap-example \
  --from-file=/etc/hosts \
  --from-file=/etc/resolv.conf

--dry-run -o yamlを付与して実行すると以下の通り。
ConfigMap内におけるファイル名は作成時に指定するファイル名がそのまま使用される。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap file-sample -n configmap-example \
>   --from-file=/etc/hosts \
>   --from-file=/etc/resolv.conf \
>   --dry-run -o yaml
W1227 10:50:53.399027   63085 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  hosts: |
    127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
  resolv.conf: |
    # Generated by NetworkManager
    nameserver 192.168.0.19
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: file-sample
  namespace: configmap-example

指定ディレクトリ直下の全ファイルを指定

ファイル埋め込みは--from-fileを複数指定で複数ファイル指定が可能だが、--from-fileディレクトリを指定すればそのディレクトリ直下の全ファイルがConfigMapに含めることができる。

$ kubectl create configmap dir-sample -n configmap-example \
  --from-file=${HOME}/work/sample

以下のようなファイルがある状態で、

[zaki@cloud-dev configmap (master)]$ find ~/work/sample
/home/zaki/work/sample
/home/zaki/work/sample/sample1.txt
/home/zaki/work/sample/sample2.txt
/home/zaki/work/sample/.dot-file.txt
/home/zaki/work/sample/subdir
/home/zaki/work/sample/subdir/example.txt

--dry-run -o yamlを付与して実行すると以下の通り。

[zaki@cloud-dev configmap (master)]$ kubectl create configmap dir-sample -n configmap-example \
>   --from-file=${HOME}/work/sample \
>   --dry-run -o yaml
W1227 11:05:36.681042   67278 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
data:
  .dot-file.txt: |
    this is dot file.
  sample1.txt: |
    nya-n
    wanwan
  sample2.txt: |
    this file is sample file.
    curry tabetai!
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: dir-sample
  namespace: configmap-example

指定ディレクトリ直下のドットファイルは含まれるが、サブディレクトリは無視される。

ハードタブを含むファイルの場合

YAML内にハードタブは含むことができないため、KYE-VALUE形式と同じように1行で記述され改行やハードタブはエスケープ文字が使われる。

以下はsample2.txtの各行の先頭をハードタブ(\tでインデントした場合)

apiVersion: v1
data:
  sample2.txt: "\tthis file is sample file.\n\tcurry tabetai!\n"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: dir-sample
  namespace: configmap-example

バイナリファイルの場合

バイナリ使えるのはSecretだけかと思ってたけど、ConfigMapも「UTF-8以外のデータを含む場合はバイナリとみなされBase64エンコード」されてリソースが作成される。

[zaki@cloud-dev configmap (master)]$ ls -l ~/src/usansible/src/icons/usansible.png 
-rw-rw-r--. 1 zaki zaki 5445  9月 26 00:21 /home/zaki/src/usansible/src/icons/usansible.png
[zaki@cloud-dev configmap (master)]$ file ~/src/usansible/src/icons/usansible.png 
/home/zaki/src/usansible/src/icons/usansible.png: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced

上記のデータでConfigMapを作ると、

[zaki@cloud-dev configmap (master)]$ kubectl create configmap binary-sample -n configmap-example \>   --from-file=${HOME}/src/usansible/src/icons/usansible.png --dry-run -o yaml
W1227 11:16:56.211264   70269 helpers.go:553] --dry-run is deprecated and can be replaced with --dry-run=client.
apiVersion: v1
binaryData:
  usansible.png: iVBORw0KGgoAAAANSUhEUgAAAI ...
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: binary-sample
  namespace: configmap-example

この通り、PNG画像ファイルがBase64エンコードされてConfigMapが作成される。
マウントすればデコードされて元のデータを使用できる。

YAMLマニフェストで作成

0から作るよりは上記の出力を参考に--dry-run -o yamlの結果をファイルリダイレクトすると簡単に作れる。

注意点として、dataに指定できるのは文字列のみのため、ポート番号のように数値(m/[0-9]+/)のみの指定になる場合はクォートで囲まないとYAML的に数値とみなされてエラーとなる。

ConfigMapをPodから参照する

環境変数として参照

apiVersion: v1
data:
  SAMPLE_EDITOR: emacs
  SAMPLE_SERVER: 10.0.0.2
  SAMPLE_PORT: '8080'
  SAMPLE_USERNAME: curry
kind: ConfigMap
metadata:
  name: configmap-key-value

こんなConfigMapがあるとして、、

ConfigMap内データ全てを環境変数として使用する

key=valueとして作成したConfigMapを環境変数に使用するには、対象のcontainers定義で以下のようにenvFrom:を指定すれば、対象ConfigMapの全データが環境変数としてコンテナに設定される。

configmap-key-valueという名前のConfigMapリソースを参照する場合は以下の通り。

      containers:
      - image: httpd
        name: httpd
        envFrom:
        - configMapRef:
            name: configmap-key-value

ConfigMapの指定キーのみを環境変数で使用する

全てのキーでなく、指定キーのみを参照したい場合は、env.[].valueFromを使用する。

configmap-key-valueというConfigMap内にあるSAMPLE_EDITORのみを使用する場合は以下の通り。
以下では、更に環境変数のキー名をコンテナ上ではPICKUPED_CONFIGMAP_KEYVALとして使うような内容になっている。(env.[].nameは必須キー)
(configMapKeyRef.keyでConfigMapの指定キーを取り出して、env.[].nameの名前で環境変数名としている、ということ)

      containers:
      - image: httpd
        name: httpd
        env:
        - name: PICKUPED_CONFIGMAP_KEYVAL
          valueFrom:
            configMapKeyRef:
              name: configmap-key-value
              key: SAMPLE_EDITOR

全体はこんな感じ

github.com

(参考) ConfigMapを使わない環境変数定義

Deploymentの場合はこんな感じ。

    spec:
      containers:
      - image: httpd
        name: httpd
        env:
        - name: REMOTE_SERVER
          value: "10.0.0.2"
        - name: REMOTE_PORT
          value: "80"

注意点として、前述のYAMLでConfigMap作成時と同様に、環境変数で指定する値は全てYAML的には文字列である必要があるため、ポート番号のような値もYAMLでは文字列として記述しなければならないのでクォートで囲む必要がある。

上の定義でREMOTE_PORTの値の80でクォートが無いとエラーになる。

[zaki@cloud-dev deploy-env (master)]$ kubectl apply -f sample-http.yaml -n zzz 
Error from server (BadRequest): error when creating "sample-http.yaml": Deployment in version "v1" cannot be handled as a Deployment: v1.Deployment.Spec: v1.DeploymentSpec.Template: v1.PodTemplateSpec.Spec: v1.PodSpec.Containers: []v1.Container: v1.Container.Env: []v1.EnvVar: v1.EnvVar.Value: ReadString: expects " or n, but found 8, error found in #10 byte of ...|,"value":80}],"image|..., bigger context ...|value":"10.0.0.2"},{"name":"REMOTE_PORT","value":80}],"image":"httpd","name":"httpd"}]}}}}
|...

ファイルシステムとしてマウント

前提としてConfigMapのデータをファイルシステムとしてマウントする場合はread onlyになる。
ボリュームやマウント関連の操作については基本的にはConfigMap特有の話ではなく、PersistentVolumeを使ったりする場合も同様。

また、ConfigMapのデータをPodからマウントしている状態で、ConfigMapのデータを更新した場合は、デフォルト60秒間隔で更新処理がKubernetesによって行われるため、Podの再作成無しにファイルの更新が反映される。

Mounted ConfigMaps are updated automatically
日本語ドキュメント マウントされたConfigMapは自動的に更新される

※ これはあくまでPod/コンテナにマウントされたファイルがConfigMapの更新によって動的に行われるのであって、コンテナ上で動いているプロセスが更新されたファイルを認識するかはプロセス次第。例えばApache Webサーバーなんかがhttpd.confの変更を再起動無しで認識するか?という話。

ただし、subPath使用時にはこの動的反映は機能しない。(後述)

apiVersion: v1
data:
  sample1.txt: |
    nya-n
    wanwan
  sample2.txt: |
    this file is sample file.
    curry tabetai!
kind: ConfigMap
metadata:
  name: configmap-sample-data

例として上記のConfigMapがあるとして、、

ConfigMap内の全ファイルをマウント

上記ConfigMapの全データ(sample1.txtsample2.txtの両方)をマウントするには、例えばDeploymentの定義であれば、

    spec:
      containers:
      - image: httpd
        name: httpd
        volumeMounts:
        - name: configmap-volume
          mountPath: /var/tmp/configmap
      volumes:
      - name: configmap-volume
        configMap:
          name: configmap-sample-data

containersの定義でマウントするパスとボリューム設定名を記述。
volumesの定義で↑で使用するボリューム設定の実態として対象ConfigMapの名前(上記のconfigmap-sample-data)を指定する。

[zaki@cloud-dev configmap (master)]$ kubectl exec -it -n cm-mount cm-mount-app-6474cfd495-cw6lj -c httpd -- bash
root@cm-mount-app-6474cfd495-cw6lj:/usr/local/apache2# ls /var/tmp/configmap/
sample1.txt  sample2.txt
root@cm-mount-app-6474cfd495-cw6lj:/usr/local/apache2# cat /var/tmp/configmap/sample1.txt 
nya-n
wanwan
root@cm-mount-app-6474cfd495-cw6lj:/usr/local/apache2# cat /var/tmp/configmap/sample2.txt 
this file is sample file.
curry tabetai!

この通り、sample1.txt, sample2.txt共にmountPathに指定したディレクトリにConfigMapのデータがファイルシステムとしてマウントされることで参照できる。

ConfigMapの指定ファイルのみマウント

同じConfigMapのうち、sample1.txtだけを参照したい場合(sample2.txtは参照させない場合)は、volumes定義部分でitemsを使ってキーを指定する。

      volumes:
      - name: configmap-volume1
        configMap:
          name: configmap-sample-data
          items:
          - key: sample1.txt
            path: data1

※ container側は変更無し。

    spec:
      containers:
      - image: httpd
        name: httpd
        volumeMounts:
        - name: configmap-volume1
          mountPath: /var/tmp/configmap

この設定でデプロイすると、以下のように、「configmap-sample-dataリソース内のsample1.txtキーのデータをdata1というファイル名でマウント」という動きになる。
環境変数で指定キーのみの場合と同じように、ConfigMapにおけるキー名がそのままファイル名とはならない動作となる。(もちろんpathにキー名と同じ文字列を指定すればConfigMap定義上のファイル名でマウントできる)

[zaki@cloud-dev configmap (master)]$ kubectl exec -it -n cm-mount file-mount-9c8d8f9df-twd9l -c httpd -- bash
root@file-mount-9c8d8f9df-twd9l:/usr/local/apache2# ls /var/tmp/configmap/
data1
root@file-mount-9c8d8f9df-twd9l:/usr/local/apache2# cat /var/tmp/configmap/data1 
nya-n
wanwan

github.com

subPathを使ったマウント

Using subPath

通常のボリュームマウントでは、対象のルートディレクトリをmountPathのパスにマウントする。なので対象ストレージの全体がマウントされる。
ストレージの全体は不要で、サブディレクトリ以下だけが欲しい場合はsubPathを使うことで、サブディレクトリ以下をマウントすることもできる。

ConfigMapの場合もこの機能を使うことができ、でもConfigMapではディレクトリの概念がないため、「マウントポイントにファイルをマウント」という動作になる。

文書だとよくわからないので実際に(比較のために1Pod複数コンテナ構成で)やってみると、

    spec:
      containers:
      - image: httpd
        name: httpd
        volumeMounts:
        - name: configmap-volume
          mountPath: /var/tmp/configmap/nyan
          subPath: sample1.txt
      - image: centos:7
        name: centos7
        command: [ "tail", "-f", "/dev/null"]
        volumeMounts:
        - name: configmap-volume
          mountPath: /tmp/configmap-file/wan
          subPath: sample1.txt
      volumes:
      - name: configmap-volume
        configMap:
          name: configmap-sample-data

containers.[]volumeMounts定義にsubPath:でパスを指定。このパスというのはボリューム内のパスを指定するが、ConfigMapではファイルしかないのでConfigMap内のキー(つまりファイル名)指定となっている。

この設定でどうなるかというと、sample1.txtのファイルの内容がhttpdコンテナ内では/var/tmp/configmap/nyanファイルに、centos7コンテナ内では/tmp/configmap-file/wanファイルにマウントされるという動作になる。

[zaki@cloud-dev configmap (master)]$ kc exec -it -n cm-mount cm-mount-subpath-64fd77d994-czddj -c httpd -- bash
root@cm-mount-subpath-64fd77d994-czddj:/usr/local/apache2# cat /var/tmp/configmap/nyan 
nya-n
wanwan
[zaki@cloud-dev configmap (master)]$ kc exec -it -n cm-mount cm-mount-subpath-64fd77d994-czddj -c centos7 -- bash
[root@cm-mount-subpath-64fd77d994-czddj /]# cat /tmp/configmap-file/wan 
nya-n
wanwan

subPath指定時の動作として、ConfigMapの内容が更新されてもコンテナ内には反映されないので注意。

github.com


ストレージ上に/path/to/fileという構成のファイルがあったとすると、mountPath/var/tmp/hogeを指定すると、コンテナ内では/var/tmp/hoge/path/to/fileというディレクトリ構成になる、というのが通常の使い方。
subPathの本来の使い方としては、要件として/path/toというディレクトリが不要でfileだけを参照したい場合(任意のパスにfileを置きたい場合)に、subPath: path/toを記述することで、コンテナ内で/var/tmp/hoge/fileというパスで参照できるようになる、というもの。


一つのConfigMapデータに対してコンテナ毎にファイル名を変更して参照したいのであれば、記述量は増えるがitemsを使って別々のpathによるファイル名設定を行い、それぞれのボリューム設定をマウントする方が(ConfigMap更新によるファイル変更を検知できるという点では)良い。
逆にデプロイ時のConfigMapの内容から不用意に変更されたくないような使い方をしたい場合は有効。

      volumes:
      - name: configmap-volume1         # "wann"というファイル名を使いたいコンテナはこれを使う
        configMap:
          name: configmap-sample-data
          items:
          - key: sample1.txt
            path: wann
      - name: configmap-volume2         # "nyan"というファイル名を使いたいコンテナはこれを使う
        configMap:
          name: configmap-sample-data
          items:
          - key: sample1.txt
            path: nyan

なお、値を不変にしたいという目的であれば、Kubernetes 1.19時点ではbetaの「Immutable ConfigMaps」機能もある。(動作未検証)


そういえば--dry-runじゃなくて--dry-run=clientって書かないといけなかったのか。忘れてた。


環境

[zaki@cloud-dev configmap (master)]$ kubectl version --short
Client Version: v1.20.1
Server Version: v1.19.4

関連ドキュメント

kubernetes.io

kubernetes.io