Kubernetes 中的 subPath 和 mountPath 有什么区别

gir*_*er1 6 kubernetes

我正在尝试将 volumeMounts 中的文件添加到 .dockerignore 并试图了解 subPath 和 mountPath 之间的区别。阅读官方文档对我来说并不清楚。

我应该从我读取的内容中添加 mountPath 是 pod 中将安装卷的目录。

来自官方文档:“subPath volumeMounts.subPath 属性指定引用卷内的子路径而不是其根。” https://kubernetes.io/docs/concepts/storage/volumes/#using-subpath(这部分不清楚)

- mountPath: /root/test.pem
            name: test-private-key
            subPath: test.testing.com.key
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我应该将 test.pem 和 test.testing.com.key 都包含到 dockerignore 中吗?

Yar*_*riv 37

mountPath 和 subPath 之间的区别在于 subPath 是 mountPath 的补充,它的存在是为了解决问题。

查看示例 Pod 清单中的评论,我解释了问题以及 subPath 如何解决它。

要进一步了解差异,请查看“底层”部分,了解 kubernetes 如何处理这两个属性。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  volumes:
  - name: vol1
    emptyDir: {}
    
  containers:
  - image: nginx
    name: mypod-container

    volumeMounts:

      # In our example let's say we want to mount "vol1" on path "/a/b/c"
      # So we declare this:

    - name: vol1
      mountPath: /a/b/c

      # But what if we also want to use a child folder "d" ?
      # If we try to use "/a/b/c/d" then we wont have access to /a/b/c
      # because the mountPath /a/b/c is overwritten by mountPath /a/b/c/d
      # So if we try the following mountPath we lose our above declaration:

#   - name: vol1
#     mountPath: /a/b/c/d  # This overwrites the above mount to /a/b/c


      # The solution:
      # Using subPath we enjoy both worlds.
      # (1) We dont overwrite the info in our volume at path /a/b/c .
      # (2) We have a separate path /a/b/c/d that when we can write to 
      #     without affecting the content in path /a/b/c.
      # Here is how we write the correct declaration:

    - name: vol1
      mountPath: /a/b/c/d
      subPath: d
Run Code Online (Sandbox Code Playgroud)

在 mountPath 和 subPath 的背后

让我们看看 kubernetes 的底层,看看它如何以不同的方式管理 mountPath 和 subPath 属性:


1.kubernetes如何管理mountPath:
当声明 mountPath 时,kubernetes 将在以下路径中创建一个具有卷名称的文件:

/var/lib/kubelet/pods/<pod-id>/volumes/kubernetes.io~empty-dir/<volume name>

因此,在上面的清单示例中,这就是创建的内容(“vol1”是卷名称): /var/lib/kubelet/pods/301d...a71c/volumes/kubernetes.io~empty-dir/vol1

现在您可以看到,如果我们定义了“/a/b/c/d”挂载路径,我们将触发在同一目录中创建另一个文件“vol1”,从而覆盖原始文件。

2.kubernetes如何管理subPath:
当声明 subPath 时,kubernetes 会创建一个具有相同卷名称 Volume 但位于不同路径的文件:

/var/lib/kubelet/pods/<pod-id>/volume-subpaths/<volume name>

因此,在上面的清单示例中,这就是创建的内容(“vol1”是卷名称): /var/lib/kubelet/pods/3eaa...6d1/volume-subpaths/vol1

结论:
现在您可以看到,subPath 使我们能够定义额外的volumePath,而不会与根voulmePath 发生冲突。它通过在 kubernetes kubelet 中的不同目录中创建具有相同卷名但不同目录的文件来实现此目的。

  • 这些文件是否真的被删除了,或者包含它们的目录现在是一个挂载点,因此它下面的任何内容都将变得无法访问?我相信是后者。 (3认同)
  • @Yariv你误读了链接。“不建议将此示例子路径配置用于生产用途。”,而不是 `subPath` 参数。 (3认同)

Bur*_*dar 17

