你真的搞清楚k8s的subpath了吗?
我们经常会使用subpath,当我们直接挂载一个存储或者configmap到一个目录后,目录下面原有的内容将会被覆盖。比如,我们只想替换nginx容器里面的 /etc/default.conf 这个文件,但如果通过configmap 直接挂载到 /etc 目录是不行的,会直接覆盖整个目录,导致原有的文件被覆盖了。此时,就需要借助subpath了。
通过subpath我们可以只挂载一个文件到/etc 目录下,从而避免全目录覆盖,但这里也要一个小瑕疵,就是这个文件后续的变化不会更新到容器里面了,这点需要注意。
回到主题,那么这个subpath是如何实现的? 我们先看这样一个例子。apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - image: nginx name: nginx ports: - containerPort: 80 volumeMounts: - mountPath: /mnt/aaa/bbb name: volume-test subPath: aaa/bbb - mountPath: /mnt/ccc/ddd name: volume-test subPath: ccc/eee #故意写成eee volumes: - name: volume-test hostPath: path: /srv
当我们登录到容器里面后,可以看到 /mnt/aaa/bbb 和 /mnt/ccc/ddd 这两个目录,在宿主机上可以看到 /srv/aaa/bbb 和 /srv/aaa/eee目录。这个subpath 其实是我们存储的subpath,k8s 会在存储的目录下找寻这个文件或者目录,如果不存在,则会创建。我们看一下k8s代码实现。 # 存储在宿主机上路径 volumePath := hostPath # 拼接 subpath 路径 hostPath = filepath.Join(volumePath, subPath) # 如果路不存在则先创建这个subpath路径 if subPathExists, err := hu.PathExists(hostPath); err != nil { klog.Errorf("Could not determine if subPath %s exists; will not attempt to change its permissions", hostPath) } else if !subPathExists { perm, err := hu.GetMode(volumePath) if err != nil { return nil, cleanupAction, err } if err := subpather.SafeMakeDir(subPath, volumePath, perm); err != nil { // Don"t pass detailed error back to the user because it could give information about host filesystem klog.Errorf("failed to create subPath directory for volumeMount %q of container %q: %v", mount.Name, container.Name, err) return nil, cleanupAction, fmt.Errorf("failed to create subPath directory for volumeMount %q of container %q", mount.Name, container.Name) } }
如果存储里面已经包含了subpath路径,比如 NAS存储子目录已经存在,或者configmap 某个key 已经存在,此时就不会创建这个文件或者目录,反之,如果不存在,则会首先创建这个目录。
后续只是将这个subpath路径 bind mount 到容器里面的路径,和其他存储挂载没啥差别。