容器中的文件在磁盘上是临时存放的,这给在容器中运行较重要的应用带来一些问题。 当容器崩溃或停止时会出现一个问题。此时容器状态未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题。 跨所有容器设置和访问共享文件系统具有一定的挑战性。

Kubernetes 卷(Volume) 这一抽象概念能够解决这两个问题。

阅读本文前建议你熟悉一下 Pod

背景

Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同, 但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。

使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像 的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。 任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。

卷挂载在镜像中的指定路径下。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。

卷不能挂载到其他卷之上(不过存在一种使用 subPath 的相关机制),也不能与其他卷有硬链接。

卷类型

Kubernetes 支持下列类型的卷:

awsElasticBlockStore (已弃用)

在 Kubernetes 1.31 中,所有针对树内 awsElasticBlockStore 类型的操作都会被重定向到 ebs.csi.aws.com CSI 驱动。

AWSElasticBlockStore 树内存储驱动已在 Kubernetes v1.19 版本中废弃, 并在 v1.27 版本中被完全移除。

Kubernetes 项目建议你转为使用 AWS EBS 第三方存储驱动插件。

azureDisk (已弃用)

在 Kubernetes 1.31 中,所有针对树内 azureDisk 类型的操作都会被重定向到 disk.csi.azure.com CSI 驱动。

AzureDisk 树内存储驱动已在 Kubernetes v1.19 版本中废弃,并在 v1.27 版本中被完全移除。

Kubernetes 项目建议你转为使用 Azure Disk 第三方存储驱动插件。

azureFile (已弃用)

特性状态: Kubernetes v1.21 [deprecated]

azureFile 卷类型用来在 Pod 上挂载 Microsoft Azure 文件卷(File Volume)(SMB 2.1 和 3.0)。

更多详情请参考 azureFile 卷插件

azureFile CSI 迁移

特性状态: Kubernetes v1.26 [stable]

启用 azureFileCSIMigration 特性后,所有插件操作将从现有的树内插件重定向到 file.csi.azure.com 容器存储接口(CSI)驱动程序。要使用此特性,必须在集群中安装 Azure 文件 CSI 驱动程序, 并且 CSIMigrationAzureFile 特性门控 必须被启用。

Azure 文件 CSI 驱动尚不支持为同一卷设置不同的 fsgroup。 如果 CSIMigrationAzureFile 特性被启用,用不同的 fsgroup 来使用同一卷也是不被支持的。

azureFile CSI 迁移完成

特性状态: Kubernetes v1.21 [alpha]

要禁止控制器管理器和 kubelet 加载 azureFile 存储插件, 请将 InTreePluginAzureFileUnregister 标志设置为 true

cephfs(已移除)

Kubernetes 1.31 不包括 cephfs 卷类型。

cephfs 树内存储驱动在 Kubernetes v1.28 版本中被弃用,并在 v1.31 版本中被完全移除。

cinder(已弃用)

在 Kubernetes 1.31 中,所有针对树内 cinder 类型的操作都会被重定向到 cinder.csi.openstack.org CSI 驱动。

OpenStack Cinder 树内存储驱动已在 Kubernetes v1.11 版本中废弃, 并在 v1.26 版本中被完全移除。

Kubernetes 项目建议你转为使用 OpenStack Cinder 第三方存储驱动插件。

configMap

configMap 卷提供了向 Pod 注入配置数据的方法。 ConfigMap 对象中存储的数据可以被 configMap 类型的卷引用,然后被 Pod 中运行的容器化应用使用。

引用 configMap 对象时,你可以在卷中通过它的名称来引用。 你可以自定义 ConfigMap 中特定条目所要使用的路径。 下面的配置显示了如何将名为 log-config 的 ConfigMap 挂载到名为 configmap-pod 的 Pod 中:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: configmap-pod
  5. spec:
  6. containers:
  7. - name: test
  8. image: busybox:1.28
  9. command: ['sh', '-c', 'echo "The app is running!" && tail -f /dev/null']
  10. volumeMounts:
  11. - name: config-vol
  12. mountPath: /etc/config
  13. volumes:
  14. - name: config-vol
  15. configMap:
  16. name: log-config
  17. items:
  18. - key: log_level
  19. path: log_level

log-config ConfigMap 以卷的形式挂载,并且存储在 log_level 条目中的所有内容都被挂载到 Pod 的 /etc/config/log_level 路径下。 请注意,这个路径来源于卷的 mountPathlog_level 键对应的 path

说明:

  • 你必须先创建 ConfigMap, 才能使用它。
  • ConfigMap 总是以 readOnly 的模式挂载。
  • 容器以 subPath 卷挂载方式使用 ConfigMap 时,将无法接收 ConfigMap 的更新。
  • 文本数据挂载成文件时采用 UTF-8 字符编码。如果使用其他字符编码形式,可使用 binaryData 字段。

downwardAPI

downwardAPI 卷用于为应用提供 downward API 数据。 在这类卷中,所公开的数据以纯文本格式的只读文件形式存在。

说明:

容器以 subPath 卷挂载方式使用 downward API 时,在字段值更改时将不能接收到它的更新。

更多详细信息请参考通过文件将 Pod 信息呈现给容器

emptyDir

对于定义了 emptyDir 卷的 Pod,在 Pod 被指派到某节点时此卷会被创建。 就像其名称所表示的那样,emptyDir 卷最初是空的。尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。

说明:

容器崩溃并会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。

emptyDir 的一些用途:

  • 缓存空间,例如基于磁盘的归并排序。
  • 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  • 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。

