Kubernetes数据卷Volume和数据卷分类详解
数据卷概述
Kubernetes Volume(数据卷)主要解决了如下两方面问题:
- 数据持久性:通常情况下,容器运行起来之后,写入到其文件系统的文件暂时性的。当容器崩溃后,kubelet 将会重启该容器,此时原容器运行后写入的文件将丢失,因为容器将重新从镜像创建。
- 数据共享:同一个 Pod(容器组)中运行的容器之间,经常会存在共享文件/文件夹的需求
Docker 里同样也存在一个 volume(数据卷)的概念,但是 docker 对数据卷的管理相对 kubernetes 而言要更少一些。在 Docker 里,一个 Volume(数据卷)仅仅是宿主机(或另一个容器)文件系统上的一个文件夹。Docker 并不管理 Volume(数据卷)的生命周期。
在 Kubernetes 里,Volume(数据卷)存在明确的生命周期(与包含该数据卷的容器组相同)。因此,Volume(数据卷)的生命周期比同一容器组中任意容器的生命周期要更长,不管容器重启了多少次,数据都能被保留下来。当然,如果容器组退出了,数据卷也就自然退出了。此时,根据容器组所使用的 Volume(数据卷)类型不同,数据可能随数据卷的退出而删除,也可能被真正持久化,并在下次容器组重启时仍然可以使用。
从根本上来说,一个 Volume(数据卷)仅仅是一个可被容器组中的容器访问的文件目录(也许其中包含一些数据文件)。这个目录是怎么来的,取决于该数据卷的类型(不同类型的数据卷使用不同的存储介质)。
使用 Volume(数据卷)时,我们需要先在容器组中定义一个数据卷,并将其挂载到容器的挂载点上。容器中的一个进程所看到(可访问)的文件系统是由容器的 docker 镜像和容器所挂载的数据卷共同组成的。Docker 镜像将被首先加载到该容器的文件系统,任何数据卷都被在此之后挂载到指定的路径上。Volume(数据卷)不能被挂载到其他数据卷上,或者通过引用其他数据卷。同一个容器组中的不同容器各自独立地挂载数据卷,即同一个容器组中的两个容器可以将同一个数据卷挂载到各自不同的路径上。
我们现在通过下图来理解 容器组、容器、挂载点、数据卷、存储介质(nfs、PVC、ConfigMap)等几个概念之间的关系:
- 一个容器组可以包含多个数据卷、多个容器
- 一个容器通过挂载点决定某一个数据卷被挂载到容器中的什么路径
- 不同类型的数据卷对应不同的存储介质(图中列出了 nfs、PVC、ConfigMap 三种存储介质,接下来将介绍更多)
数据卷内子路径
数据卷内子路径(SubPath)是Kubernetes中一种用于挂载数据卷中指定部分内容的机制。在Kubernetes中,数据卷是一种将主机上的文件或目录挂载到容器中的机制,允许容器访问主机上的文件或目录。而数据卷内子路径则允许将数据卷中指定的子目录或子文件挂载到容器中的指定路径上。
具体来说,当创建Pod时,在定义容器的VolumeMounts时,可以通过指定SubPath来选择数据卷中的特定子目录或子文件,将其挂载到容器的指定路径上。 例如,可以将一个包含多个文件的数据卷挂载到容器的某个目录下,然后指定SubPath为其中的某个文件的名称,这样只有这个文件会被挂载到容器中。
使用数据卷内子路径可以在容器中灵活地选择需要挂载的数据卷的部分内容,避免将整个数据卷都挂载到容器中造成资源浪费,并且可以方便地控制容器中的文件路径和内容。
数据卷的类型
Kubernetes 目前支持多达 28 种数据卷类型(其中大部分特定于具体的云环境如 GCE/AWS/Azure 等),如需查阅所有的数据卷类型,请查阅 Kubernetes 官方文档
本文针对自建 Kubernetes 时,经常使用的数据卷的类型描述如下:
emptyDir
-
描述
emptyDir类型的数据卷在容器组被创建时分配给该容器组,并且直到容器组被移除,该数据卷才被释放。该数据卷初始分配时,始终是一个空目录。 同一容器组中的不同容器都可以对该目录执行读写操作,并且共享其中的数据,(尽管不同的容器可能将该数据卷挂载到容器中的不同路径)。当容器组被移除时,emptyDir数据卷中的数据将被永久删除
-
解析
“emptyDir” 存储卷的默认存储路径是 “/var/lib/kubelet/pods/[PodID]/volumes/kubernetes.io~empty-dir/[VolumeName]”,其中 [PodID] 是 Pod 的唯一标识符,[VolumeName] 是存储卷的名称。例如,如果你在 Pod YAML 中定义了一个名为 “my-volume” 的 “emptyDir” 存储卷,那么它的默认存储路径将是 “/var/lib/kubelet/pods/[PodID]/volumes/kubernetes.io~empty-dir/my-volume”。在容器中,你可以将 “emptyDir” 存储卷挂载到容器的文件系统中的任何目录中。例如,你可以使用以下 YAML 文件在容器中将 “emptyDir” 存储卷挂载到 “/data” 目录中:
apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: container1 image: my-image volumeMounts: - name: shared-data mountPath: /data - name: container2 image: my-other-image volumeMounts: - name: shared-data mountPath: /data volumes: - name: shared-data emptyDir: {}
在上面的示例中,我们创建了一个名为my-pod
的 Pod,其中包含两个容器:container1
和container2
。这两个容器都将shared-data
卷挂载到/data
目录,并使用emptyDir
类型的卷。这样,两个容器就可以共享/data
目录下的任何数据。
容器崩溃时,kubelet 并不会删除容器组,而仅仅是将容器重启,因此 emptyDir 中的数据在容器崩溃并重启后,仍然是存在的。
- 适用场景
- 空白的初始空间,例如合并/排序算法中,临时将数据存在磁盘上
- 长时间计算中存储检查点(中间结果),以便容器崩溃时,可以从上一次存储的检查点(中间结果)继续进行,而不是从头开始
- 作为两个容器的共享存储,使得第一个内容管理的容器可以将生成的页面存入其中,同时由一个 webserver 容器对外提供这些页面
- 默认情况下,emptyDir 数据卷被存储在 node(节点)的存储介质(机械硬盘、SSD、或者网络存储)上。此外,您可以设置 emptyDir.medium 字段为 “Memory”,此时 Kubernetes 将挂载一个 tmpfs(基于 RAM 的文件系统)。tmpfs 的读写速度非常快,但是与磁盘不一样,tmpfs 在节点重启后将被清空,且您向该 emptyDir 写入文件时,将消耗对应容器的内存限制。
nfs
-
描述
nfs 类型的数据卷可以加载 NFS(Network File System)到您的容器组/容器。容器组被移除时,将仅仅 umount(卸载)NFS 数据卷,NFS 中的数据仍将被保留。
- 可以在加载 NFS 数据卷前就在其中准备好数据;
- 可以在不同容器组之间共享数据;
- 可以被多个容器组加载并同时读写;
-
适用场景
- 存储日志文件
- MySQL的data目录(建议只在测试环境中)
- 用户上传的临时文件
cephfs
-
描述
cephfs 数据卷使得您可以挂载一个外部 CephFS 卷到您的容器组中。对于 kubernetes 而言,cephfs 与 nfs 的管理方式和行为完全相似,适用场景也相同。不同的仅仅是背后的存储介质。
-
适用场景
同 nfs 数据卷
hostPath
-
描述
hostPath 类型的数据卷将 Pod(容器组)所在节点的文件系统上某一个文件或文件夹挂载进容器组(容器)。
除了为 hostPath 指定 path 字段以外,您还可以为其指定 type 字段,可选的 type 字段描述如下:
Type字段取值 描述 空字符串(default)用于向后兼容,此时,kubernetes 在挂载 hostPath 数据卷前不会执行任何检查 DirectoryOrCreate 如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径上创建一个空文件夹,权限设置为 0755,与 kubelet 进程具备相同的 group 和 ownership Directory 指定 hostPath 路径必须存在,且是一个文件夹 FileOrCreate 如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径上创建一个空的文件,权限设置为 0644,与 kubelet 进程具备相同的 group 和 ownership File 指定 hostPath 路径必须存在,且是一个文件 Socket 指定 hostPath 路径必须存在,且是一个 Unix Socket CharDevice 指定 hostPath 路径必须存在,且是一个 character device BlockDevice 指定 hostPath 路径必须存在,且是一个 block device 警告
使用 hostPath 数据卷时,必须十分小心,因为:
- 不同节点上配置完全相同的容器组(例如同一个Deployment的容器组)可能执行结果不一样,因为不同节点上 hostPath 所对应的文件内容不同;
- Kubernetes 计划增加基于资源的调度,但这个特性将不会考虑对 hostPath 的支持
- hostPath 对应的文件/文件夹只有 root 可以写入。您要么在 privileged Container (opens new window)以 root 身份运行您的进程,要么修改与 hostPath 数据卷对应的节点上的文件/文件夹的权限
-
适用场景
绝大多数容器组并不需要使用 hostPath 数据卷,但是少数情况下,hostPath 数据卷非常有用:
- 某容器需要访问 Docker,可使用 hostPath 挂载宿主节点的 /var/lib/docker
- 在容器中运行 cAdvisor,使用 hostPath 挂载宿主节点的 /sys
configMap
-
描述
ConfigMap 提供了一种向容器组注入配置信息的途径。ConfigMap 中的数据可以被 Pod(容器组)中的容器作为一个数据卷挂载。
在数据卷中引用 ConfigMap 时:
- 您可以直接引用整个 ConfigMap 到数据卷,此时 ConfigMap 中的每一个 key 对应一个文件名,value 对应该文件的内容
- 您也可以只引用 ConfigMap 中的某一个名值对,此时可以将 key 映射成一个新的文件名
在k8s中,ConfigMap也是一种资源对象,可以被持久化存储到文件系统中,因此**可以通过在Pod中定义一个Volume并将其绑定到ConfigMap上,然后将Volume挂载到容器的挂载点上,容器就可以像访问本地文件系统一样访问ConfigMap中定义的数据了。 **这种方式可以方便地将诸如配置信息注入到容器中,从而实现应用程序的动态配置。
在挂载后,ConfigMap会以文件的形式出现在容器的指定路径下,并且文件名与ConfigMap的名称相同,文件内容就是ConfigMap中的数据。在容器中,可以像访问普通文件一样访问这个文件,从而读取ConfigMap中的数据。
-
适用场景
使用 ConfigMap 中的某一 key 作为文件名,对应 value 作为文件内容,替换 nginx 容器中的 /etc/nginx/conf.d/default.conf 配置文件。
secret
-
描述
secret 数据卷可以用来注入敏感信息(例如密码)到容器组。您可以将敏感信息存入 kubernetes secret 对象,并通过 Volume(数据卷)以文件的形式挂载到容器组(或容器)。secret 数据卷使用 tmpfs(基于 RAM 的文件系统)挂载。
将 Secret 数据卷挂载到容器时,如果该挂载点指定了 数据卷内子路径 (subPath),则该 Secret 被改变后,该容器挂载的内容仍然不变。
-
适用场景
- 将 HTTPS 证书存入 kubernets secret,并挂载到 /etc/nginx/conf.d/myhost.crt、/etc/nginx/conf.d/myhost.pem 路径,用来配置 nginx 的 HTTPS 证书