卷
Kubernetes 的 卷(volume) 为Pod 中的容器提供了一种通过文件系统访问和共享数据的方式。你可以根据不同的目的使用不同类型的卷,例如:
- 基于 ConfigMap 或 Secret 填充配置文件
- 为 Pod 提供一些临时的暂存空间
- 在同一 Pod 中的两个不同容器之间共享文件系统
- 在两个不同 Pod 之间共享文件系统(即使这些 Pod 运行在不同的节点上)
- 持久地存储数据,以便即使 Pod 重启或被替换,数据也仍然可用
- 将配置信息传递给容器中运行的应用,基于容器所在 Pod 的详细信息(例如:告诉sidecar 容器 Pod 正在哪个命名空间中运行)
- 提供对不同容器镜像中数据的只读访问
数据共享可以在容器内的不同本地进程之间进行,也可以在不同容器之间或在 Pod 之间进行。
为什么卷很重要
数据持久性:容器中的磁盘文件是短暂的,这给在容器中运行的非普通应用带来了一些问题。一个问题是,当容器崩溃或停止时,容器状态不会被保存,因此在容器生命周期内创建或修改的所有文件都将丢失。崩溃后,kubelet 会以一个干净的状态重新启动容器。
共享存储:另一个问题是,当多个容器在一个
Pod
中运行并且需要共享文件时。跨所有容器设置和访问共享文件系统可能具有挑战性。
Kubernetes 卷(volume) 抽象可以帮助你解决这两个问题。
在学习卷、PersistentVolume 和 PersistentVolumeClaim 之前,你应该先阅读有关 Pod 的内容,并确保你理解 Kubernetes 如何使用 Pod 来运行容器。
卷的工作原理
Kubernetes 支持多种类型的卷。一个 Pod 可以同时使用任意数量的卷类型。临时卷(Ephemeral volume) 的生命周期与特定的 Pod 绑定,而持久卷(persistent volumes) 的存在时间则超出任何单个 Pod 的生命周期。当 Pod 不复存在时,Kubernetes 会销毁临时卷;但是,Kubernetes 不会销毁持久卷。对于给定 Pod 中的任何类型的卷,数据在容器重启后都会被保留。
从本质上讲,卷是一个目录,其中可能包含一些数据,Pod 中的容器可以访问该目录。该目录如何生成、支持它的介质以及目录内容都取决于所使用的特定卷类型。
要使用卷,请在 .spec.volumes
中指定要为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts
中声明将这些卷挂载到容器中的位置。
当 Pod 启动时,容器中的进程看到的文件系统视图由容器镜像的初始内容以及挂载在容器内的卷(如果已定义)组成。进程看到一个最初与容器镜像内容匹配的根文件系统。对该文件系统层级结构内的任何写入(如果允许)会影响该进程执行后续文件系统访问时所看到的内容。卷会被挂载到容器文件系统内的指定路径。对于 Pod 中定义的每个容器,你必须独立指定容器使用每个卷的挂载位置。
卷不能挂载在其他卷内(但相关机制请参阅使用 subPath)。此外,一个卷不能包含指向不同卷中任何内容的硬链接。
卷的类型
Kubernetes 支持几种类型的卷。
awsElasticBlockStore (已弃用)
在 Kubernetes 1.33 中,树内 (in-tree) awsElasticBlockStore
类型的所有操作都将重定向到 ebs.csi.aws.com
CSI 驱动。
AWSElasticBlockStore 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.27 版本中被完全移除。
Kubernetes 项目建议你改用 AWS EBS 第三方存储驱动。
azureDisk (已弃用)
在 Kubernetes 1.33 中,树内 azureDisk
类型的所有操作都将重定向到 disk.csi.azure.com
CSI 驱动。
AzureDisk 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.27 版本中被完全移除。
Kubernetes 项目建议你改用 Azure Disk 第三方存储驱动。
azureFile (已弃用)
在 Kubernetes 1.33 中,树内 azureFile
类型的所有操作都将重定向到 file.csi.azure.com
CSI 驱动。
AzureFile 树内存储驱动在 Kubernetes v1.21 版本中被弃用,然后在 v1.30 版本中被完全移除。
Kubernetes 项目建议你改用 Azure File 第三方存储驱动。
cephfs (已移除)
Kubernetes 1.33 不包含 cephfs
卷类型。
cephfs
树内存储驱动在 Kubernetes v1.28 版本中被弃用,然后在 v1.31 版本中被完全移除。
cinder (已弃用)
在 Kubernetes 1.33 中,树内 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
的一些用途包括:
- 暂存空间,例如用于基于磁盘的归并排序
- 检查点(checkpointing)长时间计算以便从崩溃中恢复
- 保存内容管理器容器抓取的文件,同时 Web 服务器容器提供数据服务
emptyDir.medium
字段控制 emptyDir
卷的存储位置。默认情况下,emptyDir
卷存储在支持节点的介质上,例如磁盘、SSD 或网络存储,具体取决于你的环境。如果你将 emptyDir.medium
字段设置为 "Memory"
,Kubernetes 将为你挂载一个 tmpfs(RAM 支持的文件系统)。虽然 tmpfs 非常快,但请注意,与磁盘不同的是,你写入的文件会计入写入它们的容器的内存限制。
可以为默认介质指定大小限制,这将限制 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.33 中,树内 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
卷挂载到初始化容器中,该容器使用 Git 克隆仓库,然后将该 EmptyDir 挂载到 Pod 的容器中。
你可以使用策略(例如ValidatingAdmissionPolicy)来限制在集群中使用 gitRepo
卷。你可以使用以下通用表达式语言 (CEL) 表达式作为策略的一部分来拒绝使用 gitRepo
卷:
!has(object.spec.volumes) || !object.spec.volumes.exists(v, has(v.gitRepo))
如果你显式启用 GitRepoVolumeDriver
特性门控,可以在你的集群中使用此已弃用的存储插件。
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.33 不包含 glusterfs
卷类型。
GlusterFS 树内存储驱动在 Kubernetes v1.25 版本中被弃用,然后在 v1.26 版本中被完全移除。
hostPath
hostPath
卷将主机节点文件系统中的文件或目录挂载到你的 Pod 中。这不是大多数 Pod 所需要的,但它为某些应用提供了一个强大的逃生通道。
警告
使用 hostPath
卷类型会带来许多安全风险。如果可以避免使用 hostPath
卷,则应该这样做。例如,定义一个 local
PersistentVolume,并使用它代替。
如果你使用准入时校验来限制对节点上特定目录的访问,那么只有当你额外要求该 hostPath
卷的任何挂载都是只读时,该限制才有效。如果你允许不受信任的 Pod 对任何主机路径进行读写挂载,则该 Pod 中的容器可能能够颠覆该读写主机挂载。
在使用 hostPath
卷时,无论是作为只读还是读写挂载,请务必小心,因为:
- 访问主机文件系统可能会暴露特权系统凭证(例如 kubelet 的凭证)或特权 API(例如容器运行时 socket),这些可被用于容器逃逸或攻击集群的其他部分。
- 配置完全相同的 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 socket |
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,该 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
Kubernetes v1.33 [beta]
(默认禁用)image
卷源表示一个 OCI 对象(容器镜像或 artifact),该对象可在 kubelet 的主机上获取。
以下是使用 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 从不拉取引用,并且只使用本地镜像或 artifact。如果镜像的任何层不在本地或镜像的清单文件未被缓存,则 Pod 状态变为
Failed
。 IfNotPresent
- 如果引用不在磁盘上,kubelet 会拉取它。如果引用不存在且拉取失败,则 Pod 状态变为
Failed
。
如果 Pod 被删除并重新创建,该卷会重新解析,这意味着在 Pod 重新创建时将有新的远程内容可用。在 Pod 启动期间解析或拉取镜像失败将阻止容器启动,并可能增加显著的延迟。失败将使用正常的卷回退进行重试,并在 Pod 的 reason 和 message 中报告。
此卷可能挂载的对象类型由主机上的容器运行时实现定义。至少,它们必须包含容器镜像字段支持的所有有效类型。OCI 对象会挂载到一个单独的目录(spec.containers[*].volumeMounts.mountPath
)中,并将以只读方式挂载。在 Linux 上,容器运行时通常也会以阻止文件执行 (noexec
) 的方式挂载卷。
除此之外,
subPath
或subPathExpr
对容器的挂载(spec.containers[*].volumeMounts.[subPath,subPathExpr]
)仅从 Kubernetes v1.33 开始支持。- 字段
spec.securityContext.fsGroupChangePolicy
对此卷类型没有影响。 AlwaysPullImages
准入控制器 也像容器镜像一样对此卷源生效。
image
类型支持以下字段:
reference
- 要使用的 Artifact 引用。例如,你可以指定
registry.k8s.io/conformance:v1.33.0
从 Kubernetes conformance test 镜像加载文件。其行为方式与pod.spec.containers[*].image
相同。通过查找节点凭证、ServiceAccount 镜像拉取 Secret 和 Pod 规约的镜像拉取 Secret,拉取 Secret 将以与容器镜像相同的方式组合。此字段是可选的,以允许更高级别的配置管理在工作负载控制器(如 Deployments 和 StatefulSets)中提供容器镜像的默认值或覆盖它们。有关容器镜像的更多信息 pullPolicy
- OCI 对象拉取策略。可能的值为:
Always
、Never
或IfNotPresent
。如果指定了 `:latest` 标签,则默认为 `Always`,否则默认为 `IfNotPresent`。
有关如何使用卷源的更多详细信息,请参阅使用带有 Pod 的镜像卷示例。
iscsi
一个 `iscsi` 卷允许将现有的 iSCSI (SCSI over IP) 卷挂载到 Pod 中。与 Pod 移除时会被删除的 `emptyDir` 不同,`iscsi` 卷的内容会被保留,卷只是被卸载。这意味着 iSCSI 卷可以预先填充数据,并且这些数据可以在 Pod 之间共享。
注意
在使用之前,必须运行自己的 iSCSI 服务器并创建好卷。iSCSI 的一个特性是它可以由多个消费者同时以只读方式挂载。这意味着你可以用你的数据集预先填充一个卷,然后根据需要从任意数量的 Pod 并行提供服务。遗憾的是,iSCSI 卷只能由单个消费者以读写模式挂载。不允许同时写入。
有关更多详细信息,请参阅iSCSI 示例。
local
一个 `local` 卷表示挂载的本地存储设备,例如磁盘、分区或目录。
本地卷只能用作静态创建的 PersistentVolume。不支持动态配置。
与 `hostPath` 卷相比,`local` 卷以持久和可移植的方式使用,无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 上的节点亲和性来了解卷的节点约束。
然而,`local` 卷受底层节点可用性的影响,并非适用于所有应用程序。如果节点变得不健康,则 Pod 将无法访问 `local` 卷。使用此卷的 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"),以将本地卷作为原始块设备暴露。
使用本地卷时,建议创建 StorageClass 并将 `volumeBindingMode` 设置为 `WaitForFirstConsumer`。有关更多详细信息,请参阅本地 StorageClass 示例。延迟卷绑定可确保 PersistentVolumeClaim 绑定决策也会与 Pod 可能具有的任何其他节点约束(例如节点资源要求、节点选择器、Pod 亲和性和 Pod 反亲和性)一起进行评估。
可以单独运行外部静态供应器,以改进本地卷的生命周期管理。请注意,此供应器尚不支持动态配置。有关如何运行外部本地供应器的示例,请参阅本地卷供应器用户指南。
注意
如果未使用外部静态供应器来管理卷生命周期,则本地 PersistentVolume 需要用户手动清理和删除。nfs
一个 `nfs` 卷允许将现有的 NFS (Network File System) 共享挂载到 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 挂载 NFS 卷的示例,请参阅NFS 示例。
persistentVolumeClaim
一个 `persistentVolumeClaim` 卷用于将 PersistentVolume 挂载到 Pod 中。PersistentVolumeClaim 是用户“申领”持久存储(例如 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 卷示例。
Portworx CSI 迁移
在 Kubernetes 1.33 中,所有针对树内 Portworx 卷的操作默认都会重定向到 `pxd.portworx.com` Container Storage Interface (CSI) 驱动程序。
必须在集群上安装Portworx CSI 驱动程序。
projected
projected 卷将多个现有卷源映射到同一目录中。有关更多详细信息,请参阅projected 卷。
rbd (已移除)
Kubernetes 1.33 不包含 `rbd` 卷类型。
Rados Block Device (RBD) 树内存储驱动程序及其 CSI 迁移支持在 Kubernetes v1.28 版本中被废弃,然后在 v1.31 版本中被完全移除。
secret
一个 `secret` 卷用于向 Pod 传递敏感信息,例如密码。可以将 Secret 存储在 Kubernetes API 中,并将其挂载为文件供 Pod 使用,而无需直接与 Kubernetes 耦合。`secret` 卷由 tmpfs(基于 RAM 的文件系统)支持,因此它们永远不会写入非易失性存储。
注意
在使用之前,必须在 Kubernetes API 中创建一个 Secret。
Secret 始终以 `readOnly` 方式挂载。
使用 Secret 作为 `subPath` 卷挂载的容器将不会接收 Secret 更新。
有关更多详细信息,请参阅配置 Secret。
vsphereVolume (已废弃)
在 Kubernetes 1.33 中,所有针对树内 `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` 卷的存储介质(例如 Disk 或 SSD)由保存 kubelet 根目录(通常为 `/var/lib/kubelet`)的文件系统的介质决定。对 `emptyDir` 或 `hostPath` 卷可以使用的空间没有限制,并且容器或 Pod 之间也没有隔离。
要了解如何使用资源规约请求空间,请参阅如何管理资源。
树外卷插件
树外卷插件包括 Container Storage Interface (CSI) 以及 FlexVolume(已废弃)。这些插件使存储厂商能够创建自定义存储插件,而无需将插件源代码添加到 Kubernetes 仓库。
以前,所有卷插件都是“树内”的。“树内”插件是随 Kubernetes 核心二进制文件构建、链接、编译和发布的。这意味着向 Kubernetes 添加新的存储系统(卷插件)需要将代码提交到 Kubernetes 核心代码仓库。
CSI 和 FlexVolume 都允许卷插件独立于 Kubernetes 代码库进行开发,并作为扩展部署(安装)在 Kubernetes 集群上。
寻求创建树外卷插件的存储厂商,请参考卷插件常见问题。
csi
Container Storage Interface (CSI) 定义了一个标准接口,供容器编排系统(如 Kubernetes)将其任意存储系统暴露给其容器工作负载。
请阅读CSI 设计提案以获取更多信息。
注意
对 CSI 规约版本 0.2 和 0.3 的支持在 Kubernetes v1.13 中被废弃,并将在未来的版本中移除。注意
CSI 驱动程序可能无法兼容所有 Kubernetes 版本。请查阅特定 CSI 驱动程序的文档,了解每个 Kubernetes 版本支持的部署步骤和兼容性矩阵。一旦在 Kubernetes 集群上部署了 CSI 兼容的卷驱动程序,用户就可以使用 `csi` 卷类型来挂接或挂载 CSI 驱动程序暴露的卷。
`csi` 卷可以通过三种不同的方式在 Pod 中使用:
- 通过引用 PersistentVolumeClaim
- 通过通用临时卷
- 如果驱动程序支持,通过CSI 临时卷
以下字段可供存储管理员配置 CSI 持久卷:
- `driver`:指定要使用的卷驱动程序名称的字符串值。此值必须与 CSI 驱动程序在 CSI 规约中定义的 `GetPluginInfoResponse` 中返回的值相对应。它由 Kubernetes 用于标识要调用的 CSI 驱动程序,并由 CSI 驱动程序组件用于标识哪些 PV 对象属于该 CSI 驱动程序。
- `volumeHandle`:唯一标识卷的字符串值。此值必须与 CSI 驱动程序在 CSI 规约中定义的 `CreateVolumeResponse` 的 `volume.id` 字段中返回的值相对应。在引用卷时,此值会作为 `volume_id` 传递给 CSI 卷驱动程序的所有调用。
- `readOnly`:可选的布尔值,指示卷是否应以只读方式“ControllerPublished”(已挂接)。默认值为 false。此值通过 `ControllerPublishVolumeRequest` 中的 `readonly` 字段传递给 CSI 驱动程序。
- `fsType`:如果 PV 的 `VolumeMode` 是 `Filesystem`,则此字段可用于指定用于挂载卷的文件系统。如果卷尚未格式化且支持格式化,则此值将用于格式化卷。此值通过 `ControllerPublishVolumeRequest`、`NodeStageVolumeRequest` 和 `NodePublishVolumeRequest` 的 `VolumeCapability` 字段传递给 CSI 驱动程序。
- `volumeAttributes`:指定卷的静态属性的字符串到字符串映射。此映射必须与 CSI 驱动程序在 CSI 规约中定义的 `CreateVolumeResponse` 的 `volume.attributes` 字段中返回的映射相对应。此映射通过 `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 原始块卷支持
拥有外部 CSI 驱动程序的厂商可以在 Kubernetes 工作负载中实现原始块卷支持。
您可以像往常一样设置具有原始块卷支持的 PersistentVolume/PersistentVolumeClaim,无需进行任何 CSI 特定更改。
CSI 临时卷
您可以在 Pod 规约中直接配置 CSI 卷。以这种方式指定的卷是临时性的,并且在 Pod 重启后不会持久存在。有关更多信息,请参阅临时卷。
有关如何开发 CSI 驱动程序的更多信息,请参考kubernetes-csi 文档。
Windows CSI 代理
CSI 节点插件需要执行各种特权操作,例如扫描磁盘设备和挂载文件系统。这些操作对于不同的宿主操作系统有所不同。对于 Linux 工作节点,容器化的 CSI 节点插件通常部署为特权容器。对于 Windows 工作节点,使用 csi-proxy 支持容器化 CSI 节点插件的特权操作,csi-proxy 是一个社区管理的独立二进制文件,需要在每个 Windows 节点上预先安装。
有关更多详细信息,请参阅您希望部署的 CSI 插件的部署指南。
从树内插件迁移到 CSI 驱动程序
`CSIMigration` 特性将针对现有树内插件的操作重定向到相应的 CSI 插件(预期这些插件已被安装和配置)。因此,当迁移到取代树内插件的 CSI 驱动程序时,操作人员无需对现有 Storage Class、PersistentVolumes 或 PersistentVolumeClaims(指向树内插件)进行任何配置更改。
注意
由树内卷插件创建的现有 PV 未来仍可使用,无需进行任何配置更改,即使在该卷类型迁移到 CSI 完成后,即使升级到不支持该类存储的编译内置版本的 Kubernetes 之后也是如此。
作为该迁移的一部分,您或另一位集群管理员**必须**安装并配置该存储的相应 CSI 驱动程序。Kubernetes 核心不会为您安装该软件。
迁移完成后,您还可以定义指向遗留的、内置存储集成的新的 PVC 和 PV。只要您已安装并配置了相应的 CSI 驱动程序,即使对于全新的卷,PV 创建也能继续工作。实际的存储管理现在通过 CSI 驱动程序进行。
支持的操作和特性包括:卷的配置/删除、挂接/解除挂接、挂载/卸载和扩容。
支持 `CSIMigration` 并已实现相应 CSI 驱动程序的树内插件列在卷类型中。
flexVolume (已废弃)
FlexVolume 是一种树外插件接口,它使用基于 exec 的模型与存储驱动程序交互。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 上,只读挂载默认不是递归只读的。例如,考虑一个将主机 `/mnt` 作为 `hostPath` 卷挂载的 Pod。如果在 `/mnt/
递归只读挂载
通过为 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 级别
下一步
本页中的项目引用了提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者对这些第三方产品或项目不负责任。有关更多详细信息,请参阅CNCF 网站指南。
在提议添加额外第三方链接的更改之前,您应该阅读内容指南。