emptyDir.medium 字段用来控制 emptyDir 卷的存储位置。 默认情况下,emptyDir 卷存储在该节点所使用的介质上; 此处的介质可以是磁盘、SSD 或网络存储,这取决于你的环境。 你可以将 emptyDir.medium 字段设置为 "Memory", 以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。 虽然 tmpfs 速度非常快,但是要注意它与磁盘不同, 并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。

你可以通过为默认介质指定大小限制,来限制 emptyDir 卷的存储容量。 此存储是从节点临时存储中分配的。 如果来自其他来源(如日志文件或镜像分层数据)的数据占满了存储,emptyDir 可能会在达到此限制之前发生存储容量不足的问题。

说明:

你可以指定内存作为介质的卷的大小,前提是集群中启用了 SizeMemoryBackedVolumes 特性门控 (自 Kubernetes 1.22 发布以来,此特性一直处于 Beta 阶段,并且默认启用)。 如果你未指定大小,内存作为介质的卷的大小根据节点可分配内存进行调整。

注意:

使用内存作为介质的 emptyDir 卷时, 请查阅此处, 了解有关资源管理方面的注意事项。

emptyDir 配置示例

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: test-pd
  5. spec:
  6. containers:
  7. - image: registry.k8s.io/test-webserver
  8. name: test-container
  9. volumeMounts:
  10. - mountPath: /cache
  11. name: cache-volume
  12. volumes:
  13. - name: cache-volume
  14. emptyDir:
  15. sizeLimit: 500Mi

fc (光纤通道)

fc 卷类型允许将现有的光纤通道块存储卷挂载到 Pod 中。 可以使用卷配置中的参数 targetWWNs 来指定单个或多个目标 WWN(World Wide Names)。 如果指定了多个 WWN,targetWWNs 期望这些 WWN 来自多路径连接。

说明:

你必须配置 FC SAN Zoning,以便预先向目标 WWN 分配和屏蔽这些 LUN(卷),这样 Kubernetes 主机才可以访问它们。

更多详情请参考 FC 示例

gcePersistentDisk(已弃用)

在 Kubernetes 1.31 中,所有针对树内 gcePersistentDisk 类型的操作都会被重定向到 pd.csi.storage.gke.io CSI 驱动。

gcePersistentDisk 源代码树内卷存储驱动在 Kubernetes v1.17 版本中被弃用,在 v1.28 版本中被完全移除。

Kubernetes 项目建议你转为使用 Google Compute Engine Persistent Disk CSI 第三方存储驱动插件。

GCE CSI 迁移

特性状态: Kubernetes v1.25 [stable]

启用 GCE PD 的 CSIMigration 特性后,所有插件操作将从现有的树内插件重定向到 pd.csi.storage.gke.io 容器存储接口(CSI)驱动程序。 为了使用此特性,必须在集群中上安装 GCE PD CSI 驱动程序

gitRepo (已弃用)

警告:

gitRepo 卷类型已经被弃用。

如果需要制备已挂载 Git 仓库的 Pod,你可以将 EmptyDir 卷挂载到 Init 容器 中, 使用 Git 命令完成仓库的克隆操作,然后将 EmptyDir 卷挂载到 Pod 的容器中。


你可以使用 ValidatingAdmissionPolicy 这类策略来限制在你的集群中使用 gitRepo 卷。 你可以使用以下通用表达语言(CEL)表达式作为策略的一部分,以拒绝使用 gitRepo 卷: has(object.spec.volumes) || !object.spec.volumes.exists(v, has(v.gitRepo))

gitRepo 卷是一个卷插件的例子。 该查卷挂载一个空目录,并将一个 Git 代码仓库克隆到这个目录中供 Pod 使用。

下面给出一个 gitRepo 卷的示例:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: server
  5. spec:
  6. containers:
  7. - image: nginx
  8. name: nginx
  9. volumeMounts:
  10. - mountPath: /mypath
  11. name: git-volume
  12. volumes:
  13. - name: git-volume
  14. gitRepo:
  15. repository: "git@somewhere:me/my-git-repository.git"
  16. revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

glusterfs(已移除)

Kubernetes 1.31 不包含 glusterfs 卷类型。

GlusterFS 树内存储驱动程序在 Kubernetes v1.25 版本中被弃用,然后在 v1.26 版本中被完全移除。

hostPath

hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用提供了强大的逃生舱。

警告:

使用 hostPath 类型的卷存在许多安全风险。如果可以,你应该尽量避免使用 hostPath 卷。 例如,你可以改为定义并使用 local PersistentVolume

如果你通过准入时的验证来限制对节点上特定目录的访问,这种限制只有在你额外要求所有 hostPath 卷的挂载都是只读的情况下才有效。如果你允许不受信任的 Pod 以读写方式挂载任意主机路径, 则该 Pod 中的容器可能会破坏可读写主机挂载卷的安全性。


无论 hostPath 卷是以只读还是读写方式挂载,使用时都需要小心,这是因为:

  • 访问主机文件系统可能会暴露特权系统凭证(例如 kubelet 的凭证)或特权 API(例如容器运行时套接字), 这些可以被用于容器逃逸或攻击集群的其他部分。
  • 具有相同配置的 Pod(例如基于 PodTemplate 创建的 Pod)可能会由于节点上的文件不同而在不同节点上表现出不同的行为。
  • hostPath 卷的用量不会被视为临时存储用量。 你需要自己监控磁盘使用情况,因为过多的 hostPath 磁盘使用量会导致节点上的磁盘压力。