mountPath显示引用卷应安装在容器中的位置。例如,如果您将卷挂载到mountPath: /a/b/c,则该卷将可供目录下的容器使用/a/b/c

挂载卷将使所有卷在mountPath. 如果只需要挂载卷的一部分,例如卷中的单个文件,则使用subPath来指定必须挂载的部分。例如,mountPath: /a/b/c,subPath: d将使d目录下挂载卷中的任何内容/a/b/c

  • 感谢您的答复。所以为了确保我理解你的最后一句话,你是说只有文件 d 才会被安装到 /a/b/c 吗?那么它会是/a/b/c/d吗? (7认同)
  • 是的,使用“subPath”,这就是您将得到的。 (4认同)
  • 注意,当`subPath`是文件夹时,文件夹的内容将被挂载到`mountPath` (4认同)

gau*_*har 8

subPath 用于选择卷中应安装容器卷的特定路径。默认为“”(卷的根目录)。

检查这里提到的这一点。因此,这意味着您仍然可以在 mountPath 中提到的路径上挂载卷,但不是从卷根挂载它,而是可以在卷内指定一个单独的子路径以挂载在容器中的volumeMount 目录下。

为了澄清这意味着什么,我在我的 minikube 节点上创建了一个简单的卷。

docker@minikube:/mnt/data$ ls -lrth
total 8.0K
drwxr-xr-x 2 root root 4.0K Dec 30 16:23 path1
drwxr-xr-x 2 root root 4.0K Dec 30 16:23 path2
docker@minikube:/mnt/data$ 
docker@minikube:/mnt/data$ pwd
/mnt/data
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,在本例中,我有一个目录,并且在该卷中创建了两个子目录。在每个路径 1 或路径 2 文件夹下,我都放置了不同的索引文件。

docker@minikube:/mnt/data$ pwd
/mnt/data
docker@minikube:/mnt/data$ cat path1/index.html 
This is index file from path1
docker@minikube:/mnt/data$ 
docker@minikube:/mnt/data$ cat path2/index.html 
This is index file from path2
docker@minikube:/mnt/data$ 
Run Code Online (Sandbox Code Playgroud)

现在,我使用以下示例清单在我的 minikube 节点上使用此卷创建了一个示例 PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
Run Code Online (Sandbox Code Playgroud)

之后,我使用下面的清单创建了示例 PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 800Mi
Run Code Online (Sandbox Code Playgroud)

现在,如果我创建了一个 nginx pod 并在我的卷下使用此 PVC,根据我在 pod 规范中使用的子路径配置,我将从该特定子文件夹安装该卷。

即如果我对我的 nginx pod 使用以下清单

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
    - name: test
      image: nginx
      volumeMounts:
        # a mount for site-data
        - name: config
          mountPath: /usr/share/nginx/html
          subPath: path1
  volumes:
    - name: config
      persistentVolumeClaim:
        claimName: task-pv-claim
Run Code Online (Sandbox Code Playgroud)

我在 POD IP 上做了一个curl,我得到了从path1 提供的index.html。

Gauravs-MBP:K8s alieninvader$ kubectl exec -it mycurlpod -- /bin/sh
/ $ curl 172.17.0.3
This is index file from path1
Run Code Online (Sandbox Code Playgroud)

如果我更改了我的 pod 清单并将子路径用作路径2,那么新的清单就变成了这样

apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
    - name: test
      image: nginx
      volumeMounts:
        # a mount for site-data
        - name: config
          mountPath: /usr/share/nginx/html
          subPath: path2
  volumes:
    - name: config
      persistentVolumeClaim:
        claimName: task-pv-claim
Run Code Online (Sandbox Code Playgroud)

然后,正如预期的那样,curl 到 nginx pod 将产生以下输出,为路径 2 中的文件提供服务。

Gauravs-MBP:K8s alieninvader$ kubectl exec -it mycurlpod -- /bin/sh

/ $ curl 172.17.0.3
This is index file from path2
/ $ 
Run Code Online (Sandbox Code Playgroud)