k8s で volume があるはずなのに volumeMounts[0].name: Not found になってしまう

まさかの同名 volume が定義できるケースがある


Posted on 2021年 11月 19日 (金)
Tags kubernetes
kubernetes

ConfigMap の volumeMounts で思わぬ沼にはまってしまう

  • 検証したバージョン: kubernetes 1.20.11

下記のように、Deployment などで ConfigMap を volumeMounts して利用することがある。

yaml-1

 1    spec:
 2      containers:
 3      - image: httpd
 4        name: httpd
 5        volumeMounts:
 6        - name: configmap-volume
 7          mountPath: /var/tmp/configmap
 8      volumes:
 9      - name: configmap-volume
10        configMap:
11          name: configmap-sample-data

上記の volume なのだが、通常は同名で重複して applyできない。

yaml-2

 1    spec:
 2      containers:
 3      - image: httpd
 4        name: httpd
 5        volumeMounts:
 6        - name: configmap-volume
 7          mountPath: /var/tmp/configmap
 8      volumes:
 9      - name: configmap-volume
10        configMap:
11          name: configmap-sample-data
12      - name: configmap-volume
13        configMap:
14          name: configmap-sample-data
1The Deployment "test" is invalid: spec.template.spec.volumes[1].name: Duplicate value: "configmap-volume"

しかし、yaml-1 を apply した後、yaml-2 を apply すると、同名の volume を apply できてしまう (!!!)

apply すると last-applied-configuration としては configmap-volume が 2つ生成されている。

その後、yaml-1 を apply すると Not found のエラーが発生する。

 1# ここは普通
 2$ kubectl apply -f ~/Desktop/yaml-1.yml
 3deployment.apps/test created
 4
 5# ここが反映できてしまう
 6$ kubectl apply -f ~/Desktop/yaml-2.yml
 7deployment.apps/test configured
 8
 9# Not found になる
10$ kubectl apply -f ~/Desktop/yaml-1.yml
11The Deployment "test" is invalid:
12* spec.template.spec.containers[0].volumeMounts[0].name: Not found: "configmap-volume"

Kubernetes 本家の issue をざっと見たが、仕様なのか、あるいはバグなのか不明(volume が配列で定義されていて、apply 後の追加をどう処理しているか次第)

調査の仕方として、1回目の apply を行ったあとに kubectl get deployment test -oyaml で deployment の中の last-applied-configuration の json を展開して確認する。

よく言われるように、kubernetes は宣言的なシステムというが、宣言的な設定のマスタは etcd の中にあり、それは last-applied-configuration で閲覧できる。

kustomize や helm で色々試していると出くわすかもしれないケースで、沼にハマってしまった。

minikube での検証