hostPath 的一些用法有:

  • 运行一个需要访问节点级系统组件的容器 (例如一个将系统日志传输到集中位置的容器,使用只读挂载 /var/log 来访问这些日志)
  • 让存储在主机系统上的配置文件可以被静态 Pod 以只读方式访问;与普通 Pod 不同,静态 Pod 无法访问 ConfigMap。

hostPath 卷类型

除了必需的 path 属性外,你还可以选择为 hostPath 卷指定 type

type 的可用值有:

取值行为
‌””空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
Directory在给定路径上必须存在的目录。
FileOrCreate如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
File在给定路径上必须存在的文件。
Socket在给定路径上必须存在的 UNIX 套接字。
CharDevice(仅 Linux 节点) 在给定路径上必须存在的字符设备。
BlockDevice(仅 Linux 节点) 在给定路径上必须存在的块设备。

注意:

FileOrCreate 模式不会创建文件的父目录。如果挂载文件的父目录不存在,Pod 将启动失败。 为了确保这种模式正常工作,你可以尝试分别挂载目录和文件,如 hostPathFileOrCreate 示例所示。

下层主机上创建的某些文件或目录只能由 root 用户访问。 此时,你需要在特权容器中以 root 身份运行进程,或者修改主机上的文件权限,以便能够从 hostPath 卷读取数据(或将数据写入到 hostPath 卷)。

hostPath 配置示例

  1. ---
  2. # 此清单将主机上的 /data/foo 挂载为 hostpath-example-linux Pod 中运行的单个容器内的 /foo
  3. #
  4. # 容器中的挂载是只读的
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: hostpath-example-linux
  9. spec:
  10. os: { name: linux }
  11. nodeSelector:
  12. kubernetes.io/os: linux
  13. containers:
  14. - name: example-container
  15. image: registry.k8s.io/test-webserver
  16. volumeMounts:
  17. - mountPath: /foo
  18. name: example-volume
  19. readOnly: true
  20. volumes:
  21. - name: example-volume
  22. # 挂载 /data/foo,但仅当该目录已经存在时
  23. hostPath:
  24. path: /data/foo # 主机上的目录位置
  25. type: Directory # 此字段可选
  1. ---
  2. # 此清单将主机上的 C:\Data\foo 挂载为 hostpath-example-windows Pod 中运行的单个容器内的 C:\foo
  3. #
  4. # 容器中的挂载是只读的
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: hostpath-example-windows
  9. spec:
  10. os: { name: windows }
  11. nodeSelector:
  12. kubernetes.io/os: windows
  13. containers:
  14. - name: example-container
  15. image: microsoft/windowsservercore:1709
  16. volumeMounts:
  17. - name: example-volume
  18. mountPath: "C:\\foo"
  19. readOnly: true
  20. volumes:
  21. # 从主机挂载 C:\Data\foo,但仅当该目录已经存在时
  22. - name: example-volume
  23. hostPath:
  24. path: "C:\\Data\\foo" # 主机上的目录位置
  25. type: Directory # 此字段可选

hostPath FileOrCreate 配置示例

以下清单定义了一个 Pod,将 /var/local/aaa 挂载到 Pod 中的单个容器内。 如果节点上还没有路径 /var/local/aaa,kubelet 会创建这一目录,然后将其挂载到 Pod 中。

如果 /var/local/aaa 已经存在但不是一个目录,Pod 会失败。 此外,kubelet 还会尝试在该目录内创建一个名为 /var/local/aaa/1.txt 的文件(从主机的视角来看); 如果在该路径上已经存在某个东西且不是常规文件,则 Pod 会失败。

以下是清单示例:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: test-webserver
  5. spec:
  6. os: { name: linux }
  7. nodeSelector:
  8. kubernetes.io/os: linux
  9. containers:
  10. - name: test-webserver
  11. image: registry.k8s.io/test-webserver:latest
  12. volumeMounts:
  13. - mountPath: /var/local/aaa
  14. name: mydir
  15. - mountPath: /var/local/aaa/1.txt
  16. name: myfile
  17. volumes:
  18. - name: mydir
  19. hostPath:
  20. # 确保文件所在目录成功创建。
  21. path: /var/local/aaa
  22. type: DirectoryOrCreate
  23. - name: myfile
  24. hostPath:
  25. path: /var/local/aaa/1.txt
  26. type: FileOrCreate

image

特性状态: Kubernetes v1.31 [alpha]

image 卷源代表一个在 kubelet 主机上可用的 OCI 对象(容器镜像或工件)。

使用 image 卷源的一个例子是:

  1. pods/image-volumes.yaml
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: image-volume
  5. spec:
  6. containers:
  7. - name: shell
  8. command: ["sleep", "infinity"]
  9. image: debian
  10. volumeMounts:
  11. - name: volume
  12. mountPath: /volume
  13. volumes:
  14. - name: volume
  15. image:
  16. reference: quay.io/crio/artifact:v1
  17. pullPolicy: IfNotPresent

此卷在 Pod 启动时基于提供的 pullPolicy 值进行解析:

Always

kubelet 始终尝试拉取此引用。如果拉取失败,kubelet 会将 Pod 设置为 Failed

Never

kubelet 从不拉取此引用,仅使用本地镜像或工件。 如果本地没有任何镜像层存在,或者该镜像的清单未被缓存,则 Pod 会变为 Failed

IfNotPresent

如果引用在磁盘上不存在,kubelet 会进行拉取。 如果引用不存在且拉取失败,则 Pod 会变为 Failed

