kubernetesでのVolumeMountが意図通りにいかない時の対処


Tips for kubernetes volume mount

kubelogo

kubernetesでSecretやConfigMapのVolumeマウントがなかなか意図した通りにいかなかったときの対処を備忘録として残しておきます。

kubernetesでのVolume管理

そもそもkubernetesでのVolumeはどうなっているのか確認してみます。

試しに下記のようにConfigMapとそれをマウントして可視化するようなJobを実行して Volumeマウントしたディレクトリ配下を見てみます。

job.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: extra-config
  namespace: default
data:
  some_config: thisisconfigvalue
---
apiVersion: batch/v1
kind: Job
metadata:
  name: mounttest-job
  namespace: default
spec:
  template:
    spec:
      containers:
      - name: ls-recursive
        image: busybox
        command: ["ls", "-laR", "/volumes/"]
        volumeMounts:
        - name: config-volume
          mountPath: /volumes
      volumes:
        - name: config-volume
          configMap:
            name: extra-config
      restartPolicy: Never

で実行してみます

ᐅ kubectl apply -f job.yaml && \
    sleep 5 && \
    kubectl logs jobs/mounttest-job && \
    kubectl delete jobs/mounttest-job

configmap/extra-config created
job.batch/mounttest-job created
/volumes/:
total 12
drwxrwxrwx    3 root     root          4096 Jul  4 09:08 .
drwxr-xr-x    1 root     root          4096 Jul  4 09:08 ..
drwxr-xr-x    2 root     root          4096 Jul  4 09:08 ..2021_07_03_09_08_23.921812632
lrwxrwxrwx    1 root     root            31 Jul  4 09:08 ..data -> ..2021_07_03_09_08_23.921812632
lrwxrwxrwx    1 root     root            18 Jul  4 09:08 some_config -> ..data/some_config

/volumes/..2021_07_03_09_08_23.921812632:
total 12
drwxr-xr-x    2 root     root          4096 Jul  4 09:08 .
drwxrwxrwx    3 root     root          4096 Jul  4 09:08 ..
-rw-r--r--    1 root     root            17 Jul  4 09:08 some_config
job.batch "mounttest-job" deleted

kubernetesでのVolumeは、上記の通りSym link構造の構成となっています。

  • 目的のConfigファイル/volumes/some_configはVolume dir直下..datadir配下のsome_configへのlink
  • ..datadirはtimestamp dirへのlink
  • 実態は/volumes/<timestamp>/some_configにある

..dataディレクトリの参照先をtimestampディレクトリに貼ることでAtomicな構成を実現しているようです。

で、この特異な構成によって困ったことがあったのでそのケースと対応についてまとめます。

Case and Solutions

CASE1: あるDir配下のConfigを再帰的に捜査する処理の場合

例えば、特定ディレクトリを指定して、そのディレクトリ配下のファイルを再帰的に捜査して読み込むようなプログラム(例えばmigrateツールなど)を実行する場合、困ることがあります。

上記の例だと、/volumesdir配下のsome_configを読み込みたい場合、

  • /volumes/some_config
  • /volumes/<timestamp>/some_config

… と、同一ファイルが2回読み込まれてしまいます。。

この場合、考えられる対処として下記で対応しました。

  1. dir指定に..data(上記例だと/volumes/..data/)を指定する
  2. kubernetesでのVolumeを使わずあらかじめ利用するImageに入れておく
  3. 影響のない適当なdir配下にマウントし、コマンドで該当dir配下にlinkを貼る(またはmv,cpなど)

3の方法は、具体的には下記の感じです。

job2.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: mounttest-job2
  namespace: default
spec:
  template:
    spec:
      containers:
      - name: ls-recursive
        image: busybox
        command: ["/bin/sh", "-c"]
        args:
        - |
          ln -s /configmaps/some_config /volumes/some_config
          ls -laR /volumes/          
        volumeMounts:
        - name: config-volume
          mountPath: /configmaps
      volumes:
        - name: config-volume
          configMap:
            name: extra-config
      restartPolicy: Never

本来指定したいdirにはマウントせず、適当な影響ないdirに一旦マウントしてコマンドの最初にlink処理を行います。

実行してみると下記の感じになります。

ᐅ kubectl apply -f job2.yaml && \
    sleep 10 && \
    kubectl logs jobs/mounttest-job2 && \
    kubectl delete jobs/mounttest-job2
job.batch/mounttest-job2 created
/volumes/:
total 12
drwxr-xr-x    1 root     root          4096 Jul  4 18:51 .
drwxr-xr-x    1 root     root          4096 Jul  4 18:51 ..
lrwxrwxrwx    1 root     root            23 Jul  4 18:51 some_config -> /configmaps/some_config
job.batch "mounttest-job2" deleted

上記の通り重複読み込みはないですね

CASE2: 既存のImageの設定にConfigMapで設定追加する場合

既存のコンテナイメージの特定dir配下の設定ファイルに、kubernetesのVolumeマウントで設定追加したい場合にも困ることがあります。

試しに下記のようなconfigを同梱したイメージを作成し、マウントしてみます。

Dockerfile

FROM busybox
COPY conf/example.conf /volumes/example.conf