下記の方法で再現が行える。

  1$ minikube kubectl -- apply -f <(cat <<EOF
  2apiVersion: v1
  3kind: Namespace
  4metadata:
  5  name: test
  6---
  7apiVersion: v1
  8kind: ConfigMap
  9metadata:
 10  name: configmap-sample-data
 11  namespace: test
 12data:
 13  test: "test"
 14---
 15apiVersion: apps/v1
 16kind: Deployment
 17metadata:
 18  name: nginx-deployment
 19  labels:
 20    app: nginx
 21  namespace: test
 22spec:
 23  replicas: 1
 24  selector:
 25    matchLabels:
 26      app: nginx
 27  template:
 28    metadata:
 29      labels:
 30        app: nginx
 31    spec:
 32      containers:
 33      - name: nginx
 34        image: nginx:latest
 35        ports:
 36        - containerPort: 80
 37        volumeMounts:
 38        - name: configmap-volume
 39          mountPath: /var/tmp/configmap
 40      volumes:
 41      - name: configmap-volume
 42        configMap:
 43          name: configmap-sample-data  
 44EOF
 45)
 46namespace/test created
 47configmap/configmap-sample-data created
 48deployment.apps/nginx-deployment created
 49
 50
 51$ minikube kubectl -- apply -f <(cat <<EOF
 52apiVersion: apps/v1
 53kind: Deployment
 54metadata:
 55  name: nginx-deployment
 56  labels:
 57    app: nginx
 58  namespace: test
 59spec:
 60  replicas: 1
 61  selector:
 62    matchLabels:
 63      app: nginx
 64  template:
 65    metadata:
 66      labels:
 67        app: nginx
 68    spec:
 69      containers:
 70      - name: nginx
 71        image: nginx:latest
 72        ports:
 73        - containerPort: 80
 74        volumeMounts:
 75        - name: configmap-volume
 76          mountPath: /var/tmp/configmap
 77      volumes:
 78      - name: configmap-volume
 79        configMap:
 80          name: configmap-sample-data 
 81      - name: configmap-volume # これが適用できてしまう
 82        configMap:
 83          name: configmap-sample-data
 84EOF
 85)
 86deployment.apps/nginx-deployment configured
 87
 88$ minikube kubectl -- -ntest get deploy nginx-deployment -oyaml
 89apiVersion: apps/v1
 90kind: Deployment
 91metadata:
 92  annotations:
 93    deployment.kubernetes.io/revision: "1"
 94    kubectl.kubernetes.io/last-applied-configuration: |
 95      {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx-deployment","namespace":"test"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx:latest","name":"nginx","ports":[{"containerPort":80}],"volumeMounts":[{"mountPath":"/var/tmp/configmap","name":"configmap-volume"}]}],"volumes":[{"configMap":{"name":"configmap-sample-data"},"name":"configmap-volume"},{"configMap":{"name":"configmap-sample-data"},"name":"configmap-volume"}]}}}}
 96  creationTimestamp: "2021-11-20T14:27:43Z"
 97  generation: 2
 98  labels:
 99    app: nginx
100...
101
102# last-applied-configuration を見てみる
103{
104    "apiVersion": "apps/v1",
105    "kind": "Deployment",
106    "metadata": {
107        "annotations": {},
108        "labels": {
109            "app": "nginx"
110        },
111        "name": "nginx-deployment",
112        "namespace": "test"
113    },
114    "spec": {
115        "replicas": 1,
116        "selector": {
117            "matchLabels": {
118                "app": "nginx"
119            }
120        },
121        "template": {
122            "metadata": {
123                "labels": {
124                    "app": "nginx"
125                }
126            },
127            "spec": {
128                "containers": [
129                    {
130                        "image": "nginx:latest",
131                        "name": "nginx",
132                        "ports": [
133                            {
134                                "containerPort": 80
135                            }
136                        ],
137                        "volumeMounts": [
138                            {
139                                "mountPath": "/var/tmp/configmap",
140                                "name": "configmap-volume"
141                            }
142                        ]
143                    }
144                ],
145                "volumes": [
146                    {
147                        "configMap": {
148                            "name": "configmap-sample-data"
149                        },
150                        "name": "configmap-volume"     # <-- 同名が2つ存在できる...
151                    },
152                    {
153                        "configMap": {
154                            "name": "configmap-sample-data"
155                        },
156                        "name": "configmap-volume"     # <-- 同名が2つ存在できる..
157                    }
158                ]
159            }
160        }
161    }
162}
163
164
165# こちらが失敗する
166$ minikube kubectl -- apply -f <(cat <<EOF
167apiVersion: apps/v1
168kind: Deployment
169metadata:
170  name: nginx-deployment
171  labels:
172    app: nginx
173  namespace: test
174spec:
175  replicas: 1
176  selector:
177    matchLabels:
178      app: nginx
179  template:
180    metadata:
181      labels:
182        app: nginx
183    spec:
184      containers:
185      - name: nginx
186        image: nginx:latest
187        ports:
188        - containerPort: 80
189        volumeMounts:
190        - name: configmap-volume
191          mountPath: /var/tmp/configmap
192      volumes:
193      - name: configmap-volume
194        configMap:
195          name: configmap-sample-data
196EOF
197)
198The Deployment "nginx-deployment" is invalid: spec.template.spec.containers[0].volumeMounts[0].name: Not found: "configmap-volume"

Share


See also