如果 Pod 被删除并重新创建,此卷会被重新解析,这意味着在 Pod 重新创建时将可以访问新的远程内容。 在 Pod 启动期间解析或拉取镜像失败将导致容器无法启动,并可能显著增加延迟。 如果失败,将使用正常的卷回退进行重试,并输出 Pod 失败的原因和相关消息。

此卷可以挂载的对象类型由主机上的容器运行时实现负责定义,至少必须包含容器镜像字段所支持的所有有效类型。 OCI 对象将以只读方式被挂载到单个目录(spec.containers[*].volumeMounts.mountPath)中。 在 Linux 上,容器运行时通常还会挂载阻止文件执行(noexec)的卷。

此外:

  • 不支持容器使用子路径挂载(spec.containers[*].volumeMounts.subpath)。
  • spec.securityContext.fsGroupChangePolicy 字段对这种卷没有效果。
  • AlwaysPullImages 准入控制器也适用于此卷源, 就像适用于容器镜像一样。

image 类型可用的字段如下:

reference

要使用的工件引用。例如,你可以指定 registry.k8s.io/conformance:v1.31.0 来加载 Kubernetes 合规性测试镜像中的文件。其行为与 pod.spec.containers[*].image 相同。 拉取 Secret 的组装方式与容器镜像所用的方式相同,即通过查找节点凭据、服务账户镜像拉取 Secret 和 Pod 规约镜像拉取 Secret。此字段是可选的,允许更高层次的配置管理在 Deployment 和 StatefulSet 这类工作负载控制器中默认使用或重载容器镜像。 参阅容器镜像更多细节

pullPolicy

拉取 OCI 对象的策略。可能的值为:AlwaysNeverIfNotPresent。 如果指定了 :latest 标记,则默认为 Always,否则默认为 IfNotPresent

有关如何使用卷源的更多细节, 请参见 Pod 使用镜像卷示例。

iscsi

iscsi 卷能将 iSCSI (基于 IP 的 SCSI) 卷挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,iscsi 卷的内容在删除 Pod 时会被保留,卷只是被卸载。 这意味着 iscsi 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

说明:

在使用 iSCSI 卷之前,你必须拥有自己的 iSCSI 服务器,并在上面创建卷。

iSCSI 的一个特点是它可以同时被多个用户以只读方式挂载。 这意味着你可以用数据集预先填充卷,然后根据需要在尽可能多的 Pod 上使用它。 不幸的是,iSCSI 卷只能由单个使用者以读写模式挂载。不允许同时写入。

更多详情请参考 iSCSI 示例

local

local 卷所代表的是某个被挂载的本地存储设备,例如磁盘、分区或者目录。

local 卷只能用作静态创建的持久卷。不支持动态配置。

hostPath 卷相比,local 卷能够以持久和可移植的方式使用,而无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 的节点亲和性配置,就能了解卷的节点约束。

然而,local 卷仍然取决于底层节点的可用性,并不适合所有应用程序。 如果节点变得不健康,那么 local 卷也将变得不可被 Pod 访问。使用它的 Pod 将不能运行。 使用 local 卷的应用程序必须能够容忍这种可用性的降低,以及因底层磁盘的耐用性特征而带来的潜在的数据丢失风险。

下面是一个使用 local 卷和 nodeAffinity 的持久卷示例:

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: example-pv
  5. spec:
  6. capacity:
  7. storage: 100Gi
  8. volumeMode: Filesystem
  9. accessModes:
  10. - ReadWriteOnce
  11. persistentVolumeReclaimPolicy: Delete
  12. storageClassName: local-storage
  13. local:
  14. path: /mnt/disks/ssd1
  15. nodeAffinity:
  16. required:
  17. nodeSelectorTerms:
  18. - matchExpressions:
  19. - key: kubernetes.io/hostname
  20. operator: In
  21. values:
  22. - example-node

使用 local 卷时,你需要设置 PersistentVolume 对象的 nodeAffinity 字段。 Kubernetes 调度器使用 PersistentVolume 的 nodeAffinity 信息来将使用 local 卷的 Pod 调度到正确的节点。

PersistentVolume 对象的 volumeMode 字段可被设置为 “Block” (而不是默认值 “Filesystem”),以将 local 卷作为原始块设备暴露出来。

使用 local 卷时,建议创建一个 StorageClass 并将其 volumeBindingMode 设置为 WaitForFirstConsumer。要了解更多详细信息,请参考 local StorageClass 示例。 延迟卷绑定的操作可以确保 Kubernetes 在为 PersistentVolumeClaim 作出绑定决策时,会评估 Pod 可能具有的其他节点约束,例如:如节点资源需求、节点选择器、Pod 亲和性和 Pod 反亲和性。

你可以在 Kubernetes 之外单独运行静态驱动以改进对 local 卷的生命周期管理。 请注意,此驱动尚不支持动态配置。 有关如何运行外部 local 卷驱动,请参考 local 卷驱动用户指南

说明:

如果不使用外部静态驱动来管理卷的生命周期,用户需要手动清理和删除 local 类型的持久卷。

nfs

nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: test-pd
  5. spec:
  6. containers:
  7. - image: registry.k8s.io/test-webserver
  8. name: test-container
  9. volumeMounts:
  10. - mountPath: /my-nfs-data
  11. name: test-volume
  12. volumes:
  13. - name: test-volume
  14. nfs:
  15. server: my-nfs-server.example.com
  16. path: /my-nfs-volume
  17. readOnly: true

说明:

在使用 NFS 卷之前,你必须运行自己的 NFS 服务器并将目标 share 导出备用。

