卷
Kubernetes 卷 提供了一种方式,使得 Pod 中的容器可以通过文件系统访问和共享数据。你可以使用不同类型的卷来实现不同的目的,例如:
- 根据 ConfigMap 或 Secret 填充配置文件
- 为 Pod 提供一些临时暂存空间
- 在同一个 Pod 中的两个不同容器之间共享文件系统
- 在两个不同的 Pod 之间共享文件系统(即使这些 Pod 运行在不同的节点上)
- 持久存储数据,即使 Pod 重启或被替换,数据仍然可用
- 根据容器所在 Pod 的详细信息,将配置信息传递给容器中运行的应用程序(例如:告诉 边车容器 Pod 正在哪个命名空间中运行)
- 提供对不同容器镜像中的数据的只读访问
数据共享可以在容器内部的不同本地进程之间、不同容器之间或不同 Pod 之间进行。
为什么卷很重要
数据持久性: 容器中的磁盘文件是短暂的,这给在容器中运行的非简单应用程序带来了一些问题。一个问题是当容器崩溃或停止时,容器状态不会被保存,因此在容器生命周期中创建或修改的所有文件都会丢失。崩溃后,kubelet 会以干净的状态重新启动容器。
共享存储: 另一个问题是当多个容器在 `Pod` 中运行并需要共享文件时。在所有容器之间设置和访问共享文件系统可能很困难。
Kubernetes 卷 抽象可以帮助你解决这两个问题。
在你了解卷、PersistentVolume 和 PersistentVolumeClaim 之前,你应该阅读 Pod,并确保你理解 Kubernetes 如何使用 Pod 运行容器。
卷的工作原理
Kubernetes 支持多种卷类型。一个 Pod 可以同时使用任意数量的卷类型。临时卷 类型的生命周期与特定的 Pod 绑定,而 持久卷 存在于任何单个 Pod 的生命周期之外。当 Pod 不再存在时,Kubernetes 会销毁临时卷;但是,Kubernetes 不会销毁持久卷。对于给定 Pod 中的任何类型的卷,数据在容器重启后仍会保留。
从本质上讲,卷是一个目录,可能包含一些数据,可供 Pod 中的容器访问。该目录如何生成,其背后的介质以及其内容由所使用的特定卷类型决定。
要使用卷,请在 `.spec.volumes` 中指定要为 Pod 提供的卷,并在 `.spec.containers[*].volumeMounts` 中声明将这些卷挂载到容器中的位置。
当 Pod 启动时,容器中的进程会看到一个由 容器镜像 的初始内容以及挂载在容器内部的卷(如果已定义)组成的文件系统视图。进程会看到一个最初与容器镜像内容匹配的根文件系统。对该文件系统层次结构内任何(如果允许)的写入都会影响该进程在后续文件系统访问时所看到的视图。卷被挂载到容器文件系统内的指定路径。对于 Pod 中定义的每个容器,你必须独立指定该容器使用的每个卷的挂载位置。
卷不能挂载到其他卷内部(但请参阅使用 subPath 来了解相关的机制)。此外,一个卷不能包含指向不同卷中任何内容的硬链接。
卷类型
Kubernetes 支持多种类型的卷。
awsElasticBlockStore (已弃用)
在 Kubernetes 1.34 中,树内 `awsElasticBlockStore` 类型的所有操作都被重定向到 `ebs.csi.aws.com` CSI 驱动。
AWSElasticBlockStore 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.27 版本中完全移除。
Kubernetes 项目建议你改用 AWS EBS 第三方存储驱动。
azureDisk (已弃用)
在 Kubernetes 1.34 中,树内 `azureDisk` 类型的所有操作都被重定向到 `disk.csi.azure.com` CSI 驱动。
AzureDisk 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.27 版本中完全移除。
Kubernetes 项目建议你改用 Azure Disk 第三方存储驱动。
azureFile (已弃用)
在 Kubernetes 1.34 中,树内 `azureFile` 类型的所有操作都被重定向到 `file.csi.azure.com` CSI 驱动。
AzureFile 树内存储驱动在 Kubernetes v1.21 版本中被弃用,然后在 v1.30 版本中完全移除。
Kubernetes 项目建议你改用 Azure File 第三方存储驱动。
cephfs (已移除)
Kubernetes 1.34 不包含 `cephfs` 卷类型。
`cephfs` 树内存储驱动在 Kubernetes v1.28 版本中被弃用,然后在 v1.31 版本中完全移除。
cinder (已弃用)
在 Kubernetes 1.34 中,树内 `cinder` 类型的所有操作都被重定向到 `cinder.csi.openstack.org` CSI 驱动。
OpenStack Cinder 树内存储驱动在 Kubernetes v1.11 版本中被弃用,然后在 v1.26 版本中完全移除。
Kubernetes 项目建议你改用 OpenStack Cinder 第三方存储驱动。
configMap
ConfigMap 提供了一种将配置数据注入 Pod 的方式。ConfigMap 中存储的数据可以在 `configMap` 类型的卷中引用,然后由在 Pod 中运行的容器化应用程序使用。
当引用 ConfigMap 时,你需要在卷中提供 ConfigMap 的名称。你可以自定义 ConfigMap 中特定条目的使用路径。以下配置展示了如何将 `log-config` ConfigMap 挂载到名为 `configmap-pod` 的 Pod 上。
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox:1.28
command: ['sh', '-c', 'echo "The app is running!" && tail -f /dev/null']
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: log-config
items:
- key: log_level
path: log_level.conf
`log-config` ConfigMap 作为卷挂载,其 `log_level` 条目中存储的所有内容都被挂载到 Pod 的路径 `/etc/config/log_level.conf`。请注意,此路径是根据卷的 `mountPath` 和以 `log_level` 为键的 `path` 派生出来的。
注意
你必须创建 ConfigMap 才能使用它。
ConfigMap 总是以 `readOnly` 方式挂载。
使用 ConfigMap 作为 `subPath` 卷挂载的容器在 ConfigMap 更改时不会收到更新。
文本数据以 UTF-8 字符编码的文件形式公开。对于其他字符编码,请使用 `binaryData`。
downwardAPI
一个 `downwardAPI` 卷使 downward API 数据可供应用程序使用。在卷内,你可以找到以纯文本格式的只读文件形式公开的数据。
注意
当字段值发生变化时,使用 downward API 作为 `subPath` 卷挂载的容器不会收到更新。请参阅通过文件向容器暴露 Pod 信息了解更多信息。
emptyDir
对于定义了 `emptyDir` 卷的 Pod,当 Pod 分配到节点时,该卷就会被创建。顾名思义,`emptyDir` 卷最初是空的。Pod 中的所有容器都可以在 `emptyDir` 卷中读写相同的文件,尽管该卷可以在每个容器中挂载到相同或不同的路径。当 Pod 因任何原因从节点中移除时,`emptyDir` 中的数据将被永久删除。
注意
容器崩溃不会将 Pod 从节点中移除。`emptyDir` 卷中的数据在容器崩溃后仍然安全。`emptyDir` 的一些用途是:
- 暂存空间,例如用于基于磁盘的归并排序
- 对长时间计算进行检查点,以便从崩溃中恢复
- 保存内容管理容器获取的文件,而 Web 服务器容器提供数据
`emptyDir.medium` 字段控制 `emptyDir` 卷的存储位置。默认情况下,`emptyDir` 卷存储在支持节点的任何介质上,例如磁盘、SSD 或网络存储,具体取决于你的环境。如果你将 `emptyDir.medium` 字段设置为 `"Memory"`,Kubernetes 将为你挂载一个 tmpfs(RAM 支持的文件系统)。虽然 tmpfs 速度非常快,但请注意,与磁盘不同,你写入的文件会计入写入它们的容器的内存限制。
可以为默认介质指定大小限制,这限制了 `emptyDir` 卷的容量。存储是从节点临时存储中分配的。如果该存储被其他来源(例如日志文件或镜像叠加层)填满,则 `emptyDir` 可能会在该限制之前耗尽容量。如果未指定大小,则内存支持的卷的大小默认为节点可分配内存。
注意
请此处查看使用内存支持的 `emptyDir` 时资源管理方面的注意事项。emptyDir 配置示例
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir:
sizeLimit: 500Mi
emptyDir 内存配置示例
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir:
sizeLimit: 500Mi
medium: Memory
fc(光纤通道)
`fc` 卷类型允许在 Pod 中挂载现有的光纤通道块存储卷。你可以在卷配置中使用参数 `targetWWNs` 指定一个或多个目标全球通用名称 (WWN)。如果指定了多个 WWN,`targetWWNs` 期望这些 WWN 来自多路径连接。
注意
你必须事先配置 FC SAN Zoning,将这些 LUN(卷)分配和屏蔽到目标 WWN,以便 Kubernetes 主机可以访问它们。gcePersistentDisk (已弃用)
在 Kubernetes 1.34 中,树内 `gcePersistentDisk` 类型的所有操作都被重定向到 `pd.csi.storage.gke.io` CSI 驱动。
`gcePersistentDisk` 树内存储驱动在 Kubernetes v1.17 版本中被弃用,然后在 v1.28 版本中完全移除。
Kubernetes 项目建议你改用 Google Compute Engine Persistent Disk 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))
如果你明确启用 `GitRepoVolumeDriver` Feature Gate,则可以在集群中使用此已弃用的存储插件。
`gitRepo` 卷是卷插件的一个示例。此插件挂载一个空目录,并将 Git 仓库克隆到该目录中供 Pod 使用。
以下是一个 `gitRepo` 卷的示例:
apiVersion: v1
kind: Pod
metadata:
name: server
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /mypath
name: git-volume
volumes:
- name: git-volume
gitRepo:
repository: "git@somewhere:me/my-git-repository.git"
revision: "22f1d8406d464b0c0874075539c1f2e96c253775"
glusterfs (已移除)
Kubernetes 1.34 不包含 `glusterfs` 卷类型。
GlusterFS 树内存储驱动在 Kubernetes v1.25 版本中被弃用,然后在 v1.26 版本中完全移除。
hostPath
一个 `hostPath` 卷将宿主机节点文件系统中的文件或目录挂载到你的 Pod 中。这不是大多数 Pod 所需要的,但它为某些应用程序提供了强大的应急出口。
警告
使用 `hostPath` 卷类型会带来许多安全风险。如果可以避免使用 `hostPath` 卷,就应该避免。例如,定义一个 `local` PersistentVolume,并改用它。
如果你在准入时验证中限制了对节点上特定目录的访问,那么只有当你同时要求该 `hostPath` 卷的任何挂载都是只读时,该限制才有效。如果你允许不受信任的 Pod 读写挂载任何宿主机路径,那么该 Pod 中的容器可能能够规避读写宿主机挂载。
在使用 `hostPath` 卷时,无论是只读挂载还是读写挂载,都应谨慎,因为:
- 对主机文件系统的访问可能会暴露特权系统凭据(例如 kubelet 的凭据)或特权 API(例如容器运行时套接字),这些凭据或 API 可用于容器逃逸或攻击集群的其他部分。
- 配置相同(例如从 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 将无法启动。为了确保此模式正常工作,你可以尝试单独挂载目录和文件,如 `hostPath` 的 `FileOrCreate` 示例所示。底层主机上创建的一些文件或目录可能只能由 root 访问。然后,你需要将进程作为 root 在 特权容器 中运行,或者修改主机上的文件权限以从 `hostPath` 卷读取或写入。
hostPath 配置示例
---
# This manifest mounts /data/foo on the host as /foo inside the
# single container that runs within the hostpath-example-linux Pod.
#
# The mount into the container is read-only.
apiVersion: v1
kind: Pod
metadata:
name: hostpath-example-linux
spec:
os: { name: linux }
nodeSelector:
kubernetes.io/os: linux
containers:
- name: example-container
image: registry.k8s.io/test-webserver
volumeMounts:
- mountPath: /foo
name: example-volume
readOnly: true
volumes:
- name: example-volume
# mount /data/foo, but only if that directory already exists
hostPath:
path: /data/foo # directory location on host
type: Directory # this field is optional
---
# This manifest mounts C:\Data\foo on the host as C:\foo, inside the
# single container that runs within the hostpath-example-windows Pod.
#
# The mount into the container is read-only.
apiVersion: v1
kind: Pod
metadata:
name: hostpath-example-windows
spec:
os: { name: windows }
nodeSelector:
kubernetes.io/os: windows
containers:
- name: example-container
image: microsoft/windowsservercore:1709
volumeMounts:
- name: example-volume
mountPath: "C:\\foo"
readOnly: true
volumes:
# mount C:\Data\foo from the host, but only if that directory already exists
- name: example-volume
hostPath:
path: "C:\\Data\\foo" # directory location on host
type: Directory # this field is optional
hostPath FileOrCreate 配置示例
以下清单定义了一个 Pod,它将 `/var/local/aaa` 挂载到 Pod 中的单个容器内部。如果节点上还没有路径 `/var/local/aaa`,kubelet 会将其创建为一个目录,然后将其挂载到 Pod 中。
如果 `/var/local/aaa` 已经存在但不是目录,则 Pod 会失败。此外,kubelet 会尝试在该目录内部(从主机角度看)创建一个名为 `/var/local/aaa/1.txt` 的文件;如果该路径已经存在且不是常规文件,则 Pod 会失败。
这是示例清单
apiVersion: v1
kind: Pod
metadata:
name: test-webserver
spec:
os: { name: linux }
nodeSelector:
kubernetes.io/os: linux
containers:
- name: test-webserver
image: registry.k8s.io/test-webserver:latest
volumeMounts:
- mountPath: /var/local/aaa
name: mydir
- mountPath: /var/local/aaa/1.txt
name: myfile
volumes:
- name: mydir
hostPath:
# Ensure the file directory is created.
path: /var/local/aaa
type: DirectoryOrCreate
- name: myfile
hostPath:
path: /var/local/aaa/1.txt
type: FileOrCreate
image
`image` 卷源表示 kubelet 主机上可用的 OCI 对象(容器镜像或制品)。
使用 `image` 卷源的示例是
apiVersion: v1
kind: Pod
metadata:
name: image-volume
spec:
containers:
- name: shell
command: ["sleep", "infinity"]
image: debian
volumeMounts:
- name: volume
mountPath: /volume
volumes:
- name: volume
image:
reference: quay.io/crio/artifact:v2
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`) 的方式挂载该卷。
除此之外
- 容器的 `subPath` 或 `subPathExpr` 挂载(`spec.containers[*].volumeMounts.[subPath,subPathExpr]`)仅在 Kubernetes v1.33 及更高版本中支持。
- `spec.securityContext.fsGroupChangePolicy` 字段对这种卷类型没有影响。
- `AlwaysPullImages` 准入控制器 对此卷源也像对容器镜像一样工作。
`image` 类型可用的字段如下:
reference
- 要使用的制品引用。例如,你可以指定 `registry.k8s.io/conformance:v1.34.0` 来从 Kubernetes 一致性测试镜像加载文件。其行为与 `pod.spec.containers[*].image` 相同。拉取 Secret 将以与容器镜像相同的方式进行组装,方法是查找节点凭据、Service Account 镜像拉取 Secret 和 Pod Spec 镜像拉取 Secret。此字段是可选的,允许更高级别的配置管理在工作负载控制器(如 Deployments 和 StatefulSets)中默认或覆盖容器镜像。有关容器镜像的更多信息
pullPolicy
- 拉取 OCI 对象的策略。可能的值有:`Always`、`Never` 或 `IfNotPresent`。如果指定了 `:latest` 标签,则默认为 `Always`;否则为 `IfNotPresent`。
请参阅使用带 Pod 的镜像卷示例,了解如何使用卷源的更多详细信息。
iscsi
`iscsi` 卷允许将现有 iSCSI (通过 IP 的 SCSI) 卷挂载到 Pod 中。与 Pod 移除时被擦除的 `emptyDir` 不同,`iscsi` 卷的内容会保留,并且卷只是被卸载。这意味着 iSCSI 卷可以预先填充数据,并且该数据可以在 Pod 之间共享。
注意
在使用它之前,你必须运行自己的 iSCSI 服务器并创建卷。iSCSI 的一个特性是可以被多个消费者同时以只读方式挂载。这意味着你可以用你的数据集预先填充一个卷,然后从你需要的尽可能多的 Pod 中并行提供它。不幸的是,iSCSI 卷只能由一个消费者以读写模式挂载。不允许同时写入。
local
`local` 卷表示一个已挂载的本地存储设备,例如磁盘、分区或目录。
本地卷只能作为静态创建的 PersistentVolume 使用。不支持动态供应。
与 `hostPath` 卷相比,`local` 卷以持久和可移植的方式使用,无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 上的节点亲和性来了解卷的节点约束。
然而,`local` 卷受底层节点的可用性限制,不适用于所有应用程序。如果节点变得不健康,那么 `local` 卷将对 Pod 不可访问。使用此卷的 Pod 将无法运行。使用 `local` 卷的应用程序必须能够容忍这种可用性降低以及潜在的数据丢失,具体取决于底层磁盘的耐久性特征。
以下示例显示了一个使用 `local` 卷和 `nodeAffinity` 的 PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- example-node
使用 `local` 卷时,必须设置 PersistentVolume `nodeAffinity`。Kubernetes 调度器使用 PersistentVolume `nodeAffinity` 将这些 Pod 调度到正确的节点。
PersistentVolume `volumeMode` 可以设置为 “Block”(而不是默认值 “Filesystem”),以将本地卷暴露为裸块设备。
使用本地卷时,建议创建一个将 `volumeBindingMode` 设置为 `WaitForFirstConsumer` 的 StorageClass。有关更多详细信息,请参见本地 StorageClass 示例。延迟卷绑定可确保 PersistentVolumeClaim 绑定决策也将与 Pod 可能拥有的任何其他节点约束(例如节点资源需求、节点选择器、Pod 亲和性和 Pod 反亲和性)一起进行评估。
可以单独运行一个外部静态供应器,以改进本地卷生命周期的管理。请注意,此供应器尚不支持动态供应。有关如何运行外部本地供应器的示例,请参见 本地卷供应器用户指南。
注意
如果未使用外部静态供应器管理卷生命周期,则本地 PersistentVolume 需要用户手动清理和删除。nfs
`nfs` 卷允许将现有的 NFS (网络文件系统) 共享挂载到 Pod 中。与 Pod 移除时被擦除的 `emptyDir` 不同,`nfs` 卷的内容会保留,并且卷只是被卸载。这意味着 NFS 卷可以预先填充数据,并且该数据可以在 Pod 之间共享。NFS 可以被多个写入者同时挂载。
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /my-nfs-data
name: test-volume
volumes:
- name: test-volume
nfs:
server: my-nfs-server.example.com
path: /my-nfs-volume
readOnly: true
注意
在使用它之前,你必须运行自己的 NFS 服务器并导出共享。
另请注意,你无法在 Pod 规范中指定 NFS 挂载选项。你可以设置服务器端挂载选项或使用 /etc/nfsmount.conf。你也可以通过 PersistentVolumes 挂载 NFS 卷,PersistentVolumes 允许你设置挂载选项。
persistentVolumeClaim
`persistentVolumeClaim` 卷用于将 PersistentVolume 挂载到 Pod 中。PersistentVolumeClaims 是一种让用户“声明”持久存储(例如 iSCSI 卷)而无需了解特定云环境细节的方式。
有关更多详细信息,请参阅 PersistentVolumes 的信息。
portworxVolume (已弃用)
`portworxVolume` 是一个弹性块存储层,与 Kubernetes 超融合运行。Portworx 对服务器中的存储进行指纹识别,根据功能进行分层,并聚合多个服务器的容量。Portworx 在虚拟机或裸机 Linux 节点中以访客模式运行。
`portworxVolume` 可以通过 Kubernetes 动态创建,也可以预先配置并在 Pod 中引用。以下是一个引用预配置 Portworx 卷的 Pod 示例:
apiVersion: v1
kind: Pod
metadata:
name: test-portworx-volume-pod
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /mnt
name: pxvol
volumes:
- name: pxvol
# This Portworx volume must already exist.
portworxVolume:
volumeID: "pxvol"
fsType: "<fs-type>"
注意
在使用之前,请确保你有一个名为 `pxvol` 的现有 PortworxVolume。Portworx CSI 迁移
Kubernetes v1.33 [stable]
(默认启用:true)在 Kubernetes 1.34 中,树内 Portworx 卷的所有操作默认都重定向到 `pxd.portworx.com` 容器存储接口 (CSI) 驱动。
Portworx CSI 驱动必须安装在集群上。
projected
投影卷将多个现有卷源映射到同一个目录中。有关更多详细信息,请参阅投影卷。
rbd (已移除)
Kubernetes 1.34 不包含 `rbd` 卷类型。
Rados 块设备 (RBD) 树内存储驱动及其 CSI 迁移支持在 Kubernetes v1.28 版本中被弃用,然后在 v1.31 版本中完全移除。
secret
`secret` 卷用于将敏感信息(例如密码)传递给 Pod。你可以将 Secret 存储在 Kubernetes API 中,并将其作为文件挂载供 Pod 使用,而无需直接与 Kubernetes 耦合。`secret` 卷由 tmpfs(RAM 支持的文件系统)支持,因此它们永远不会写入非易失性存储。
注意
在使用 Secret 之前,你必须在 Kubernetes API 中创建一个 Secret。
Secret 总是以 `readOnly` 方式挂载。
使用 Secret 作为 `subPath` 卷挂载的容器将不会收到 Secret 更新。
有关更多详细信息,请参阅 配置 Secret。
vsphereVolume (已弃用)
在 Kubernetes 1.34 中,树内 `vsphereVolume` 类型的所有操作都被重定向到 `csi.vsphere.vmware.com` CSI 驱动。
`vsphereVolume` 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.30 版本中完全移除。
Kubernetes 项目建议你改用 vSphere CSI 第三方存储驱动。
使用 subPath
有时,在单个 Pod 中为多个用途共享一个卷很有用。`volumeMounts[*].subPath` 属性指定引用卷内部的子路径,而不是其根路径。
以下示例展示了如何使用单个共享卷配置具有 LAMP 堆栈(Linux Apache MySQL PHP)的 Pod。此 `subPath` 示例配置不建议用于生产环境。
PHP 应用程序的代码和资源映射到卷的 `html` 文件夹,MySQL 数据库存储在卷的 `mysql` 文件夹中。例如:
apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpasswd"
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: mysql
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: html
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-data
使用带有扩展环境变量的 subPath
使用 `subPathExpr` 字段从 downward API 环境变量构建 `subPath` 目录名。`subPath` 和 `subPathExpr` 属性是互斥的。
在此示例中,一个 `Pod` 使用 `subPathExpr` 在 `hostPath` 卷 `/var/log/pods` 内创建一个目录 `pod1`。`hostPath` 卷从 `downwardAPI` 获取 `Pod` 名称。主机目录 `/var/log/pods/pod1` 在容器中挂载到 `/logs`。
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: container1
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
image: busybox:1.28
command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
volumeMounts:
- name: workdir1
mountPath: /logs
# The variable expansion uses round brackets (not curly brackets).
subPathExpr: $(POD_NAME)
restartPolicy: Never
volumes:
- name: workdir1
hostPath:
path: /var/log/pods
资源
`emptyDir` 卷的存储介质(如磁盘或 SSD)由 kubelet 根目录(通常为 `/var/lib/kubelet`)所在文件系统的介质决定。`emptyDir` 或 `hostPath` 卷可以消耗的空间没有限制,容器或 Pod 之间也没有隔离。
要了解如何使用资源规范请求空间,请参阅如何管理资源。
树外卷插件
树外卷插件包括 容器存储接口 (CSI),以及 FlexVolume(已弃用)。这些插件使存储供应商能够创建自定义存储插件,而无需将其插件源代码添加到 Kubernetes 仓库中。
以前,所有卷插件都是“树内”的。“树内”插件是随核心 Kubernetes 二进制文件一起构建、链接、编译和发布的。这意味着将新存储系统添加到 Kubernetes(一个卷插件)需要将代码签入核心 Kubernetes 代码仓库。
CSI 和 FlexVolume 都允许卷插件独立于 Kubernetes 代码库进行开发,并作为扩展部署(安装)在 Kubernetes 集群上。
对于希望创建树外卷插件的存储供应商,请参阅 卷插件常见问题。
csi
容器存储接口 (CSI) 定义了一个标准接口,用于容器编排系统(如 Kubernetes)将其任意存储系统暴露给容器工作负载。
请阅读CSI 设计提案以获取更多信息。
注意
Kubernetes v1.13 中已弃用对 CSI 规范版本 0.2 和 0.3 的支持,并将在未来的版本中移除。注意
CSI 驱动程序可能无法在所有 Kubernetes 版本中兼容。请查阅特定 CSI 驱动程序的文档,以获取每个 Kubernetes 版本支持的部署步骤和兼容性矩阵。在 Kubernetes 集群上部署 CSI 兼容卷驱动程序后,用户可以使用 `csi` 卷类型来挂载 CSI 驱动程序公开的卷。
`csi` 卷可以通过三种不同的方式在 Pod 中使用:
- 通过引用 PersistentVolumeClaim
- 使用 通用临时卷
- 如果驱动支持,则使用 CSI 临时卷
以下字段可供存储管理员配置 CSI 持久卷:
- `driver`:一个字符串值,指定要使用的卷驱动程序的名称。此值必须与 CSI 驱动程序在 `GetPluginInfoResponse` 中返回的值相对应,如 CSI 规范中所定义。Kubernetes 使用它来识别要调用哪个 CSI 驱动程序,CSI 驱动程序组件使用它来识别哪些 PV 对象属于该 CSI 驱动程序。
- `volumeHandle`:一个字符串值,唯一标识该卷。此值必须与 CSI 驱动程序在 `CreateVolumeResponse` 中返回的 `volume.id` 字段值相对应,如 CSI 规范中所定义。在引用该卷时,该值将作为 `volume_id` 传递给所有对 CSI 卷驱动程序的调用。
- `readOnly`:一个可选的布尔值,指示卷是否应以只读方式“ControllerPublished”(挂载)。默认为 false。此值通过 `ControllerPublishVolumeRequest` 中的 `readonly` 字段传递给 CSI 驱动程序。
- `fsType`:如果 PV 的 `VolumeMode` 为 `Filesystem`,则此字段可用于指定应用于挂载卷的文件系统。如果卷尚未格式化并且支持格式化,则此值将用于格式化卷。此值通过 `ControllerPublishVolumeRequest`、`NodeStageVolumeRequest` 和 `NodePublishVolumeRequest` 的 `VolumeCapability` 字段传递给 CSI 驱动程序。
- `volumeAttributes`:一个字符串到字符串的映射,指定卷的静态属性。此映射必须与 CSI 驱动程序在 `CreateVolumeResponse` 中返回的 `volume.attributes` 字段中的映射相对应,如 CSI 规范中所定义。该映射通过 `ControllerPublishVolumeRequest`、`NodeStageVolumeRequest` 和 `NodePublishVolumeRequest` 中的 `volume_context` 字段传递给 CSI 驱动程序。
- `controllerPublishSecretRef`:对包含敏感信息的 Secret 对象的引用,该信息将传递给 CSI 驱动程序以完成 CSI `ControllerPublishVolume` 和 `ControllerUnpublishVolume` 调用。此字段是可选的,如果不需要 Secret,则可以为空。如果 Secret 包含多个 Secret,则所有 Secret 都将被传递。
- `nodeExpandSecretRef`:一个 Secret 对象的引用,其中包含要传递给 CSI 驱动程序的敏感信息,以完成 CSI `NodeExpandVolume` 调用。此字段是可选的,如果不需要 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 工作负载中实现裸块卷支持。
你可以像往常一样设置你的 PersistentVolume/PersistentVolumeClaim,支持裸块卷,无需任何 CSI 特定的更改。
CSI 临时卷
Kubernetes v1.25 [稳定]
你可以在 Pod 规范中直接配置 CSI 卷。以这种方式指定的卷是临时的,并且在 Pod 重启后不会持久化。有关更多信息,请参阅临时卷。
有关如何开发 CSI 驱动程序的更多信息,请参阅 kubernetes-csi 文档。
Windows CSI 代理
CSI 节点插件需要执行各种特权操作,例如扫描磁盘设备和挂载文件系统。这些操作对于每个主机操作系统都不同。对于 Linux worker 节点,容器化的 CSI 节点插件通常作为特权容器部署。对于 Windows worker 节点,容器化的 CSI 节点插件的特权操作通过 csi-proxy 支持,这是一个社区管理、独立的二进制文件,需要预先安装在每个 Windows 节点上。
有关更多详细信息,请参阅你希望部署的 CSI 插件的部署指南。
从树内插件迁移到 CSI 驱动
Kubernetes v1.25 [稳定]
`CSIMigration` 功能将针对现有树内插件的操作重定向到相应的 CSI 插件(预计已安装和配置)。因此,当过渡到取代树内插件的 CSI 驱动程序时,操作员无需对现有存储类、PersistentVolume 或 PersistentVolumeClaim(引用树内插件)进行任何配置更改。
注意
由树内卷插件创建的现有 PV 即使在该卷类型的 CSI 迁移完成后,以及即使你升级到不再包含该类型存储的编译支持的 Kubernetes 版本之后,仍然可以在将来使用,无需进行任何配置更改。
作为该迁移的一部分,你——或另一个集群管理员——必须为该存储安装并配置了适当的 CSI 驱动程序。Kubernetes 核心不会为你安装该软件。
迁移后,你还可以定义引用旧版内置存储集成的新 PVC 和 PV。只要你安装并配置了适当的 CSI 驱动程序,PV 创建就会继续有效,即使对于全新的卷也是如此。实际的存储管理现在通过 CSI 驱动程序进行。
支持的操作和功能包括:卷的供应/删除、附加/分离、挂载/卸载和大小调整。
支持 `CSIMigration` 并已实现相应 CSI 驱动程序的树内插件列在 卷类型 中。
flexVolume (已弃用)
FlexVolume 是一个树外插件接口,它使用基于执行的模型与存储驱动程序进行交互。FlexVolume 驱动程序二进制文件必须安装在每个节点上预定义的卷插件路径中,在某些情况下,控制平面节点也需要安装。
Pod 通过 `flexVolume` 树内卷插件与 FlexVolume 驱动程序交互。
以下 FlexVolume 插件,作为 PowerShell 脚本部署在主机上,支持 Windows 节点:
注意
FlexVolume 已弃用。建议使用树外 CSI 驱动程序将外部存储与 Kubernetes 集成。
FlexVolume 驱动程序的维护者应实现 CSI 驱动程序,并帮助将 FlexVolume 驱动程序的用户迁移到 CSI。FlexVolume 的用户应将其工作负载迁移到使用等效的 CSI 驱动程序。
挂载传播
注意
挂载传播是一个低级功能,并非在所有卷类型上都一致工作。Kubernetes 项目建议仅将挂载传播与 `hostPath` 或内存支持的 `emptyDir` 卷一起使用。有关更多上下文,请参见 Kubernetes issue #95049。挂载传播允许将容器挂载的卷共享给同一 Pod 中的其他容器,甚至共享给同一节点上的其他 Pod。
卷的挂载传播由 `containers[*].volumeMounts` 中的 `mountPropagation` 字段控制。其值包括:
`None` - 此卷挂载不会接收主机后续挂载到此卷或其任何子目录的任何挂载。同样,容器创建的任何挂载在主机上将不可见。这是默认模式。
此模式等同于 `mount(8)` 中描述的 `rprivate` 挂载传播。
然而,当 `rprivate` 传播不适用时,CRI 运行时可能会选择 `rslave` 挂载传播(即 `HostToContainer`)。已知 cri-dockerd (Docker) 在挂载源包含 Docker 守护进程的根目录 (`/var/lib/docker`) 时选择 `rslave` 挂载传播。
`HostToContainer` - 此卷挂载将接收所有后续挂载到此卷或其任何子目录的挂载。
换句话说,如果主机在该卷挂载点内部挂载任何内容,容器将看到它被挂载在那里。
同样,如果任何具有 `Bidirectional` 挂载传播到同一卷的 Pod 在那里挂载任何内容,具有 `HostToContainer` 挂载传播的容器也将看到它。
此模式等同于 `mount(8)` 中描述的 `rslave` 挂载传播。
`Bidirectional` - 此卷挂载的行为与 `HostToContainer` 挂载相同。此外,容器创建的所有卷挂载都将传播回主机以及使用相同卷的所有 Pod 的所有容器。
此模式的典型用例是带有 FlexVolume 或 CSI 驱动程序的 Pod,或需要使用 `hostPath` 卷在主机上挂载内容的 Pod。
此模式等同于 `mount(8)` 中描述的 `rshared` 挂载传播。
警告
`Bidirectional` 挂载传播可能是危险的。它可能会损坏主机操作系统,因此只允许在特权容器中使用。强烈建议熟悉 Linux 内核行为。此外,容器在 Pod 中创建的任何卷挂载在终止时必须由容器销毁(卸载)。
只读挂载
通过将 `.spec.containers[].volumeMounts[].readOnly` 字段设置为 `true`,可以将挂载设置为只读。这不会使卷本身只读,但该特定容器将无法向其写入。Pod 中的其他容器可以以读写方式挂载相同的卷。
在 Linux 上,默认情况下只读挂载并非递归只读。例如,考虑一个 Pod,它将主机上的 `/mnt` 作为 `hostPath` 卷挂载。如果 `/mnt/
递归只读挂载
Kubernetes v1.33 [stable]
(默认启用:true)可以通过为 kubelet 和 kube-apiserver 设置 `RecursiveReadOnlyMounts` 功能门,并为 Pod 设置 `.spec.containers[].volumeMounts[].recursiveReadOnly` 字段来启用递归只读挂载。
允许的值为:
`Disabled`(默认):无效果。
`Enabled`:使挂载递归只读。需要满足以下所有要求:
- `readOnly` 设置为 `true`
- `mountPropagation` 未设置,或设置为 `None`
- 主机正在运行 Linux 内核 v5.12 或更高版本
- CRI 级别容器运行时支持递归只读挂载
- OCI 级别容器运行时支持递归只读挂载。
如果上述任何一项不为真,则会失败。
`IfPossible`:尝试应用 `Enabled`,如果内核或运行时类不支持此功能,则回退到 `Disabled`。
示例
apiVersion: v1
kind: Pod
metadata:
name: rro
spec:
volumes:
- name: mnt
hostPath:
# tmpfs is mounted on /mnt/tmpfs
path: /mnt
containers:
- name: busybox
image: busybox
args: ["sleep", "infinity"]
volumeMounts:
# /mnt-rro/tmpfs is not writable
- name: mnt
mountPath: /mnt-rro
readOnly: true
mountPropagation: None
recursiveReadOnly: Enabled
# /mnt-ro/tmpfs is writable
- name: mnt
mountPath: /mnt-ro
readOnly: true
# /mnt-rw/tmpfs is writable
- name: mnt
mountPath: /mnt-rw
当 kubelet 和 kube-apiserver 识别此属性时,`.status.containerStatuses[].volumeMounts[].recursiveReadOnly` 字段将被设置为 `Enabled` 或 `Disabled`。
实现
已知以下容器运行时支持递归只读挂载。
CRI 级别
- containerd,自 v2.0 起
- CRI-O,自 v1.30 起
OCI 级别
下一步
请按照 使用持久卷部署 WordPress 和 MySQL 的示例操作。
本页上的项目指的是提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南。
在提议添加额外第三方链接的更改之前,你应该阅读内容指南。