上記イメージを使って/volumesdirに追加でマウントしてみます。

docker build -t mount-busybox:local .

job3.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: mounttest-job3
  namespace: default
spec:
  template:
    spec:
      containers:
      - name: ls-recursive
        image: mount-busybox:local
        # for pull from local
        imagePullPolicy: Never
        command: ["ls", "-laR", "/volumes/"]
        volumeMounts:
        - name: config-volume
          mountPath: /volumes
      volumes:
        - name: config-volume
          configMap:
            name: extra-config
      restartPolicy: Never

で、上記を実行してみると、、

ᐅ kubectl apply -f job3.yaml && \
    sleep 10 && \
    kubectl logs jobs/mounttest-job3 && \
    kubectl delete jobs/mounttest-job3

job.batch/mounttest-job3 created
/volumes/:
total 12
drwxrwxrwx    3 root     root          4096 Jul  4 19:18 .
drwxr-xr-x    1 root     root          4096 Jul  4 19:18 ..
drwxr-xr-x    2 root     root          4096 Jul  4 19:18 ..2021_07_04_19_18_49.338411331
lrwxrwxrwx    1 root     root            31 Jul  4 19:18 ..data -> ..2021_07_04_19_18_49.338411331
lrwxrwxrwx    1 root     root            18 Jul  4 19:18 some_config -> ..data/some_config

/volumes/..2021_07_04_19_18_49.338411331:
total 12
drwxr-xr-x    2 root     root          4096 Jul  4 19:18 .
drwxrwxrwx    3 root     root          4096 Jul  4 19:18 ..
-rw-r--r--    1 root     root            17 Jul  4 19:18 some_config
job.batch "mounttest-job3" deleted

上記の通りもともとコンテナイメージに同梱していた/volumesdir配下のexample.confが消えています。

kube公式docsにも確かにそう記載されています。

Caution: If there are some files in the /etc/config/ directory, they will be deleted.

これはCASE1での3の方法で対応しました。

CASE3: あるDir配下に複数のVolumeをマウントする場合

これは調べればすぐ出てきますが一応。。

下記のように同一ディレクトリ配下に複数マウントする場合、エラーとなります。

...
spec:
  template:
    spec:
      containers:
      - name: ls-recursive
        image: busybox
        command: ["ls", "-laR", "/volumes/"]
        volumeMounts:
        - name: config-volume
          mountPath: /volumes
        - name: config-volume2
          mountPath: /volumes
      volumes:
        - name: config-volume
          configMap:
            name: extra-config
        - name: config-volume2
          configMap:
            name: extra-config2
ᐅ kubectl apply -f job4.yaml && \
    sleep 10 && \
    kubectl logs jobs/mounttest-job4 && \
    kubectl delete jobs/mounttest-job4

The Job "mounttest-job4" is invalid: spec.template.spec.containers[0].volumeMounts[1].mountPath: Invalid value: "/volumes": must be unique

projectedでVolumeを束ねてからマウントさせます。

参考:https://kubernetes.io/docs/concepts/storage/volumes/#projected

...
spec:
  template:
    spec:
      containers:
      - name: ls-recursive
        image: busybox
        command: ["/bin/sh", "-c"]
        args:
        - |
          #ln -s /configmaps/some_config /volumes/some_config;
          ls -laR /volumes/          
        volumeMounts:
        - name: config-bundle-volume
          mountPath: /volumes
      volumes:
      - name: config-bundle-volume
        projected:
          sources:
          - configMap:
              name: extra-config
          - configMap:
              name: extra-config2

確認してみるとちゃんと意図したとおりにマウントされています。

ᐅ kubectl apply -f job4.yaml && \
    sleep 10 && \
    kubectl logs jobs/mounttest-job4 && \
    kubectl delete jobs/mounttest-job4

job.batch/mounttest-job4 created
/volumes/:
total 4
drwxrwxrwt    3 root     root           120 Jul  4 19:47 .
drwxr-xr-x    1 root     root          4096 Jul  4 19:47 ..
drwxr-xr-x    2 root     root            80 Jul  4 19:47 ..2021_07_04_19_47_19.259541501
lrwxrwxrwx    1 root     root            31 Jul  4 19:47 ..data -> ..2021_07_04_19_47_19.259541501
lrwxrwxrwx    1 root     root            18 Jul  4 19:47 some_config -> ..data/some_config
lrwxrwxrwx    1 root     root            19 Jul  4 19:47 some_config2 -> ..data/some_config2

/volumes/..2021_07_04_19_47_19.259541501:
total 8
drwxr-xr-x    2 root     root            80 Jul  4 19:47 .
drwxrwxrwt    3 root     root           120 Jul  4 19:47 ..
-rw-r--r--    1 root     root            17 Jul  4 19:47 some_config
-rw-r--r--    1 root     root            18 Jul  4 19:47 some_config2
job.batch "mounttest-job4" deleted

まとめ

基本的にkubernetesのVolumeマウントは、基本的にピンポイントにファイル指定して利用する分には問題になりにくいでしょうが、仕様を把握してないとあれ?っとなりますです。。

ちゃんと仕様を把握して気をつけましょう。

ここでの例ではConfigMapでのものでしたが、これはSecretでも同様です。

参考


See also