还需要注意,不能在 Pod spec 中指定 NFS 挂载可选项。 可以选择设置服务端的挂载可选项,或者使用 /etc/nfsmount.conf。 此外,还可以通过允许设置挂载可选项的持久卷挂载 NFS 卷。

如需了解用持久卷挂载 NFS 卷的示例,请参考 NFS 示例

persistentVolumeClaim

persistentVolumeClaim 卷用来将持久卷(PersistentVolume)挂载到 Pod 中。 持久卷申领(PersistentVolumeClaim)是用户在不知道特定云环境细节的情况下“申领”持久存储(例如 iSCSI 卷)的一种方法。

更多详情请参考持久卷

portworxVolume(已弃用)

特性状态: Kubernetes v1.25 [deprecated]

portworxVolume 是一个可伸缩的块存储层,能够以超融合(hyperconverged)的方式与 Kubernetes 一起运行。 Portworx 支持对服务器上存储的指纹处理、基于存储能力进行分层以及跨多个服务器整合存储容量。 Portworx 可以以 in-guest 方式在虚拟机中运行,也可以在裸金属 Linux 节点上运行。

portworxVolume 类型的卷可以通过 Kubernetes 动态创建,也可以预先配备并在 Pod 内引用。 下面是一个引用预先配备的 Portworx 卷的示例 Pod:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: test-portworx-volume-pod
  5. spec:
  6. containers:
  7. - image: registry.k8s.io/test-webserver
  8. name: test-container
  9. volumeMounts:
  10. - mountPath: /mnt
  11. name: pxvol
  12. volumes:
  13. - name: pxvol
  14. # 此 Portworx 卷必须已经存在
  15. portworxVolume:
  16. volumeID: "pxvol"
  17. fsType: "<fs-type>"

说明:

在 Pod 中使用 portworxVolume 之前,你要确保有一个名为 pxvol 的 PortworxVolume 存在。

更多详情可以参考 Portworx 卷

Portworx CSI 迁移

特性状态: Kubernetes v1.25 [beta]

默认情况下,Kubernetes 1.31 尝试将传统的 Portworx 卷迁移为使用 CSI。 (Portworx 的 CSI 迁移自 Kubernetes v1.23 版本以来一直可用,但从 v1.31 版本开始才默认启用)。 如果你想禁用自动迁移,可以将 CSIMigrationPortworx 特性门控 设置为 false; 你需要在 kube-controller-manager 每个相关的 kubelet 上进行此更改。

它将所有插件操作不再指向树内插件(In-Tree Plugin),转而指向 pxd.portworx.com 容器存储接口(Container Storage Interface,CSI)驱动。 Portworx CSI 驱动程序必须安装在集群上。

投射(projected)

投射卷能将若干现有的卷来源映射到同一目录上。更多详情请参考投射卷

rbd(已移除)

Kubernetes 1.31 不包括 rbd 卷类型。

Rados 块设备(RBD) 树内存储驱动及其 CSI 迁移支持在 Kubernetes v1.28 版本中被弃用,并在 v1.31 版本中被完全移除。

secret

secret 卷用来给 Pod 传递敏感信息,例如密码。你可以将 Secret 存储在 Kubernetes API 服务器上,然后以文件的形式挂载到 Pod 中,无需直接与 Kubernetes 耦合。 secret 卷由 tmpfs(基于 RAM 的文件系统)提供存储,因此它们永远不会被写入非易失性(持久化的)存储器。

说明:

  • 使用前你必须在 Kubernetes API 中创建 Secret。
  • Secret 总是以 readOnly 的模式挂载。
  • 容器以 subPath 卷挂载方式使用 Secret 时,将无法接收 Secret 的更新。

更多详情请参考配置 Secrets

vsphereVolume(已弃用)

说明:

Kubernetes 项目建议转为使用 vSphere CSI 树外存储驱动。

vsphereVolume 用来将 vSphere VMDK 卷挂载到你的 Pod 中。 在卸载卷时,卷的内容会被保留。 vSphereVolume 卷类型支持 VMFS 和 VSAN 数据仓库。

进一步信息可参考 vSphere 卷

vSphere CSI 迁移

特性状态: Kubernetes v1.26 [stable]

在 Kubernetes 1.31 中,对树内 vsphereVolume 类的所有操作都会被重定向至 csi.vsphere.vmware.com CSI 驱动程序。

vSphere CSI 驱动必须安装到集群上。 你可以在 VMware 的文档页面迁移树内 vSphere 卷插件到 vSphere 容器存储插件 中找到有关如何迁移树内 vsphereVolume 的其他建议。 如果未安装 vSphere CSI 驱动程序,则无法对由树内 vsphereVolume 类型创建的 PV 执行卷操作。

你必须运行 vSphere 7.0u2 或更高版本才能迁移到 vSphere CSI 驱动程序。

如果你正在运行 Kubernetes v1.31,请查阅该 Kubernetes 版本的文档。

说明:

vSphere CSI 驱动不支持内置 vsphereVolume 的以下 StorageClass 参数:

  • diskformat
  • hostfailurestotolerate
  • forceprovisioning
  • cachereservation
  • diskstripes
  • objectspacereservation
  • iopslimit

使用这些参数创建的现有卷将被迁移到 vSphere CSI 驱动,不过使用 vSphere CSI 驱动所创建的新卷都不会理会这些参数。

vSphere CSI 迁移完成

特性状态: Kubernetes v1.19 [beta]

为了避免控制器管理器和 kubelet 加载 vsphereVolume 插件,你需要将 InTreePluginvSphereUnregister 特性设置为 true。你还必须在所有工作节点上安装 csi.vsphere.vmware.com CSI 驱动。

使用 subPath

有时,在单个 Pod 中共享卷以供多方使用是很有用的。 volumeMounts[*].subPath 属性可用于指定所引用的卷内的子路径,而不是其根路径。

下面例子展示了如何配置某包含 LAMP 堆栈(Linux Apache MySQL PHP)的 Pod 使用同一共享卷。 此示例中的 subPath 配置不建议在生产环境中使用。 PHP 应用的代码和相关数据映射到卷的 html 文件夹,MySQL 数据库存储在卷的 mysql 文件夹中:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: my-lamp-site
  5. spec:
  6. containers:
  7. - name: mysql
  8. image: mysql
  9. env:
  10. - name: MYSQL_ROOT_PASSWORD
  11. value: "rootpasswd"
  12. volumeMounts:
  13. - mountPath: /var/lib/mysql
  14. name: site-data
  15. subPath: mysql
  16. - name: php
  17. image: php:7.0-apache
  18. volumeMounts:
  19. - mountPath: /var/www/html
  20. name: site-data
  21. subPath: html
  22. volumes:
  23. - name: site-data
  24. persistentVolumeClaim:
  25. claimName: my-lamp-site-data

使用带有扩展环境变量的 subPath

特性状态: Kubernetes v1.17 [stable]

使用 subPathExpr 字段可以基于 downward API 环境变量来构造 subPath 目录名。 subPathsubPathExpr 属性是互斥的。

在这个示例中,Pod 使用 subPathExprhostPath/var/log/pods 中创建目录 pod1hostPath 卷采用来自 downwardAPI 的 Pod 名称生成目录名。 宿主机目录 /var/log/pods/pod1 被挂载到容器的 /logs 中。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: pod1
  5. spec:
  6. containers:
  7. - name: container1
  8. env:
  9. - name: POD_NAME
  10. valueFrom:
  11. fieldRef:
  12. apiVersion: v1
  13. fieldPath: metadata.name
  14. image: busybox:1.28
  15. command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
  16. volumeMounts:
  17. - name: workdir1
  18. mountPath: /logs
  19. # 包裹变量名的是小括号,而不是大括号
  20. subPathExpr: $(POD_NAME)
  21. restartPolicy: Never
  22. volumes:
  23. - name: workdir1
  24. hostPath:
  25. path: /var/log/pods

资源

emptyDir 卷的存储介质(例如磁盘、SSD 等)是由保存 kubelet 数据的根目录(通常是 /var/lib/kubelet)的文件系统的介质确定。 Kubernetes 对 emptyDir 卷或者 hostPath 卷可以消耗的空间没有限制,容器之间或 Pod 之间也没有隔离。

要了解如何使用资源规约来请求空间, 可参考如何管理资源

树外(Out-of-Tree)卷插件

Out-of-Tree 卷插件包括容器存储接口(CSI)和 FlexVolume(已弃用)。它们使存储供应商能够创建自定义存储插件,而无需将插件源码添加到 Kubernetes 代码仓库。

以前,所有卷插件(如上面列出的卷类型)都是“树内(In-Tree)”的。 “树内”插件是与 Kubernetes 的核心组件一同构建、链接、编译和交付的。 这意味着向 Kubernetes 添加新的存储系统(卷插件)需要将代码合并到 Kubernetes 核心代码库中。

CSI 和 FlexVolume 都允许独立于 Kubernetes 代码库开发卷插件,并作为扩展部署(安装)在 Kubernetes 集群上。

对于希望创建树外(Out-Of-Tree)卷插件的存储供应商, 请参考卷插件常见问题

CSI

容器存储接口 (CSI) 为容器编排系统(如 Kubernetes)定义标准接口,以将任意存储系统暴露给它们的容器工作负载。

更多详情请阅读 CSI 设计方案

说明:

Kubernetes v1.13 废弃了对 CSI 规范版本 0.2 和 0.3 的支持,并将在以后的版本中删除。

说明:

CSI 驱动可能并非兼容所有的 Kubernetes 版本。 请查看特定 CSI 驱动的文档,以了解各个 Kubernetes 版本所支持的部署步骤以及兼容性列表。

一旦在 Kubernetes 集群上部署了 CSI 兼容卷驱动程序,用户就可以使用 csi 卷类型来挂接、挂载 CSI 驱动所提供的卷。

csi 卷可以在 Pod 中以三种方式使用:

存储管理员可以使用以下字段来配置 CSI 持久卷:

  • driver:指定要使用的卷驱动名称的字符串值。 这个值必须与 CSI 驱动程序在 GetPluginInfoResponse 中返回的值相对应;该接口定义在 CSI 规范中。 Kubernetes 使用所给的值来标识要调用的 CSI 驱动程序;CSI 驱动程序也使用该值来辨识哪些 PV 对象属于该 CSI 驱动程序。

  • volumeHandle:唯一标识卷的字符串值。 该值必须与 CSI 驱动在 CreateVolumeResponsevolume_id 字段中返回的值相对应;接口定义在 CSI 规范 中。 在所有对 CSI 卷驱动程序的调用中,引用该 CSI 卷时都使用此值作为 volume_id 参数。

  • readOnly:一个可选的布尔值,指示通过 ControllerPublished 关联该卷时是否设置该卷为只读。默认值是 false。 该值通过 ControllerPublishVolumeRequest 中的 readonly 字段传递给 CSI 驱动。

  • fsType:如果 PV 的 VolumeModeFilesystem,那么此字段指定挂载卷时应该使用的文件系统。 如果卷尚未格式化,并且支持格式化,此值将用于格式化卷。 此值可以通过 ControllerPublishVolumeRequestNodeStageVolumeRequestNodePublishVolumeRequestVolumeCapability 字段传递给 CSI 驱动。

  • volumeAttributes:一个字符串到字符串的映射表,用来设置卷的静态属性。 该映射必须与 CSI 驱动程序返回的 CreateVolumeResponse 中的 volume.attributes 字段的映射相对应; CSI 规范中有相应的定义。 该映射通过ControllerPublishVolumeRequestNodeStageVolumeRequestNodePublishVolumeRequest 中的 volume_context 字段传递给 CSI 驱动。

  • controllerPublishSecretRef:对包含敏感信息的 Secret 对象的引用; 该敏感信息会被传递给 CSI 驱动来完成 CSI ControllerPublishVolumeControllerUnpublishVolume 调用。 此字段是可选的;在不需要 Secret 时可以是空的。 如果 Secret 包含多个 Secret 条目,则所有的 Secret 条目都会被传递。

  • nodeExpandSecretRef:对包含敏感信息的 Secret 对象的引用, 该信息会传递给 CSI 驱动以完成 CSI NodeExpandVolume 调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 包含多个 Secret 条目,则传递所有 Secret 条目。 当你为节点初始化的卷扩展配置 Secret 数据时,kubelet 会通过 NodeExpandVolume() 调用将该数据传递给 CSI 驱动。所有受支持的 Kubernetes 版本都提供 nodeExpandSecretRef 字段, 并且默认可用。Kubernetes v1.25 之前的版本不包括此支持。 为每个 kube-apiserver 和每个节点上的 kubelet 启用名为 CSINodeExpandSecret特性门控。 自 Kubernetes 1.27 版本起,此特性已默认启用,无需显式启用特性门控。 在节点初始化的存储大小调整操作期间,你还必须使用支持或需要 Secret 数据的 CSI 驱动。

  • nodePublishSecretRef:对包含敏感信息的 Secret 对象的引用。 该信息传递给 CSI 驱动来完成 CSI NodePublishVolume 调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 对象包含多个 Secret 条目,则传递所有 Secret 条目。

  • nodeStageSecretRef:对包含敏感信息的 Secret 对象的引用, 该信息会传递给 CSI 驱动以完成 CSI NodeStageVolume 调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 包含多个 Secret 条目,则传递所有 Secret 条目。

CSI 原始块卷支持

特性状态: Kubernetes v1.18 [stable]

具有外部 CSI 驱动程序的供应商能够在 Kubernetes 工作负载中实现原始块卷支持。

你可以和以前一样, 安装自己的带有原始块卷支持的 PV/PVC, 采用 CSI 对此过程没有影响。

CSI 临时卷

特性状态: Kubernetes v1.25 [stable]

你可以直接在 Pod 规约中配置 CSI 卷。采用这种方式配置的卷都是临时卷, 无法在 Pod 重新启动后继续存在。 进一步的信息可参阅临时卷

有关如何开发 CSI 驱动的更多信息,请参考 kubernetes-csi 文档

Windows CSI 代理

特性状态: Kubernetes v1.22 [stable]

CSI 节点插件需要执行多种特权操作,例如扫描磁盘设备和挂载文件系统等。 这些操作在每个宿主机操作系统上都是不同的。对于 Linux 工作节点而言,容器化的 CSI 节点插件通常部署为特权容器。对于 Windows 工作节点而言,容器化 CSI 节点插件的特权操作是通过 csi-proxy 来支持的。csi-proxy 是一个由社区管理的、独立的可执行二进制文件, 需要被预安装到每个 Windows 节点上。

要了解更多的细节,可以参考你要部署的 CSI 插件的部署指南。

从树内插件迁移到 CSI 驱动程序

特性状态: Kubernetes v1.25 [stable]

CSIMigration 特性针对现有树内插件的操作会被定向到相应的 CSI 插件(应已安装和配置)。 因此,操作员在过渡到取代树内插件的 CSI 驱动时,无需对现有存储类、PV 或 PVC(指树内插件)进行任何配置更改。

说明:

即使你针对这种卷完成了 CSI 迁移且你升级到不再内置对这种存储类别的支持的 Kubernetes 版本, 现有的由树内卷插件所创建的 PV 在未来无需进行任何配置更改就可以使用,

作为迁移的一部分,你或其他集群管理员必须安装和配置适用于该存储的 CSI 驱动。 Kubernetes 不会为你安装该软件。


在完成迁移之后,你也可以定义新的 PVC 和 PV,引用原来的、内置的集成存储。 只要你安装并配置了适当的 CSI 驱动,即使是全新的卷,PV 的创建仍然可以继续工作。 实际的存储管理现在通过 CSI 驱动来进行。

所支持的操作和特性包括:配备(Provisioning)/删除、挂接(Attach)/解挂(Detach)、 挂载(Mount)/卸载(Unmount)和调整卷大小。

上面的卷类型节列出了支持 CSIMigration 并已实现相应 CSI 驱动程序的树内插件。

下面是支持 Windows 节点上持久性存储的树内插件:

flexVolume(已弃用)

特性状态: Kubernetes v1.23 [deprecated]

FlexVolume 是一个使用基于 exec 的模型来与驱动程序对接的树外插件接口。 用户必须在每个节点上的预定义卷插件路径中安装 FlexVolume 驱动程序可执行文件,在某些情况下,控制平面节点中也要安装。

Pod 通过 flexvolume 树内插件与 FlexVolume 驱动程序交互。 更多详情请参考 FlexVolume README 文档。

下面的 FlexVolume 插件 以 PowerShell 脚本的形式部署在宿主机系统上,支持 Windows 节点:

说明:

FlexVolume 已被弃用。推荐使用树外 CSI 驱动来将外部存储整合进 Kubernetes。

FlexVolume 驱动的维护者应开发一个 CSI 驱动并帮助用户从 FlexVolume 驱动迁移到 CSI。 FlexVolume 用户应迁移工作负载以使用对等的 CSI 驱动。

挂载卷的传播

挂载卷的传播能力允许将容器安装的卷共享到同一 Pod 中的其他容器,甚至共享到同一节点上的其他 Pod。

卷的挂载传播特性由 containers[*].volumeMounts 中的 mountPropagation 字段控制。 它的值包括:

  • None - 此卷挂载将不会感知到主机后续在此卷或其任何子目录上执行的挂载变化。 类似的,容器所创建的卷挂载在主机上是不可见的。这是默认模式。

    该模式等同于 mount(8) 中描述的 rprivate 挂载传播选项。

    然而,当 rprivate 传播选项不适用时,CRI 运行时可以转为选择 rslave 挂载传播选项 (即 HostToContainer)。当挂载源包含 Docker 守护进程的根目录(/var/lib/docker)时, cri-dockerd (Docker) 已知可以选择 rslave 挂载传播选项。

  • HostToContainer - 此卷挂载将会感知到主机后续针对此卷或其任何子目录的挂载操作。

    换句话说,如果主机在此挂载卷中挂载任何内容,容器将能看到它被挂载在那里。

    类似的,配置了 Bidirectional 挂载传播选项的 Pod 如果在同一卷上挂载了内容,挂载传播设置为 HostToContainer 的容器都将能看到这一变化。

    该模式等同于 mount(8)中描述的 rslave 挂载传播选项。

  • Bidirectional - 这种卷挂载和 HostToContainer 挂载表现相同。 另外,容器创建的卷挂载将被传播回至主机和使用同一卷的所有 Pod 的所有容器。

    该模式等同于 mount(8) 中描述的 rshared 挂载传播选项。

    警告:

    Bidirectional 形式的挂载传播可能比较危险。 它可以破坏主机操作系统,因此它只被允许在特权容器中使用。 强烈建议你熟悉 Linux 内核行为。 此外,由 Pod 中的容器创建的任何卷挂载必须在终止时由容器销毁(卸载)。

只读挂载

通过将 .spec.containers[].volumeMounts[].readOnly 字段设置为 true 可以使挂载只读。 这不会使卷本身只读,但该容器将无法写入此卷。 Pod 中的其他容器可以以读写方式挂载同一个卷。

在 Linux 上,只读挂载默认不会以递归方式只读。 假如有一个 Pod 将主机的 /mnt 挂载为 hostPath 卷。 如果在 /mnt/<SUBMOUNT> 上有另一个以读写方式挂载的文件系统(如 tmpfs、NFS 或 USB 存储), 即使挂载本身被指定为只读,挂载到容器中的卷 /mnt/<SUBMOUNT> 也是可写的。

递归只读挂载

特性状态: Kubernetes v1.30 [alpha]

通过为 kubelet 和 kube-apiserver 设置 RecursiveReadOnlyMounts 特性门控, 并为 Pod 设置 .spec.containers[].volumeMounts[].recursiveReadOnly 字段, 递归只读挂载可以被启用。

允许的值为:

  • Disabled(默认):无效果。

  • Enabled:使挂载递归只读。需要满足以下所有要求:

    • readOnly 设置为 true
    • mountPropagation 不设置,或设置为 None
    • 主机运行 Linux 内核 v5.12 或更高版本
    • CRI 级别的容器运行时支持递归只读挂载
    • OCI 级别的容器运行时支持递归只读挂载

    如果其中任何一个不满足,递归只读挂载将会失败。

  • IfPossible:尝试应用 Enabled,如果内核或运行时类不支持该特性,则回退为 Disabled

示例:

  1. storage/rro.yaml
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: rro
  5. spec:
  6. volumes:
  7. - name: mnt
  8. hostPath:
  9. # tmpfs 被挂载到 /mnt/tmpfs 上
  10. path: /mnt
  11. containers:
  12. - name: busybox
  13. image: busybox
  14. args: ["sleep", "infinity"]
  15. volumeMounts:
  16. # /mnt-rro/tmpfs 不可写入
  17. - name: mnt
  18. mountPath: /mnt-rro
  19. readOnly: true
  20. mountPropagation: None
  21. recursiveReadOnly: Enabled
  22. # /mnt-ro/tmpfs 可写入
  23. - name: mnt
  24. mountPath: /mnt-ro
  25. readOnly: true
  26. # /mnt-rw/tmpfs 可写入
  27. - name: mnt
  28. mountPath: /mnt-rw

当此属性被 kubelet 和 kube-apiserver 识别到时, .status.containerStatuses[].volumeMounts[].recursiveReadOnly 字段将被设置为 EnabledDisabled

实现

说明: 本部分链接到提供 Kubernetes 所需功能的第三方项目。Kubernetes 项目作者不负责这些项目。此页面遵循CNCF 网站指南,按字母顺序列出项目。要将项目添加到此列表中,请在提交更改之前阅读内容指南

以下容器运行时已知支持递归只读挂载。

CRI 级别:

OCI 级别:

  • runc,自 v1.1 起
  • crun,自 v1.8.6 起

接下来

参考使用持久卷部署 WordPress 和 MySQL 示例。