存储
- 1: 卷
- 2: 持久卷
- 3: 投射卷
- 4: 临时卷
- 5: 存储类
- 6: 动态卷制备
- 7: 卷快照
- 8: 卷快照类
- 9: CSI 卷克隆
- 10: 存储容量
- 11: 特定于节点的卷数限制
- 12: 卷健康监测
- 13: Windows 存储
1 - 卷
Container 中的文件在磁盘上是临时存放的,这给 Container 中运行的较重要的应用程序带来一些问题。
问题之一是当容器崩溃时文件丢失。
kubelet 会重新启动容器,但容器会以干净的状态重启。
第二个问题会在同一 Pod
中运行多个容器并共享文件时出现。
Kubernetes 卷(Volume)
这一抽象概念能够解决这两个问题。
阅读本文前建议你熟悉一下 Pod。
背景
Docker 也有卷(Volume) 的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。
Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。
使用卷时, 在 .spec.volumes
字段中设置为 Pod 提供的卷,并在
.spec.containers[*].volumeMounts
字段中声明卷在容器中的挂载位置。
容器中的进程看到的文件系统视图是由它们的容器镜像
的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。
其中根文件系统同容器镜像的内容相吻合。
任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。
卷挂载在镜像中的指定路径下。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。
卷不能挂载到其他卷之上(不过存在一种使用 subPath 的相关机制),也不能与其他卷有硬链接。
卷类型
Kubernetes 支持下列类型的卷:
awsElasticBlockStore (已弃用)
Kubernetes v1.17 [deprecated]
awsElasticBlockStore
卷将 Amazon Web 服务(AWS)EBS 卷挂载到你的
Pod 中。emptyDir
在 Pod 被删除时也会一起被删除,但 EBS 卷的内容在删除
Pod 时会被保留,卷只是被卸载掉了。
这意味着 EBS 卷可以预先填充数据,并且该数据可以在 Pod 之间共享。
你在使用 EBS 卷之前必须使用 aws ec2 create-volume
命令或者 AWS API 创建该卷。
使用 awsElasticBlockStore
卷时有一些限制:
- Pod 运行所在的节点必须是 AWS EC2 实例。
- 这些实例需要与 EBS 卷在相同的地域(Region)和可用区(Availability-Zone)。
- EBS 卷只支持被挂载到单个 EC2 实例上。
创建 AWS EBS 卷
在将 EBS 卷用到 Pod 上之前,你首先要创建它。
aws ec2 create-volume --availability-zone=eu-west-1a --size=10 --volume-type=gp2
确保该区域与你的集群所在的区域相匹配。还要检查卷的大小和 EBS 卷类型都适合你的用途。
AWS EBS 配置示例
apiVersion: v1
kind: Pod
metadata:
name: test-ebs
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-ebs
name: test-volume
volumes:
- name: test-volume
# 此 AWS EBS 卷必须已经存在
awsElasticBlockStore:
volumeID: "<volume id>"
fsType: ext4
如果 EBS 卷是分区的,你可以提供可选的字段 partition: "<partition number>"
来指定要挂载到哪个分区上。
AWS EBS CSI 卷迁移
Kubernetes v1.25 [stable]
启用 awsElasticBlockStore
的 CSIMigration
特性后,所有插件操作将从现有的树内插件重定向到
ebs.csi.aws.com
容器存储接口(CSI)驱动程序。
为了使用此特性,必须在集群中安装
AWS EBS CSI 驱动。
AWS EBS CSI 迁移结束
Kubernetes v1.17 [alpha]
要禁止控制器管理器和 kubelet 加载 awsElasticBlockStore
存储插件,
请将 InTreePluginAWSUnregister
标志设置为 true
。
azureDisk (已弃用)
Kubernetes v1.19 [deprecated]
azureDisk
卷类型用来在 Pod 上挂载 Microsoft Azure
数据盘(Data Disk) 。
若需了解更多详情,请参考 azureDisk
卷插件。
azureDisk 的 CSI 迁移
Kubernetes v1.24 [stable]
启用 azureDisk
的 CSIMigration
特性后,所有插件操作从现有的树内插件重定向到
disk.csi.azure.com
容器存储接口(CSI)驱动程序。
为了使用此特性,必须在集群中安装
Azure 磁盘 CSI 驱动程序。
azureDisk CSI 迁移完成
Kubernetes v1.21 [alpha]
要禁止控制器管理器和 kubelet 加载 azureDisk
存储插件,
请将 InTreePluginAzureDiskUnregister
标志设置为 true
。
azureFile (已弃用)
Kubernetes v1.21 [deprecated]
azureFile
卷类型用来在 Pod 上挂载 Microsoft Azure 文件卷(File Volume)(SMB 2.1 和 3.0)。
更多详情请参考 azureFile
卷插件。
azureFile CSI 迁移
Kubernetes v1.26 [stable]
启用 azureFile
的 CSIMigration
特性后,所有插件操作将从现有的树内插件重定向到
file.csi.azure.com
容器存储接口(CSI)驱动程序。要使用此特性,必须在集群中安装
Azure 文件 CSI 驱动程序,
并且 CSIMigrationAzureFile
特性门控
必须被启用。
Azure 文件 CSI 驱动尚不支持为同一卷设置不同的 fsgroup。
如果 CSIMigrationAzureFile
特性被启用,用不同的 fsgroup 来使用同一卷也是不被支持的。
azureFile CSI 迁移完成
Kubernetes v1.21 [alpha]
要禁止控制器管理器和 kubelet 加载 azureFile
存储插件,
请将 InTreePluginAzureFileUnregister
标志设置为 true
。
cephfs
cephfs
卷允许你将现存的 CephFS 卷挂载到 Pod 中。
不像 emptyDir
那样会在 Pod 被删除的同时也会被删除,cephfs
卷的内容在 Pod 被删除时会被保留,只是卷被卸载了。
这意味着 cephfs
卷可以被预先填充数据,且这些数据可以在
Pod 之间共享。同一 cephfs
卷可同时被多个写者挂载。
在使用 Ceph 卷之前,你的 Ceph 服务器必须已经运行并将要使用的 share 导出(exported)。
更多信息请参考 CephFS 示例。
cinder (已弃用)
Kubernetes v1.18 [deprecated]
Kubernetes 必须配置了 OpenStack Cloud Provider。
cinder
卷类型用于将 OpenStack Cinder 卷挂载到 Pod 中。
Cinder 卷示例配置
apiVersion: v1
kind: Pod
metadata:
name: test-cinder
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-cinder-container
volumeMounts:
- mountPath: /test-cinder
name: test-volume
volumes:
- name: test-volume
# 此 OpenStack 卷必须已经存在
cinder:
volumeID: "<volume id>"
fsType: ext4
OpenStack CSI 迁移
Kubernetes v1.24 [stable]
自 Kubernetes 1.21 版本起,Cinder 的 CSIMigration
特性是默认被启用的。
此特性会将插件的所有操作从现有的树内插件重定向到
cinder.csi.openstack.org
容器存储接口(CSI)驱动程序。
为了使用此特性,必须在集群中安装
OpenStack Cinder CSI 驱动程序,
你可以通过设置 CSIMigrationOpenStack
特性门控
为 false
来禁止 Cinder CSI 迁移。
要禁止控制器管理器和 kubelet 加载树内 Cinder 插件,你可以启用
InTreePluginOpenStackUnregister
特性门控。
configMap
configMap
卷提供了向 Pod 注入配置数据的方法。
ConfigMap 对象中存储的数据可以被 configMap
类型的卷引用,然后被 Pod 中运行的容器化应用使用。
引用 configMap 对象时,你可以在卷中通过它的名称来引用。
你可以自定义 ConfigMap 中特定条目所要使用的路径。
下面的配置显示了如何将名为 log-config
的 ConfigMap 挂载到名为 configmap-pod
的 Pod 中:
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox:1.28
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: log-config
items:
- key: log_level
path: log_level
log-config
ConfigMap 以卷的形式挂载,并且存储在 log_level
条目中的所有内容都被挂载到 Pod 的 /etc/config/log_level
路径下。
请注意,这个路径来源于卷的 mountPath
和 log_level
键对应的 path
。
downwardAPI
downwardAPI
卷用于为应用提供 downward API 数据。
在这类卷中,所公开的数据以纯文本格式的只读文件形式存在。
更多详细信息请参考通过文件将 Pod 信息呈现给容器。
emptyDir
当 Pod 分派到某个节点上时,emptyDir
卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。
就像其名称表示的那样,卷最初是空的。
尽管 Pod 中的容器挂载 emptyDir
卷的路径可能相同也可能不同,这些容器都可以读写
emptyDir
卷中相同的文件。
当 Pod 因为某些原因被从节点上删除时,emptyDir
卷中的数据也会被永久删除。
容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir
卷中的数据是安全的。
emptyDir
的一些用途:
- 缓存空间,例如基于磁盘的归并排序。
- 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
- 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
emptyDir.medium
字段用来控制 emptyDir
卷的存储位置。
默认情况下,emptyDir
卷存储在该节点所使用的介质上;
此处的介质可以是磁盘、SSD 或网络存储,这取决于你的环境。
你可以将 emptyDir.medium
字段设置为 "Memory"
,
以告诉 Kubernetes 为你挂载 tmpfs(基于 RAM 的文件系统)。
虽然 tmpfs 速度非常快,但是要注意它与磁盘不同:tmpfs 在节点重启时会被清除,
并且你所写入的所有文件都会计入容器的内存消耗,受容器内存限制约束。
你可以通过为默认介质指定大小限制,来限制 emptyDir
卷的存储容量。
此存储是从节点临时存储中分配的。
如果来自其他来源(如日志文件或镜像分层数据)的数据占满了存储,emptyDir
可能会在达到此限制之前发生存储容量不足的问题。
当启用 SizeMemoryBackedVolumes
特性门控时,
你可以为基于内存提供的卷指定大小。
如果未指定大小,则基于内存的卷的大小为 Linux 主机上内存的 50%。
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
fc (光纤通道)
fc
卷类型允许将现有的光纤通道块存储卷挂载到 Pod 中。
可以使用卷配置中的参数 targetWWNs
来指定单个或多个目标 WWN(World Wide Names)。
如果指定了多个 WWN,targetWWNs 期望这些 WWN 来自多路径连接。
你必须配置 FC SAN Zoning,以便预先向目标 WWN 分配和屏蔽这些 LUN(卷),这样 Kubernetes 主机才可以访问它们。
更多详情请参考 FC 示例。
gcePersistentDisk(已弃用)
Kubernetes v1.17 [deprecated]
gcePersistentDisk
卷能将谷歌计算引擎 (GCE) 持久盘(PD)
挂载到你的 Pod 中。
不像 emptyDir
那样会在 Pod 被删除的同时也会被删除,持久盘卷的内容在删除 Pod
时会被保留,卷只是被卸载了。
这意味着持久盘卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
在使用 PD 前,你必须使用 gcloud
或者 GCE API 或 UI 创建它。
使用 gcePersistentDisk
时有一些限制:
- 运行 Pod 的节点必须是 GCE VM
- 这些 VM 必须和持久盘位于相同的 GCE 项目和区域(zone)
GCE PD 的一个特点是它们可以同时被多个消费者以只读方式挂载。 这意味着你可以用数据集预先填充 PD,然后根据需要并行地在尽可能多的 Pod 中提供该数据集。 不幸的是,PD 只能由单个使用者以读写模式挂载,即不允许同时写入。
在由 ReplicationController 所管理的 Pod 上使用 GCE PD 将会失败,除非 PD 是只读模式或者副本的数量是 0 或 1。
创建 GCE 持久盘(PD)
在 Pod 中使用 GCE 持久盘之前,你首先要创建它。
gcloud compute disks create --size=500GB --zone=us-central1-a my-data-disk
GCE 持久盘配置示例
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
# 此 GCE PD 必须已经存在
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
区域持久盘
区域持久盘特性允许你创建能在同一区域的两个可用区中使用的持久盘。 要使用这个特性,必须以持久卷(PersistentVolume)的方式提供卷;直接从 Pod 引用这种卷是不可以的。
手动供应基于区域 PD 的 PersistentVolume
使用为 GCE PD 定义的存储类 可以实现动态供应。在创建 PersistentVolume 之前,你首先要创建 PD。
gcloud compute disks create --size=500GB my-data-disk
--region us-central1
--replica-zones us-central1-a,us-central1-b
区域持久盘配置示例
apiVersion: v1
kind: PersistentVolume
metadata:
name: test-volume
spec:
capacity:
storage: 400Gi
accessModes:
- ReadWriteOnce
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
# failure-domain.beta.kubernetes.io/zone 应在 1.21 之前使用
- key: topology.kubernetes.io/zone
operator: In
values:
- us-central1-a
- us-central1-b
GCE CSI 迁移
Kubernetes v1.25 [stable]
启用 GCE PD 的 CSIMigration
特性后,所有插件操作将从现有的树内插件重定向到
pd.csi.storage.gke.io
容器存储接口(CSI)驱动程序。
为了使用此特性,必须在集群中上安装
GCE PD CSI 驱动程序。
GCE CSI 迁移完成
Kubernetes v1.21 [alpha]
要禁止控制器管理器和 kubelet 加载 gcePersistentDisk
存储插件,请将
InTreePluginGCEUnregister
标志设置为 true
。
gitRepo (已弃用)
gitRepo
卷类型已经被废弃。如果需要在容器中提供 git 仓库,请将一个
EmptyDir 卷挂载到 InitContainer 中,使用 git
命令完成仓库的克隆操作,然后将 EmptyDir 卷挂载到 Pod 的容器中。
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.26 不包含 glusterfs
卷类型。
GlusterFS 树内存储驱动程序在 Kubernetes v1.25 版本中被弃用,然后在 v1.26 版本中被完全移除。
hostPath
HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用 HostPath 卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。
如果通过 AdmissionPolicy 限制 HostPath 对特定目录的访问,则必须要求
volumeMounts
使用 readOnly
挂载以使策略生效。
hostPath
卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。
虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。
例如,hostPath
的一些用法有:
- 运行一个需要访问 Docker 内部机制的容器;可使用
hostPath
挂载/var/lib/docker
路径。 - 在容器中运行 cAdvisor 时,以
hostPath
方式挂载/sys
。 - 允许 Pod 指定给定的
hostPath
在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。
除了必需的 path
属性之外,你可以选择性地为 hostPath
卷指定 type
。
支持的 type
值如下:
取值 | 行为 |
---|---|
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。 | |
DirectoryOrCreate |
如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。 |
Directory |
在给定路径上必须存在的目录。 |
FileOrCreate |
如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。 |
File |
在给定路径上必须存在的文件。 |
Socket |
在给定路径上必须存在的 UNIX 套接字。 |
CharDevice |
在给定路径上必须存在的字符设备。 |
BlockDevice |
在给定路径上必须存在的块设备。 |
当使用这种类型的卷时要小心,因为:
- HostPath 卷可能会暴露特权系统凭据(例如 Kubelet)或特权 API(例如容器运行时套接字),可用于容器逃逸或攻击集群的其他部分。
- 具有相同配置(例如基于同一 PodTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。
- 下层主机上创建的文件或目录只能由 root 用户写入。
你需要在特权容器中以
root 身份运行进程,或者修改主机上的文件权限以便容器能够写入
hostPath
卷。
hostPath 配置示例
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: registry.k8s.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# 宿主上目录位置
path: /data
# 此字段为可选
type: Directory
FileOrCreate
模式不会负责创建文件的父目录。
如果欲挂载的文件的父目录不存在,Pod 启动会失败。
为了确保这种模式能够工作,可以尝试把文件和它对应的目录分开挂载,如
FileOrCreate
配置 所示。
hostPath FileOrCreate 配置示例
apiVersion: v1
kind: Pod
metadata:
name: test-webserver
spec:
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:
# 确保文件所在目录成功创建。
path: /var/local/aaa
type: DirectoryOrCreate
- name: myfile
hostPath:
path: /var/local/aaa/1.txt
type: FileOrCreate
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
的持久卷示例:
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
信息来将使用 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 之间共享。
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 卷之前,你必须运行自己的 NFS 服务器并将目标 share 导出备用。
还需要注意,不能在 Pod spec 中指定 NFS 挂载可选项。 可以选择设置服务端的挂载可选项,或者使用 /etc/nfsmount.conf。 此外,还可以通过允许设置挂载可选项的持久卷挂载 NFS 卷。
如需了解用持久卷挂载 NFS 卷的示例,请参考 NFS 示例。
persistentVolumeClaim
persistentVolumeClaim
卷用来将持久卷(PersistentVolume)挂载到 Pod 中。
持久卷申领(PersistentVolumeClaim)是用户在不知道特定云环境细节的情况下“申领”持久存储(例如
GCE PersistentDisk 或者 iSCSI 卷)的一种方法。
更多详情请参考持久卷。
portworxVolume(已弃用)
Kubernetes v1.25 [deprecated]
portworxVolume
是一个可伸缩的块存储层,能够以超融合(hyperconverged)的方式与 Kubernetes 一起运行。
Portworx
支持对服务器上存储的指纹处理、基于存储能力进行分层以及跨多个服务器整合存储容量。
Portworx 可以以 in-guest 方式在虚拟机中运行,也可以在裸金属 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
# 此 Portworx 卷必须已经存在
portworxVolume:
volumeID: "pxvol"
fsType: "<fs-type>"
在 Pod 中使用 portworxVolume 之前,你要确保有一个名为 pxvol
的 PortworxVolume 存在。
更多详情可以参考 Portworx 卷。
Portworx CSI 迁移
Kubernetes v1.25 [beta]
已针对 Portworx 添加 CSIMigration
特性,但在 Kubernetes 1.23 中默认禁用,因为它处于 Alpha 状态。
自 v1.25 以来它已进入 Beta 阶段,但默认仍关闭。
它将所有插件操作不再指向树内插件(In-Tree Plugin),转而指向
pxd.portworx.com
容器存储接口(Container Storage Interface,CSI)驱动。
Portworx CSI 驱动程序必须安装在集群上。
要启用此特性,需在 kube-controller-manager 和 kubelet 中设置 CSIMigrationPortworx=true
。
projected (投射)
投射卷能将若干现有的卷来源映射到同一目录上。更多详情请参考投射卷。
rbd
rbd
卷允许将 Rados 块设备卷挂载到你的 Pod 中。
不像 emptyDir
那样会在删除 Pod 的同时也会被删除,rbd
卷的内容在删除 Pod 时会被保存,卷只是被卸载。
这意味着 rbd
卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
在使用 RBD 之前,你必须安装运行 Ceph。
RBD 的一个特性是它可以同时被多个用户以只读方式挂载。 这意味着你可以用数据集预先填充卷,然后根据需要在尽可能多的 Pod 中并行地使用卷。 不幸的是,RBD 卷只能由单个使用者以读写模式安装。不允许同时写入。
更多详情请参考 RBD 示例。
RBD CSI 迁移
Kubernetes v1.23 [alpha]
启用 RBD 的 CSIMigration
特性后,所有插件操作从现有的树内插件重定向到
rbd.csi.ceph.com
CSI 驱动程序。
要使用该特性,必须在集群内安装
Ceph CSI 驱动,并启用 csiMigrationRBD
特性门控。
(请注意,csiMigrationRBD
标志已在 v1.24 版本中移除且替换为 CSIMigrationRBD
。)
作为一位管理存储的 Kubernetes 集群操作者,在尝试迁移到 RBD CSI 驱动前,你必须完成下列先决事项:
- 你必须在集群中安装 v3.5.0 或更高版本的 Ceph CSI 驱动(
rbd.csi.ceph.com
)。 - 因为
clusterID
是 CSI 驱动程序必需的参数,而树内存储类又将monitors
作为一个必需的参数,所以 Kubernetes 存储管理者需要根据monitors
的哈希值(例:#echo -n '<monitors_string>' | md5sum
)来创建clusterID
,并保持该monitors
存在于该clusterID
的配置中。 - 同时,如果树内存储类的
adminId
的值不是admin
,那么其adminSecretName
就需要被修改成adminId
参数的 base64 编码值。
secret
secret
卷用来给 Pod 传递敏感信息,例如密码。你可以将 Secret 存储在 Kubernetes
API 服务器上,然后以文件的形式挂载到 Pod 中,无需直接与 Kubernetes 耦合。
secret
卷由 tmpfs(基于 RAM 的文件系统)提供存储,因此它们永远不会被写入非易失性(持久化的)存储器。
使用前你必须在 Kubernetes API 中创建 Secret。
容器以 subPath
卷挂载方式挂载 Secret 时,将感知不到 Secret 的更新。
更多详情请参考配置 Secrets。
vsphereVolume(已弃用)
建议你改用 vSphere CSI 树外驱动程序。
vsphereVolume
用来将 vSphere VMDK 卷挂载到你的 Pod 中。
在卸载卷时,卷的内容会被保留。
vSphereVolume 卷类型支持 VMFS 和 VSAN 数据仓库。
进一步信息可参考 vSphere 卷。
vSphere CSI 迁移
Kubernetes v1.26 [stable]
在 Kubernetes 1.26 中,对树内 vsphereVolume
类的所有操作都会被重定向至 csi.vsphere.vmware.com
CSI 驱动程序。
vSphere CSI 驱动必须安装到集群上。
你可以在 VMware 的文档页面迁移树内 vSphere 卷插件到 vSphere 容器存储插件
中找到有关如何迁移树内 vsphereVolume
的其他建议。
如果未安装 vSphere CSI 驱动程序,则无法对由树内 vsphereVolume
类型创建的 PV 执行卷操作。
你必须运行 vSphere 7.0u2 或更高版本才能迁移到 vSphere CSI 驱动程序。
如果你正在运行 Kubernetes v1.26,请查阅该 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
文件夹中:
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
Kubernetes v1.17 [stable]
使用 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
# 包裹变量名的是小括号,而不是大括号
subPathExpr: $(POD_NAME)
restartPolicy: Never
volumes:
- name: workdir1
hostPath:
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 驱动在CreateVolumeResponse
的volume_id
字段中返回的值相对应;接口定义在 CSI 规范 中。 在所有对 CSI 卷驱动程序的调用中,引用该 CSI 卷时都使用此值作为volume_id
参数。
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 驱动来完成 CSIControllerPublishVolume
和ControllerUnpublishVolume
调用。 此字段是可选的;在不需要 Secret 时可以是空的。 如果 Secret 包含多个 Secret 条目,则所有的 Secret 条目都会被传递。
nodeExpandSecretRef
:对包含敏感信息的 Secret 对象的引用, 该信息会传递给 CSI 驱动以完成 CSINodeExpandVolume
调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 包含多个 Secret 条目,则传递所有 Secret 条目。 当你为节点初始化的卷扩展配置 Secret 数据时,kubelet 会通过NodeExpandVolume()
调用将该数据传递给 CSI 驱动。 为了使用nodeExpandSecretRef
字段,你的集群应运行 Kubernetes 1.25 或更高版本, 并且你必须为每个 kube-apiserver 和每个节点上的 kubelet 启用名为CSINodeExpandSecret
的特性门控。 在节点初始化的存储大小调整操作期间,你还必须使用支持或需要 Secret 数据的 CSI 驱动。
nodePublishSecretRef
:对包含敏感信息的 Secret 对象的引用。 该信息传递给 CSI 驱动来完成 CSINodePublishVolume
调用。 此字段是可选的,如果不需要 Secret,则可能是空的。 如果 Secret 对象包含多个 Secret 条目,则传递所有 Secret 条目。
nodeStageSecretRef
:对包含敏感信息的 Secret 对象的引用, 该信息会传递给 CSI 驱动以完成 CSINodeStageVolume
调用。 此字段是可选的,如果不需要 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(指树内插件)进行任何配置更改。
所支持的操作和特性包括:配备(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。
卷的挂载传播特性由 Container.volumeMounts
中的 mountPropagation
字段控制。
它的值包括:
-
None
- 此卷挂载将不会感知到主机后续在此卷或其任何子目录上执行的挂载变化。 类似的,容器所创建的卷挂载在主机上是不可见的。这是默认模式。该模式等同于 Linux 内核文档中描述的
private
挂载传播选项。
-
HostToContainer
- 此卷挂载将会感知到主机后续针对此卷或其任何子目录的挂载操作。换句话说,如果主机在此挂载卷中挂载任何内容,容器将能看到它被挂载在那里。
类似的,配置了
Bidirectional
挂载传播选项的 Pod 如果在同一卷上挂载了内容,挂载传播设置为HostToContainer
的容器都将能看到这一变化。该模式等同于 Linux 内核文档中描述的
rslave
挂载传播选项。
-
Bidirectional
- 这种卷挂载和HostToContainer
挂载表现相同。 另外,容器创建的卷挂载将被传播回至主机和使用同一卷的所有 Pod 的所有容器。该模式等同于 Linux 内核文档中描述的
rshared
挂载传播选项。警告:Bidirectional
形式的挂载传播可能比较危险。 它可以破坏主机操作系统,因此它只被允许在特权容器中使用。 强烈建议你熟悉 Linux 内核行为。 此外,由 Pod 中的容器创建的任何卷挂载必须在终止时由容器销毁(卸载)。
配置
在某些部署环境中,挂载传播正常工作前,必须在 Docker 中正确配置挂载共享(mount share),如下所示。
编辑你的 Docker systemd
服务文件,按下面的方法设置 MountFlags
:
MountFlags=shared
或者,如果存在 MountFlags=slave
就删除掉。然后重启 Docker 守护进程:
sudo systemctl daemon-reload
sudo systemctl restart docker
接下来
2 - 持久卷
本文描述 Kubernetes 中的持久卷(Persistent Volume) 。 建议先熟悉卷(Volume)的概念。
介绍
存储的管理是一个与计算实例的管理完全不同的问题。 PersistentVolume 子系统为用户和管理员提供了一组 API, 将存储如何制备的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。
尽管 PersistentVolumeClaim 允许用户消耗抽象的存储资源, 常见的情况是针对不同的问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管理员需要能够提供不同性质的 PersistentVolume, 并且这些 PV 卷之间的差别不仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了存储类(StorageClass) 资源。
参见基于运行示例的详细演练。
卷和申领的生命周期
PV 卷是集群中的资源。PVC 申领是对这些资源的请求,也被用来执行对资源的申领检查。 PV 卷和 PVC 申领之间的互动遵循如下生命周期:
制备
PV 卷的制备有两种方式:静态制备或动态制备。
静态制备
集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
动态制备
如果管理员所创建的所有静态 PV 卷都无法与用户的 PersistentVolumeClaim 匹配,
集群可以尝试为该 PVC 申领动态制备一个存储卷。
这一制备操作是基于 StorageClass 来实现的:PVC 申领必须请求某个
存储类,
同时集群管理员必须已经创建并配置了该类,这样动态制备卷的动作才会发生。
如果 PVC 申领指定存储类为 ""
,则相当于为自身禁止使用动态制备的卷。
为了基于存储类完成动态的存储制备,集群管理员需要在 API 服务器上启用
DefaultStorageClass
准入控制器。
举例而言,可以通过保证 DefaultStorageClass
出现在 API 服务器组件的
--enable-admission-plugins
标志值中实现这点;该标志的值可以是逗号分隔的有序列表。
关于 API 服务器标志的更多信息,可以参考
kube-apiserver
文档。
绑定
用户创建一个带有特定存储容量和特定访问模式需求的 PersistentVolumeClaim 对象; 在动态制备场景下,这个 PVC 对象可能已经创建完毕。 主控节点中的控制回路监测新的 PVC 对象,寻找与之匹配的 PV 卷(如果可能的话), 并将二者绑定到一起。 如果为了新的 PVC 申领动态制备了 PV 卷,则控制回路总是将该 PV 卷绑定到这一 PVC 申领。 否则,用户总是能够获得他们所请求的资源,只是所获得的 PV 卷可能会超出所请求的配置。 一旦绑定关系建立,则 PersistentVolumeClaim 绑定就是排他性的, 无论该 PVC 申领是如何与 PV 卷建立的绑定关系。 PVC 申领与 PV 卷之间的绑定是一种一对一的映射,实现上使用 ClaimRef 来记述 PV 卷与 PVC 申领间的双向绑定关系。
如果找不到匹配的 PV 卷,PVC 申领会无限期地处于未绑定状态。 当与之匹配的 PV 卷可用时,PVC 申领会被绑定。 例如,即使某集群上制备了很多 50 Gi 大小的 PV 卷,也无法与请求 100 Gi 大小的存储的 PVC 匹配。当新的 100 Gi PV 卷被加入到集群时, 该 PVC 才有可能被绑定。
使用
Pod 将 PVC 申领当做存储卷来使用。集群会检视 PVC 申领,找到所绑定的卷, 并为 Pod 挂载该卷。对于支持多种访问模式的卷, 用户要在 Pod 中以卷的形式使用申领时指定期望的访问模式。
一旦用户有了申领对象并且该申领已经被绑定,
则所绑定的 PV 卷在用户仍然需要它期间一直属于该用户。
用户通过在 Pod 的 volumes
块中包含 persistentVolumeClaim
节区来调度 Pod,访问所申领的 PV 卷。
相关细节可参阅使用申领作为卷。
保护使用中的存储对象
保护使用中的存储对象(Storage Object in Use Protection) 这一功能特性的目的是确保仍被 Pod 使用的 PersistentVolumeClaim(PVC) 对象及其所绑定的 PersistentVolume(PV)对象在系统中不会被删除,因为这样做可能会引起数据丢失。
如果用户删除被某 Pod 使用的 PVC 对象,该 PVC 申领不会被立即移除。 PVC 对象的移除会被推迟,直至其不再被任何 Pod 使用。 此外,如果管理员删除已绑定到某 PVC 申领的 PV 卷,该 PV 卷也不会被立即移除。 PV 对象的移除也要推迟到该 PV 不再绑定到 PVC。
你可以看到当 PVC 的状态为 Terminating
且其 Finalizers
列表中包含
kubernetes.io/pvc-protection
时,PVC 对象是处于被保护状态的。
kubectl describe pvc hostpath
Name: hostpath
Namespace: default
StorageClass: example-hostpath
Status: Terminating
Volume:
Labels: <none>
Annotations: volume.beta.kubernetes.io/storage-class=example-hostpath
volume.beta.kubernetes.io/storage-provisioner=example.com/hostpath
Finalizers: [kubernetes.io/pvc-protection]
...
你也可以看到当 PV 对象的状态为 Terminating
且其 Finalizers
列表中包含
kubernetes.io/pv-protection
时,PV 对象是处于被保护状态的。
kubectl describe pv task-pv-volume
Name: task-pv-volume
Labels: type=local
Annotations: <none>
Finalizers: [kubernetes.io/pv-protection]
StorageClass: standard
Status: Terminating
Claim:
Reclaim Policy: Delete
Access Modes: RWO
Capacity: 1Gi
Message:
Source:
Type: HostPath (bare host directory volume)
Path: /tmp/data
HostPathType:
Events: <none>
回收(Reclaiming)
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。 目前,数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除)。
保留(Retain)
回收策略 Retain
使得用户可以手动回收资源。当 PersistentVolumeClaim
对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。
由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。
管理员可以通过下面的步骤来手动回收该卷:
- 删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在。
- 根据情况,手动清除所关联的存储资产上的数据。
- 手动删除所关联的存储资产。
如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。
删除(Delete)
对于支持 Delete
回收策略的卷插件,删除动作会将 PersistentVolume 对象从
Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或
Cinder 卷)中移除所关联的存储资产。
动态制备的卷会继承其 StorageClass 中设置的回收策略,
该策略默认为 Delete
。管理员需要根据用户的期望来配置 StorageClass;
否则 PV 卷被创建之后必须要被编辑或者修补。
参阅更改 PV 卷的回收策略。
回收(Recycle)
Recycle
已被废弃。取而代之的建议方案是使用动态制备。
如果下层的卷插件支持,回收策略 Recycle
会在卷上执行一些基本的擦除
(rm -rf /thevolume/*
)操作,之后允许该卷用于新的 PVC 申领。
不过,管理员可以按
参考资料
中所述,使用 Kubernetes 控制器管理器命令行参数来配置一个定制的回收器(Recycler)
Pod 模板。此定制的回收器 Pod 模板必须包含一个 volumes
规约,如下例所示:
apiVersion: v1
kind: Pod
metadata:
name: pv-recycler
namespace: default
spec:
restartPolicy: Never
volumes:
- name: vol
hostPath:
path: /any/path/it/will/be/replaced
containers:
- name: pv-recycler
image: "registry.k8s.io/busybox"
command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/* && test -z \"$(ls -A /scrub)\" || exit 1"]
volumeMounts:
- name: vol
mountPath: /scrub
定制回收器 Pod 模板中在 volumes
部分所指定的特定路径要替换为正被回收的卷的路径。
PersistentVolume 删除保护 finalizer
Kubernetes v1.23 [alpha]
可以在 PersistentVolume 上添加终结器(Finalizer),
以确保只有在删除对应的存储后才删除具有 Delete
回收策略的 PersistentVolume。
新引入的 kubernetes.io/pv-controller
和 external-provisioner.volume.kubernetes.io/finalizer
终结器仅会被添加到动态制备的卷上。
终结器 kubernetes.io/pv-controller
会被添加到树内插件卷上。
下面是一个例子:
kubectl describe pv pvc-74a498d6-3929-47e8-8c02-078c1ece4d78
Name: pvc-74a498d6-3929-47e8-8c02-078c1ece4d78
Labels: <none>
Annotations: kubernetes.io/createdby: vsphere-volume-dynamic-provisioner
pv.kubernetes.io/bound-by-controller: yes
pv.kubernetes.io/provisioned-by: kubernetes.io/vsphere-volume
Finalizers: [kubernetes.io/pv-protection kubernetes.io/pv-controller]
StorageClass: vcp-sc
Status: Bound
Claim: default/vcp-pvc-1
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: vSphereVolume (a Persistent Disk resource in vSphere)
VolumePath: [vsanDatastore] d49c4a62-166f-ce12-c464-020077ba5d46/kubernetes-dynamic-pvc-74a498d6-3929-47e8-8c02-078c1ece4d78.vmdk
FSType: ext4
StoragePolicyName: vSAN Default Storage Policy
Events: <none>
终结器 external-provisioner.volume.kubernetes.io/finalizer
会被添加到 CSI 卷上。下面是一个例子:
Name: pvc-2f0bab97-85a8-4552-8044-eb8be45cf48d
Labels: <none>
Annotations: pv.kubernetes.io/provisioned-by: csi.vsphere.vmware.com
Finalizers: [kubernetes.io/pv-protection external-provisioner.volume.kubernetes.io/finalizer]
StorageClass: fast
Status: Bound
Claim: demo-app/nginx-logs
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 200Mi
Node Affinity: <none>
Message:
Source:
Type: CSI (a Container Storage Interface (CSI) volume source)
Driver: csi.vsphere.vmware.com
FSType: ext4
VolumeHandle: 44830fa8-79b4-406b-8b58-621ba25353fd
ReadOnly: false
VolumeAttributes: storage.kubernetes.io/csiProvisionerIdentity=1648442357185-8081-csi.vsphere.vmware.com
type=vSphere CNS Block Volume
Events: <none>
当为特定的树内卷插件启用了 CSIMigration{provider}
特性标志时,kubernetes.io/pv-controller
终结器将被替换为 external-provisioner.volume.kubernetes.io/finalizer
终结器。
预留 PersistentVolume
控制平面可以在集群中将 PersistentVolumeClaims 绑定到匹配的 PersistentVolumes。 但是,如果你希望 PVC 绑定到特定 PV,则需要预先绑定它们。
通过在 PersistentVolumeClaim 中指定 PersistentVolume,你可以声明该特定
PV 与 PVC 之间的绑定关系。如果该 PersistentVolume 存在且未被通过其
claimRef
字段预留给 PersistentVolumeClaim,则该 PersistentVolume
会和该 PersistentVolumeClaim 绑定到一起。
绑定操作不会考虑某些卷匹配条件是否满足,包括节点亲和性等等。 控制面仍然会检查存储类、 访问模式和所请求的存储尺寸都是合法的。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: foo-pvc
namespace: foo
spec:
storageClassName: "" # 此处须显式设置空字符串,否则会被设置为默认的 StorageClass
volumeName: foo-pv
...
此方法无法对 PersistentVolume 的绑定特权做出任何形式的保证。
如果有其他 PersistentVolumeClaim 可以使用你所指定的 PV,
则你应该首先预留该存储卷。你可以将 PV 的 claimRef
字段设置为相关的
PersistentVolumeClaim 以确保其他 PVC 不会绑定到该 PV 卷。
apiVersion: v1
kind: PersistentVolume
metadata:
name: foo-pv
spec:
storageClassName: ""
claimRef:
name: foo-pvc
namespace: foo
...
如果你想要使用 claimPolicy
属性设置为 Retain
的 PersistentVolume 卷时,
包括你希望复用现有的 PV 卷时,这点是很有用的
扩充 PVC 申领
Kubernetes v1.24 [stable]
现在,对扩充 PVC 申领的支持默认处于被启用状态。你可以扩充以下类型的卷:
- azureDisk
- azureFile
- awsElasticBlockStore
- cinder (已弃用)
- csi
- flexVolume (已弃用)
- gcePersistentDisk
- glusterfs (已弃用)
- rbd
- portworxVolume
只有当 PVC 的存储类中将 allowVolumeExpansion
设置为 true 时,你才可以扩充该 PVC 申领。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: example-vol-default
provisioner: vendor-name.example/magicstorage
parameters:
resturl: "http://192.168.10.100:8080"
restuser: ""
secretNamespace: ""
secretName: ""
allowVolumeExpansion: true
如果要为某 PVC 请求较大的存储卷,可以编辑 PVC 对象,设置一个更大的尺寸值。 这一编辑操作会触发为下层 PersistentVolume 提供存储的卷的扩充。 Kubernetes 不会创建新的 PV 卷来满足此申领的请求。 与之相反,现有的卷会被调整大小。
.spec
进行编辑,使该 PersistentVolumeClaim
的大小匹配 PersistentVolume 的话,则不会发生存储大小的调整。
Kubernetes 控制平面将看到两个资源的所需状态匹配,
并认为其后备卷的大小已被手动增加,无需调整。
CSI 卷的扩充
Kubernetes v1.24 [stable]
对 CSI 卷的扩充能力默认是被启用的,不过扩充 CSI 卷要求 CSI 驱动支持卷扩充操作。可参阅特定 CSI 驱动的文档了解更多信息。
重设包含文件系统的卷的大小
只有卷中包含的文件系统是 XFS、Ext3 或者 Ext4 时,你才可以重设卷的大小。
当卷中包含文件系统时,只有在 Pod 使用 ReadWrite
模式来使用 PVC
申领的情况下才能重设其文件系统的大小。文件系统扩充的操作或者是在 Pod
启动期间完成,或者在下层文件系统支持在线扩充的前提下在 Pod 运行期间完成。
如果 FlexVolumes 的驱动将 RequiresFSResize
能力设置为 true
,
则该 FlexVolume 卷(于 Kubernetes v1.23 弃用)可以在 Pod 重启期间调整大小。
重设使用中 PVC 申领的大小
Kubernetes v1.24 [stable]
在这种情况下,你不需要删除和重建正在使用某现有 PVC 的 Pod 或 Deployment。 所有使用中的 PVC 在其文件系统被扩充之后,立即可供其 Pod 使用。 此功能特性对于没有被 Pod 或 Deployment 使用的 PVC 而言没有效果。 你必须在执行扩展操作之前创建一个使用该 PVC 的 Pod。
与其他卷类型类似,FlexVolume 卷也可以在被 Pod 使用期间执行扩充操作。
处理扩充卷过程中的失败
如果用户指定的新大小过大,底层存储系统无法满足,PVC 的扩展将不断重试, 直到用户或集群管理员采取一些措施。这种情况是不希望发生的,因此 Kubernetes 提供了以下从此类故障中恢复的方法。
如果扩充下层存储的操作失败,集群管理员可以手动地恢复 PVC 申领的状态并取消重设大小的请求。否则,在没有管理员干预的情况下, 控制器会反复重试重设大小的操作。
- 将绑定到 PVC 申领的 PV 卷标记为
Retain
回收策略。 - 删除 PVC 对象。由于 PV 的回收策略为
Retain
,我们不会在重建 PVC 时丢失数据。 - 删除 PV 规约中的
claimRef
项,这样新的 PVC 可以绑定到该卷。 这一操作会使得 PV 卷变为 "可用(Available)"。 - 使用小于 PV 卷大小的尺寸重建 PVC,设置 PVC 的
volumeName
字段为 PV 卷的名称。 这一操作将把新的 PVC 对象绑定到现有的 PV 卷。 - 不要忘记恢复 PV 卷上设置的回收策略。
Kubernetes v1.23 [alpha]
RecoverVolumeExpansionFailure
必须被启用以允许使用此特性。
可参考特性门控
文档了解更多信息。
如果集群中的特性门控 RecoverVolumeExpansionFailure
已启用,在 PVC 的扩展发生失败时,你可以使用比先前请求的值更小的尺寸来重试扩展。
要使用一个更小的尺寸尝试请求新的扩展,请编辑该 PVC 的 .spec.resources
并选择一个比你之前所尝试的值更小的值。
如果由于容量限制而无法成功扩展至更高的值,这将很有用。
如果发生了这种情况,或者你怀疑可能发生了这种情况,
你可以通过指定一个在底层存储制备容量限制内的尺寸来重试扩展。
你可以通过查看 .status.resizeStatus
以及 PVC 上的事件来监控调整大小操作的状态。
请注意,
尽管你可以指定比之前的请求更低的存储量,新值必须仍然高于 .status.capacity
。
Kubernetes 不支持将 PVC 缩小到小于其当前的尺寸。
持久卷的类型
PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件:
cephfs
- CephFS volumecsi
- 容器存储接口 (CSI)fc
- Fibre Channel (FC) 存储hostPath
- HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用local
卷作为替代)iscsi
- iSCSI (SCSI over IP) 存储local
- 节点上挂载的本地存储设备nfs
- 网络文件系统 (NFS) 存储rbd
- Rados 块设备 (RBD) 卷
以下的持久卷已被弃用。这意味着当前仍是支持的,但是 Kubernetes 将来的发行版会将其移除。
awsElasticBlockStore
- AWS 弹性块存储(EBS) (于 v1.17 弃用)azureDisk
- Azure Disk (于 v1.19 弃用)azureFile
- Azure File (于 v1.21 弃用)cinder
- Cinder(OpenStack 块存储)(于 v1.18 弃用)flexVolume
- FlexVolume (于 v1.23 弃用)gcePersistentDisk
- GCE Persistent Disk (于 v1.17 弃用)glusterfs
- Glusterfs 卷 (于 v1.25 弃用)portworxVolume
- Portworx 卷 (于 v1.25 弃用)vsphereVolume
- vSphere VMDK 卷 (于 v1.19 弃用)
旧版本的 Kubernetes 仍支持这些“树内(In-Tree)”持久卷类型:
photonPersistentDisk
- Photon 控制器持久化盘。(从 v1.15 版本开始将不可用)scaleIO
- ScaleIO 卷(v1.21 之后不可用)flocker
- Flocker 存储 (v1.25 之后不可用)quobyte
- Quobyte 卷 (v1.25 之后不可用)storageos
- StorageOS 卷 (v1.25 之后不可用)
持久卷
每个 PV 对象都包含 spec
部分和 status
部分,分别对应卷的规约和状态。
PersistentVolume 对象的名称必须是合法的
DNS 子域名.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2
/sbin/mount.nfs
来支持挂载 NFS 文件系统。
容量
一般而言,每个 PV 卷都有确定的存储容量。
容量属性是使用 PV 对象的 capacity
属性来设置的。
参考词汇表中的量纲(Quantity)
词条,了解 capacity
字段可以接受的单位。
目前,存储大小是可以设置和请求的唯一资源。 未来可能会包含 IOPS、吞吐量等属性。
卷模式
Kubernetes v1.18 [stable]
针对 PV 持久卷,Kubernetes
支持两种卷模式(volumeModes
):Filesystem(文件系统)
和 Block(块)
。
volumeMode
是一个可选的 API 参数。
如果该参数被省略,默认的卷模式是 Filesystem
。
volumeMode
属性设置为 Filesystem
的卷会被 Pod 挂载(Mount) 到某个目录。
如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统。
你可以将 volumeMode
设置为 Block
,以便将卷作为原始块设备来使用。
这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。
这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助,
Pod 和卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备。
关于如何在 Pod 中使用 volumeMode: Block
的卷,
可参阅原始块卷支持。
访问模式
PersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为对应卷所支持的模式值。 例如,NFS 可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器上以只读的方式导出。 每个 PV 卷都会获得自身的访问模式集合,描述的是特定 PV 卷的能力。
访问模式有:
ReadWriteOnce
- 卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷。
ReadOnlyMany
- 卷可以被多个节点以只读方式挂载。
ReadWriteMany
- 卷可以被多个节点以读写方式挂载。
ReadWriteOncePod
- 卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。
这篇博客文章 Introducing Single Pod Access Mode for PersistentVolumes 描述了更详细的内容。
在命令行接口(CLI)中,访问模式也使用以下缩写形式:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
- RWOP - ReadWriteOncePod
Kubernetes 使用卷访问模式来匹配 PersistentVolumeClaim 和 PersistentVolume。 在某些场合下,卷访问模式也会限制 PersistentVolume 可以挂载的位置。 卷访问模式并不会在存储已经被挂载的情况下为其实施写保护。 即使访问模式设置为 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany,它们也不会对卷形成限制。 例如,即使某个卷创建时设置为 ReadOnlyMany,也无法保证该卷是只读的。 如果访问模式设置为 ReadWriteOncePod,则卷会被限制起来并且只能挂载到一个 Pod 上。
重要提醒! 每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。 例如,一个 GCEPersistentDisk 卷可以被某节点以 ReadWriteOnce 模式挂载,或者被多个节点以 ReadOnlyMany 模式挂载,但不可以同时以两种模式挂载。
卷插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany | ReadWriteOncePod |
---|---|---|---|---|
AWSElasticBlockStore | ✓ | - | - | - |
AzureFile | ✓ | ✓ | ✓ | - |
AzureDisk | ✓ | - | - | - |
CephFS | ✓ | ✓ | ✓ | - |
Cinder | ✓ | - | (如果多次挂接卷可用) | - |
CSI | 取决于驱动 | 取决于驱动 | 取决于驱动 | 取决于驱动 |
FC | ✓ | ✓ | - | - |
FlexVolume | ✓ | ✓ | 取决于驱动 | - |
GCEPersistentDisk | ✓ | ✓ | - | - |
Glusterfs | ✓ | ✓ | ✓ | - |
HostPath | ✓ | - | - | - |
iSCSI | ✓ | ✓ | - | - |
NFS | ✓ | ✓ | ✓ | - |
RBD | ✓ | ✓ | - | - |
VsphereVolume | ✓ | - | -(Pod 运行于同一节点上时可行) | - |
PortworxVolume | ✓ | - | ✓ | - |
类
每个 PV 可以属于某个类(Class),通过将其 storageClassName
属性设置为某个
StorageClass 的名称来指定。
特定类的 PV 卷只能绑定到请求该类存储卷的 PVC 申领。
未设置 storageClassName
的 PV 卷没有类设定,只能绑定到那些没有指定特定存储类的 PVC 申领。
早前,Kubernetes 使用注解 volume.beta.kubernetes.io/storage-class
而不是
storageClassName
属性。这一注解目前仍然起作用,不过在将来的 Kubernetes
发布版本中该注解会被彻底废弃。
回收策略
目前的回收策略有:
- Retain -- 手动回收
- Recycle -- 基本擦除 (
rm -rf /thevolume/*
) - Delete -- 诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除
目前,仅 NFS 和 HostPath 支持回收(Recycle)。 AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除(Delete)。
挂载选项
Kubernetes 管理员可以指定持久卷被挂载到节点上时使用的附加挂载选项。
以下卷类型支持挂载选项:
awsElasticBlockStore
azureDisk
azureFile
cephfs
cinder
(于 v1.18 弃用)gcePersistentDisk
glusterfs
(于 v1.25 弃用)iscsi
nfs
rbd
vsphereVolume
Kubernetes 不对挂载选项执行合法性检查。如果挂载选项是非法的,挂载就会失败。
早前,Kubernetes 使用注解 volume.beta.kubernetes.io/mount-options
而不是
mountOptions
属性。这一注解目前仍然起作用,不过在将来的 Kubernetes
发布版本中该注解会被彻底废弃。
节点亲和性
每个 PV 卷可以通过设置节点亲和性来定义一些约束,进而限制从哪些节点上可以访问此卷。
使用这些卷的 Pod 只会被调度到节点亲和性规则所选择的节点上执行。
要设置节点亲和性,配置 PV 卷 .spec
中的 nodeAffinity
。
持久卷
API 参考关于该字段的更多细节。
阶段
每个卷会处于以下阶段(Phase)之一:
- Available(可用)-- 卷是一个空闲资源,尚未绑定到任何申领;
- Bound(已绑定)-- 该卷已经绑定到某申领;
- Released(已释放)-- 所绑定的申领已被删除,但是资源尚未被集群回收;
- Failed(失败)-- 卷的自动回收操作失败。
命令行接口能够显示绑定到某 PV 卷的 PVC 对象。
PersistentVolumeClaims
每个 PVC 对象都有 spec
和 status
部分,分别对应申领的规约和状态。
PersistentVolumeClaim 对象的名称必须是合法的
DNS 子域名.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
matchExpressions:
- {key: environment, operator: In, values: [dev]}
访问模式
申领在请求具有特定访问模式的存储时,使用与卷相同的访问模式约定。
卷模式
申领使用与卷相同的约定来表明是将卷作为文件系统还是块设备来使用。
资源
申领和 Pod 一样,也可以请求特定数量的资源。在这个上下文中,请求的资源是存储。 卷和申领都使用相同的 资源模型。
选择算符
申领可以设置标签选择算符 来进一步过滤卷集合。只有标签与选择算符相匹配的卷能够绑定到申领上。 选择算符包含两个字段:
matchLabels
- 卷必须包含带有此值的标签matchExpressions
- 通过设定键(key)、值列表和操作符(operator) 来构造的需求。合法的操作符有 In、NotIn、Exists 和 DoesNotExist。
来自 matchLabels
和 matchExpressions
的所有需求都按逻辑与的方式组合在一起。
这些需求都必须被满足才被视为匹配。
类
申领可以通过为 storageClassName
属性设置
StorageClass 的名称来请求特定的存储类。
只有所请求的类的 PV 卷,即 storageClassName
值与 PVC 设置相同的 PV 卷,
才能绑定到 PVC 申领。
PVC 申领不必一定要请求某个类。如果 PVC 的 storageClassName
属性值设置为 ""
,
则被视为要请求的是没有设置存储类的 PV 卷,因此这一 PVC 申领只能绑定到未设置存储类的
PV 卷(未设置注解或者注解值为 ""
的 PersistentVolume(PV)对象在系统中不会被删除,
因为这样做可能会引起数据丢失。未设置 storageClassName
的 PVC 与此大不相同,
也会被集群作不同处理。具体筛查方式取决于
DefaultStorageClass
准入控制器插件
是否被启用。
- 如果准入控制器插件被启用,则管理员可以设置一个默认的 StorageClass。
所有未设置
storageClassName
的 PVC 都只能绑定到隶属于默认存储类的 PV 卷。 设置默认 StorageClass 的工作是通过将对应 StorageClass 对象的注解storageclass.kubernetes.io/is-default-class
赋值为true
来完成的。 如果管理员未设置默认存储类,集群对 PVC 创建的处理方式与未启用准入控制器插件时相同。 如果设定的默认存储类不止一个,准入控制插件会禁止所有创建 PVC 操作。 - 如果准入控制器插件被关闭,则不存在默认 StorageClass 的说法。
所有将
storageClassName
设为""
的 PVC 只能被绑定到也将storageClassName
设为""
的 PV。 不过,只要默认的 StorageClass 可用,就可以稍后更新缺少storageClassName
的 PVC。 如果这个 PVC 更新了,它将不再绑定到也将storageClassName
设为""
的 PV。
参阅可追溯的默认 StorageClass 赋值了解更多详细信息。
取决于安装方法,默认的 StorageClass 可能在集群安装期间由插件管理器(Addon Manager)部署到集群中。
当某 PVC 除了请求 StorageClass 之外还设置了 selector
,则这两种需求会按逻辑与关系处理:
只有隶属于所请求类且带有所请求标签的 PV 才能绑定到 PVC。
selector
的 PVC 对象无法让集群为其动态制备 PV 卷。
早前,Kubernetes 使用注解 volume.beta.kubernetes.io/storage-class
而不是
storageClassName
属性。这一注解目前仍然起作用,不过在将来的 Kubernetes
发布版本中该注解会被彻底废弃。
可追溯的默认 StorageClass 赋值
Kubernetes v1.25 [alpha]
你可以创建 PersistentVolumeClaim,而无需为新 PVC 指定 storageClassName
。
即使你的集群中不存在默认 StorageClass,你也可以这样做。
在这种情况下,新的 PVC 会按照你的定义进行创建,并且在默认值可用之前,该 PVC 的 storageClassName
保持不设置。
但是,如果你启用了 RetroactiveDefaultStorageClass
特性门控,
则 Kubernetes 的行为会有所不同:现有 PVC 无需更新 storageClassName
就能使用新的默认 StorageClass。
当一个默认的 StorageClass 变得可用时,控制平面会识别所有未设置 storageClassName
的现有 PVC。
对于 storageClassName
为空值或没有此主键的 PVC,
控制平面会更新这些 PVC 以设置其 storageClassName
与新的默认 StorageClass 匹配。
如果你有一个现有的 PVC,其中 storageClassName
是 ""
,
并且你配置了默认 StorageClass,则此 PVC 将不会得到更新。
为了保持绑定到 storageClassName
设为 ""
的 PV(当存在默认 StorageClass 时),
你需要将关联 PVC 的 storageClassName
设置为 ""
。
此行为可帮助管理员更改默认 StorageClass,方法是先移除旧的 PVC,然后再创建或设置另一个 PVC。
这一时间窗口内因为没有指定默认值,会导致所创建的未设置 storageClassName
的 PVC 也没有默认值设置,
但由于默认 StorageClass 赋值是可追溯的,这种更改默认值的方式是安全的。
使用申领作为卷
Pod 将申领作为卷来使用,并藉此访问存储资源。 申领必须位于使用它的 Pod 所在的同一名字空间内。 集群在 Pod 的名字空间中查找申领,并使用它来获得申领所使用的 PV 卷。 之后,卷会被挂载到宿主上并挂载到 Pod 中。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
关于名字空间的说明
PersistentVolume 卷的绑定是排他性的。
由于 PersistentVolumeClaim 是名字空间作用域的对象,使用
"Many" 模式(ROX
、RWX
)来挂载申领的操作只能在同一名字空间内进行。
类型为 hostpath
的 PersistentVolume
hostPath
PersistentVolume 使用节点上的文件或目录来模拟网络附加(network-attached)存储。
相关细节可参阅 hostPath
卷示例。
原始块卷支持
Kubernetes v1.18 [stable]
以下卷插件支持原始块卷,包括其动态制备(如果支持的话)的卷:
- AWSElasticBlockStore
- AzureDisk
- CSI
- FC (光纤通道)
- GCEPersistentDisk
- iSCSI
- Local 卷
- OpenStack Cinder
- RBD (Ceph 块设备)
- VsphereVolume
使用原始块卷的持久卷
apiVersion: v1
kind: PersistentVolume
metadata:
name: block-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
volumeMode: Block
persistentVolumeReclaimPolicy: Retain
fc:
targetWWNs: ["50060e801049cfd1"]
lun: 0
readOnly: false
申请原始块卷的 PVC 申领
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: block-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block
resources:
requests:
storage: 10Gi
在容器中添加原始块设备路径的 Pod 规约
apiVersion: v1
kind: Pod
metadata:
name: pod-with-block-volume
spec:
containers:
- name: fc-container
image: fedora:26
command: ["/bin/sh", "-c"]
args: [ "tail -f /dev/null" ]
volumeDevices:
- name: data
devicePath: /dev/xvda
volumes:
- name: data
persistentVolumeClaim:
claimName: block-pvc
绑定块卷
如果用户通过 PersistentVolumeClaim 规约的 volumeMode
字段来表明对原始块设备的请求,
绑定规则与之前版本中未在规约中考虑此模式的实现略有不同。
下面列举的表格是用户和管理员可以为请求原始块设备所作设置的组合。
此表格表明在不同的组合下卷是否会被绑定。
静态制备卷的卷绑定矩阵:
PV volumeMode | PVC volumeMode | Result |
---|---|---|
未指定 | 未指定 | 绑定 |
未指定 | Block | 不绑定 |
未指定 | Filesystem | 绑定 |
Block | 未指定 | 不绑定 |
Block | Block | 绑定 |
Block | Filesystem | 不绑定 |
Filesystem | Filesystem | 绑定 |
Filesystem | Block | 不绑定 |
Filesystem | 未指定 | 绑定 |
对卷快照及从卷快照中恢复卷的支持
Kubernetes v1.20 [stable]
卷快照(Volume Snapshot)仅支持树外 CSI 卷插件。 有关细节可参阅卷快照文档。 树内卷插件被弃用。你可以查阅卷插件 FAQ 了解已弃用的卷插件。
基于卷快照创建 PVC 申领
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restore-pvc
spec:
storageClassName: csi-hostpath-sc
dataSource:
name: new-snapshot-test
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
卷克隆
卷克隆功能特性仅适用于 CSI 卷插件。
基于现有 PVC 创建新的 PVC 申领
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cloned-pvc
spec:
storageClassName: my-csi-plugin
dataSource:
name: existing-src-pvc-name
kind: PersistentVolumeClaim
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
卷填充器(Populator)与数据源
Kubernetes v1.24 [beta]
Kubernetes 支持自定义的卷填充器。要使用自定义的卷填充器,你必须为
kube-apiserver 和 kube-controller-manager 启用 AnyVolumeDataSource
特性门控。
卷填充器利用了 PVC 规约字段 dataSourceRef
。
不像 dataSource
字段只能包含对另一个持久卷申领或卷快照的引用,
dataSourceRef
字段可以包含对同一命名空间中任何对象的引用(不包含除 PVC 以外的核心资源)。
对于启用了特性门控的集群,使用 dataSourceRef
比 dataSource
更好。
数据源引用
dataSourceRef
字段的行为与 dataSource
字段几乎相同。
如果其中一个字段被指定而另一个字段没有被指定,API 服务器将给两个字段相同的值。
这两个字段都不能在创建后改变,如果试图为这两个字段指定不同的值,将导致验证错误。
因此,这两个字段将总是有相同的内容。
在 dataSourceRef
字段和 dataSource
字段之间有两个用户应该注意的区别:
dataSource
字段会忽略无效的值(如同是空值), 而dataSourceRef
字段永远不会忽略值,并且若填入一个无效的值,会导致错误。 无效值指的是 PVC 之外的核心对象(没有 apiGroup 的对象)。dataSourceRef
字段可以包含不同类型的对象,而dataSource
字段只允许 PVC 和卷快照。
用户应该始终在启用了特性门控的集群上使用 dataSourceRef
,而在没有启用特性门控的集群上使用 dataSource
。
在任何情况下都没有必要查看这两个字段。
这两个字段的值看似相同但是语义稍微不一样,是为了向后兼容。
特别是混用旧版本和新版本的控制器时,它们能够互通。
使用卷填充器
卷填充器是能创建非空卷的控制器,
其卷的内容通过一个自定义资源决定。
用户通过使用 dataSourceRef
字段引用自定义资源来创建一个被填充的卷:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: populated-pvc
spec:
dataSourceRef:
name: example-name
kind: ExampleDataSource
apiGroup: example.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
因为卷填充器是外部组件,如果没有安装所有正确的组件,试图创建一个使用卷填充器的 PVC 就会失败。 外部控制器应该在 PVC 上产生事件,以提供创建状态的反馈,包括在由于缺少某些组件而无法创建 PVC 的情况下发出警告。
你可以把 alpha 版本的卷数据源验证器 控制器安装到你的集群中。 如果没有填充器处理该数据源的情况下,该控制器会在 PVC 上产生警告事件。 当一个合适的填充器被安装到 PVC 上时,该控制器的职责是上报与卷创建有关的事件,以及在该过程中发生的问题。
编写可移植的配置
如果你要编写配置模板和示例用来在很多集群上运行并且需要持久性存储,建议你使用以下模式:
- 将 PersistentVolumeClaim 对象包含到你的配置包(Bundle)中,和 Deployment 以及 ConfigMap 等放在一起。
- 不要在配置中包含 PersistentVolume 对象,因为对配置进行实例化的用户很可能 没有创建 PersistentVolume 的权限。
- 为用户提供在实例化模板时指定存储类名称的能力。
- 仍按用户提供存储类名称,将该名称放到
persistentVolumeClaim.storageClassName
字段中。 这样会使得 PVC 在集群被管理员启用了存储类支持时能够匹配到正确的存储类, - 如果用户未指定存储类名称,将
persistentVolumeClaim.storageClassName
留空(nil)。 这样,集群会使用默认StorageClass
为用户自动制备一个存储卷。 很多集群环境都配置了默认的StorageClass
,或者管理员也可以自行创建默认的StorageClass
。
- 仍按用户提供存储类名称,将该名称放到
- 在你的工具链中,监测经过一段时间后仍未被绑定的 PVC 对象,要让用户知道这些对象, 因为这可能意味着集群不支持动态存储(因而用户必须先创建一个匹配的 PV),或者 集群没有配置存储系统(因而用户无法配置需要 PVC 的工作负载配置)。
接下来
API 参考
阅读以下页面中描述的 API:
3 - 投射卷
本文档描述 Kubernetes 中的投射卷(Projected Volumes)。 建议先熟悉卷概念。
介绍
一个 projected
卷可以将若干现有的卷源映射到同一个目录之上。
目前,以下类型的卷源可以被投射:
所有的卷源都要求处于 Pod 所在的同一个名字空间内。更多详细信息, 可参考一体化卷设计文档。
带有 Secret、DownwardAPI 和 ConfigMap 的配置示例
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox:1.28
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "cpu_limit"
resourceFieldRef:
containerName: container-test
resource: limits.cpu
- configMap:
name: myconfigmap
items:
- key: config
path: my-group/my-config
带有非默认权限模式设置的 Secret 的配置示例
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox:1.28
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- secret:
name: mysecret2
items:
- key: password
path: my-group/my-password
mode: 511
每个被投射的卷源都列举在规约中的 sources
下面。参数几乎相同,只有两个例外:
- 对于 Secret,
secretName
字段被改为name
以便于 ConfigMap 的命名一致; defaultMode
只能在投射层级设置,不能在卷源层级设置。不过,正如上面所展示的, 你可以显式地为每个投射单独设置mode
属性。
serviceAccountToken 投射卷
你可以将当前服务账号的令牌注入到 Pod 中特定路径下。例如:
apiVersion: v1
kind: Pod
metadata:
name: sa-token-test
spec:
containers:
- name: container-test
image: busybox:1.28
volumeMounts:
- name: token-vol
mountPath: "/service-account"
readOnly: true
serviceAccountName: default
volumes:
- name: token-vol
projected:
sources:
- serviceAccountToken:
audience: api
expirationSeconds: 3600
path: token
示例 Pod 中包含一个投射卷,其中包含注入的服务账号令牌。
此 Pod 中的容器可以使用该令牌访问 Kubernetes API 服务器, 使用
Pod 的 ServiceAccount
进行身份验证。audience
字段包含令牌所针对的受众。
收到令牌的主体必须使用令牌受众中所指定的某个标识符来标识自身,否则应该拒绝该令牌。
此字段是可选的,默认值为 API 服务器的标识。
字段 expirationSeconds
是服务账号令牌预期的生命期长度。默认值为 1 小时,
必须至少为 10 分钟(600 秒)。管理员也可以通过设置 API 服务器的命令行参数
--service-account-max-token-expiration
来为其设置最大值上限。
path
字段给出与投射卷挂载点之间的相对路径。
以 subPath
形式使用投射卷源的容器无法收到对应卷源的更新。
与 SecurityContext 间的关系
关于在投射的服务账号卷中处理文件访问权限的提案 介绍了如何使得所投射的文件具有合适的属主访问权限。
Linux
在包含了投射卷并在
SecurityContext
中设置了 RunAsUser
属性的 Linux Pod 中,投射文件具有正确的属主属性设置,
其中包含了容器用户属主。
当 Pod 中的所有容器在其
PodSecurityContext
或容器
SecurityContext
中设置了相同的 runAsUser
时,kubelet 将确保 serviceAccountToken
卷的内容归该用户所有,并且令牌文件的权限模式会被设置为 0600
。
在某 Pod 被创建后为其添加的临时容器不会更改创建该 Pod 时设置的卷权限。
如果 Pod 的 serviceAccountToken
卷权限被设为 0600
是因为 Pod 中的其他所有容器都具有相同的 runAsUser
,
则临时容器必须使用相同的 runAsUser
才能读取令牌。
Windows
在包含了投射卷并在 SecurityContext
中设置了 RunAsUsername
的 Windows Pod 中,
由于 Windows 中用户账号的管理方式问题,文件的属主无法正确设置。
Windows 在名为安全账号管理器(Security Account Manager,SAM)
的数据库中保存本地用户和组信息。每个容器会维护其自身的 SAM 数据库实例,
宿主系统无法窥视到容器运行期间数据库内容。Windows 容器被设计用来运行操作系统的用户态部分,
与宿主系统之间隔离,因此维护了一个虚拟的 SAM 数据库。
所以,在宿主系统上运行的 kubelet 无法动态为虚拟的容器账号配置宿主文件的属主。
如果需要将宿主机器上的文件与容器共享,建议将它们放到挂载于 C:\
之外的独立卷中。
默认情况下,所投射的文件会具有如下例所示的属主属性设置:
PS C:\> Get-Acl C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt | Format-List
Path : Microsoft.PowerShell.Core\FileSystem::C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt
Owner : BUILTIN\Administrators
Group : NT AUTHORITY\SYSTEM
Access : NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
BUILTIN\Users Allow ReadAndExecute, Synchronize
Audit :
Sddl : O:BAG:SYD:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;0x1200a9;;;BU)
这意味着,所有类似 ContainerAdministrator
的管理员用户都具有读、写和执行访问权限,
而非管理员用户将具有读和执行访问权限。
总体而言,为容器授予访问宿主系统的权限这种做法是不推荐的,因为这样做可能会打开潜在的安全性攻击之门。
在创建 Windows Pod 时,如果在其 SecurityContext
中设置了 RunAsUser
,
Pod 会一直阻塞在 ContainerCreating
状态。因此,建议不要在 Windows
节点上使用仅针对 Linux 的 RunAsUser
选项。
4 - 临时卷
本文档描述 Kubernetes 中的 临时卷(Ephemeral Volume)。 建议先了解卷,特别是 PersistentVolumeClaim 和 PersistentVolume。
有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。
另有些应用程序需要以文件形式注入的只读数据,比如配置数据或密钥。
临时卷 就是为此类用例设计的。因为卷会遵从 Pod 的生命周期,与 Pod 一起创建和删除, 所以停止和重新启动 Pod 时,不会受持久卷在何处可用的限制。
临时卷在 Pod 规约中以 内联 方式定义,这简化了应用程序的部署和管理。
临时卷的类型
Kubernetes 为了不同的用途,支持几种不同类型的临时卷:
- emptyDir: Pod 启动时为空,存储空间来自本地的 kubelet 根目录(通常是根磁盘)或内存
- configMap、 downwardAPI、 secret: 将不同类型的 Kubernetes 数据注入到 Pod 中
- CSI 临时卷: 类似于前面的卷类型,但由专门支持此特性 的指定 CSI 驱动程序提供
- 通用临时卷: 它可以由所有支持持久卷的存储驱动程序提供
emptyDir
、configMap
、downwardAPI
、secret
是作为
本地临时存储
提供的。它们由各个节点上的 kubelet 管理。
CSI 临时卷 必须 由第三方 CSI 存储驱动程序提供。
通用临时卷 可以 由第三方 CSI 存储驱动程序提供,也可以由支持动态制备的任何其他存储驱动程序提供。 一些专门为 CSI 临时卷编写的 CSI 驱动程序,不支持动态制备:因此这些驱动程序不能用于通用临时卷。
使用第三方驱动程序的优势在于,它们可以提供 Kubernetes 本身不支持的功能, 例如,与 kubelet 管理的磁盘具有不同性能特征的存储,或者用来注入不同的数据。
CSI 临时卷
Kubernetes v1.25 [stable]
从概念上讲,CSI 临时卷类似于 configMap
、downwardAPI
和 secret
类型的卷:
在各个本地节点管理卷的存储,并在 Pod 调度到节点后与其他本地资源一起创建。
在这个阶段,Kubernetes 没有重新调度 Pod 的概念。卷创建不太可能失败,否则 Pod 启动将会受阻。
特别是,这些卷 不 支持感知存储容量的 Pod 调度。
它们目前也没包括在 Pod 的存储资源使用限制中,因为 kubelet 只能对它自己管理的存储强制执行。
下面是使用 CSI 临时存储的 Pod 的示例清单:
kind: Pod
apiVersion: v1
metadata:
name: my-csi-app
spec:
containers:
- name: my-frontend
image: busybox:1.28
volumeMounts:
- mountPath: "/data"
name: my-csi-inline-vol
command: [ "sleep", "1000000" ]
volumes:
- name: my-csi-inline-vol
csi:
driver: inline.storage.kubernetes.io
volumeAttributes:
foo: bar
volumeAttributes
决定驱动程序准备什么样的卷。每个驱动程序的属性不尽相同,没有实现标准化。
有关进一步的说明,请参阅每个 CSI 驱动程序的文档。
CSI 驱动程序限制
CSI 临时卷允许用户直接向 CSI 驱动程序提供 volumeAttributes
,它会作为 Pod 规约的一部分。
有些 volumeAttributes
通常仅限于管理员使用,允许这一类 volumeAttributes
的 CSI 驱动程序不适合在内联临时卷中使用。
例如,通常在 StorageClass 中定义的参数不应通过使用内联临时卷向用户公开。
如果集群管理员需要限制在 Pod 规约中作为内联卷使用的 CSI 驱动程序,可以这样做:
- 从 CSIDriver 规约的
volumeLifecycleModes
中删除Ephemeral
,这可以防止驱动程序被用作内联临时卷。 - 使用准入 Webhook 来限制如何使用此驱动程序。
通用临时卷
Kubernetes v1.23 [stable]
通用临时卷类似于 emptyDir
卷,因为它为每个 Pod 提供临时数据存放目录,
在最初制备完毕时一般为空。不过通用临时卷也有一些额外的功能特性:
- 存储可以是本地的,也可以是网络连接的。
- 卷可以有固定的大小,Pod 不能超量使用。
- 卷可能有一些初始数据,这取决于驱动程序和参数。
示例:
kind: Pod
apiVersion: v1
metadata:
name: my-app
spec:
containers:
- name: my-frontend
image: busybox:1.28
volumeMounts:
- mountPath: "/scratch"
name: scratch-volume
command: [ "sleep", "1000000" ]
volumes:
- name: scratch-volume
ephemeral:
volumeClaimTemplate:
metadata:
labels:
type: my-frontend-volume
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "scratch-storage-class"
resources:
requests:
storage: 1Gi
生命周期和 PersistentVolumeClaim
关键的设计思想是在 Pod 的卷来源中允许使用 卷申领的参数。 PersistentVolumeClaim 的标签、注解和整套字段集均被支持。 创建这样一个 Pod 后, 临时卷控制器在 Pod 所属的命名空间中创建一个实际的 PersistentVolumeClaim 对象, 并确保删除 Pod 时,同步删除 PersistentVolumeClaim。
如上设置将触发卷的绑定与/或制备,相应动作或者在
StorageClass
使用即时卷绑定时立即执行,
或者当 Pod 被暂时性调度到某节点时执行 (WaitForFirstConsumer
卷绑定模式)。
对于通用的临时卷,建议采用后者,这样调度器就可以自由地为 Pod 选择合适的节点。
对于即时绑定,调度器则必须选出一个节点,使得在卷可用时,能立即访问该卷。
就资源所有权而言,
拥有通用临时存储的 Pod 是提供临时存储 (ephemeral storage) 的 PersistentVolumeClaim 的所有者。
当 Pod 被删除时,Kubernetes 垃圾收集器会删除 PVC,
然后 PVC 通常会触发卷的删除,因为存储类的默认回收策略是删除卷。
你可以使用带有 retain
回收策略的 StorageClass 创建准临时 (quasi-ephemeral) 本地存储:
该存储比 Pod 寿命长,在这种情况下,你需要确保单独进行卷清理。
当这些 PVC 存在时,它们可以像其他 PVC 一样使用。 特别是,它们可以被引用作为批量克隆或快照的数据源。 PVC 对象还保持着卷的当前状态。
PersistentVolumeClaim 的命名
自动创建的 PVC 采取确定性的命名机制:名称是 Pod 名称和卷名称的组合,中间由连字符(-
)连接。
在上面的示例中,PVC 将命名为 my-app-scratch-volume
。
这种确定性的命名机制使得与 PVC 交互变得更容易,因为一旦知道 Pod 名称和卷名,就不必搜索它。
这种命名机制也引入了潜在的冲突, 不同的 Pod 之间(名为 “Pod-a” 的 Pod 挂载名为 "scratch" 的卷, 和名为 "pod" 的 Pod 挂载名为 “a-scratch” 的卷,这两者均会生成名为 "pod-a-scratch" 的 PVC),或者在 Pod 和手工创建的 PVC 之间可能出现冲突。
以下冲突会被检测到:如果 PVC 是为 Pod 创建的,那么它只用于临时卷。 此检测基于所有权关系。现有的 PVC 不会被覆盖或修改。 但这并不能解决冲突,因为如果没有正确的 PVC,Pod 就无法启动。
安全
启用 GenericEphemeralVolume 特性会有一些副作用,用户能创建 Pod 就能间接地创建 PVC, 即使他们没有权限直接创建 PVC。 集群管理员必须意识到这一点。 如果这不符合他们的安全模型,他们应该使用准入 Webhook 拒绝包含通用临时卷的对象,例如 Pod。
正常的 PVC 的名字空间配额 仍然有效,因此即使允许用户使用这种新机制,他们也不能使用它来规避其他策略。
接下来
kubelet 管理的临时卷
参阅本地临时存储。
CSI 临时卷
- 有关设计的更多信息,参阅 Ephemeral Inline CSI volumes KEP。
- 本特性下一步开发的更多信息,参阅 enhancement tracking issue #596。
通用临时卷
- 有关设计的更多信息,参阅 Generic ephemeral inline volumes KEP。
5 - 存储类
本文描述了 Kubernetes 中 StorageClass 的概念。 建议先熟悉卷和持久卷的概念。
介绍
StorageClass 为管理员提供了描述存储 "类" 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 "配置文件"。
StorageClass 资源
每个 StorageClass 都包含 provisioner
、parameters
和 reclaimPolicy
字段,
这些字段会在 StorageClass 需要动态制备 PersistentVolume 时会使用到。
StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。
管理员可以为没有申请绑定到特定 StorageClass 的 PVC 指定一个默认的存储类: 更多详情请参阅 PersistentVolumeClaim 章节。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
- debug
volumeBindingMode: Immediate
存储制备器
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。
卷插件 | 内置制备器 | 配置例子 |
---|---|---|
AWSElasticBlockStore | ✓ | AWS EBS |
AzureFile | ✓ | Azure File |
AzureDisk | ✓ | Azure Disk |
CephFS | - | - |
Cinder | ✓ | OpenStack Cinder |
FC | - | - |
FlexVolume | - | - |
GCEPersistentDisk | ✓ | GCE PD |
iSCSI | - | - |
NFS | - | NFS |
RBD | ✓ | Ceph RBD |
VsphereVolume | ✓ | vSphere |
PortworxVolume | ✓ | Portworx Volume |
Local | - | Local |
你不限于指定此处列出的 "内置" 制备器(其名称前缀为 "kubernetes.io" 并打包在 Kubernetes 中)。 你还可以运行和指定外部制备器,这些独立的程序遵循由 Kubernetes 定义的规范。 外部供应商的作者完全可以自由决定他们的代码保存于何处、打包方式、运行方式、使用的插件(包括 Flex)等。 代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 包含一个用于为外部制备器编写功能实现的类库。你可以访问代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 了解外部驱动列表。
例如,NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。
回收策略
由 StorageClass 动态创建的 PersistentVolume 会在类的 reclaimPolicy
字段中指定回收策略,可以是
Delete
或者 Retain
。如果 StorageClass 对象被创建时没有指定 reclaimPolicy
,它将默认为 Delete
。
通过 StorageClass 手动创建并管理的 PersistentVolume 会使用它们被创建时指定的回收策略。
允许卷扩展
Kubernetes v1.11 [beta]
PersistentVolume 可以配置为可扩展。将此功能设置为 true
时,允许用户通过编辑相应的 PVC 对象来调整卷大小。
当下层 StorageClass 的 allowVolumeExpansion
字段设置为 true 时,以下类型的卷支持卷扩展。
卷类型 | Kubernetes 版本要求 |
---|---|
gcePersistentDisk | 1.11 |
awsElasticBlockStore | 1.11 |
Cinder | 1.11 |
rbd | 1.11 |
Azure File | 1.11 |
Azure Disk | 1.11 |
Portworx | 1.11 |
FlexVolume | 1.13 |
CSI | 1.14 (alpha), 1.16 (beta) |
此功能仅可用于扩容卷,不能用于缩小卷。
挂载选项
由 StorageClass 动态创建的 PersistentVolume 将使用类中 mountOptions
字段指定的挂载选项。
如果卷插件不支持挂载选项,却指定了挂载选项,则制备操作会失败。 挂载选项在 StorageClass 和 PV 上都不会做验证,如果其中一个挂载选项无效,那么这个 PV 挂载操作就会失败。
卷绑定模式
volumeBindingMode
字段控制了卷绑定和动态制备应该发生在什么时候。
默认情况下,Immediate
模式表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态制备。
对于由于拓扑限制而非集群所有节点可达的存储后端,PersistentVolume
会在不知道 Pod 调度要求的情况下绑定或者制备。
集群管理员可以通过指定 WaitForFirstConsumer
模式来解决此问题。
该模式将延迟 PersistentVolume 的绑定和制备,直到使用该 PersistentVolumeClaim 的 Pod 被创建。
PersistentVolume 会根据 Pod 调度约束指定的拓扑来选择或制备。
这些包括但不限于资源需求、
节点筛选器、
Pod 亲和性和互斥性、
以及污点和容忍度。
以下插件支持动态制备的 WaitForFirstConsumer
模式:
以下插件支持预创建绑定 PersistentVolume 的 WaitForFirstConsumer
模式:
- 上述全部
- Local
Kubernetes v1.17 [stable]
动态制备和预先创建的 PV 也支持 CSI 卷, 但是你需要查看特定 CSI 驱动的文档以查看其支持的拓扑键名和例子。
如果你选择使用 WaitForFirstConsumer
,请不要在 Pod 规约中使用 nodeName
来指定节点亲和性。
如果在这种情况下使用 nodeName
,Pod 将会绕过调度程序,PVC 将停留在 pending
状态。
相反,在这种情况下,你可以使用节点选择器作为主机名,如下所示。
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
nodeSelector:
kubernetes.io/hostname: kube-01
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
允许的拓扑结构
当集群操作人员使用了 WaitForFirstConsumer
的卷绑定模式,
在大部分情况下就没有必要将制备限制为特定的拓扑结构。
然而,如果还有需要的话,可以使用 allowedTopologies
。
这个例子描述了如何将制备卷的拓扑限制在特定的区域,
在使用时应该根据插件支持情况替换 zone
和 zones
参数。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
- key: failure-domain.beta.kubernetes.io/zone
values:
- us-central-1a
- us-central-1b
参数
Storage Classes 的参数描述了存储类的卷。取决于制备器,可以接受不同的参数。 例如,参数 type 的值 io1 和参数 iopsPerGB 特定于 EBS PV。 当参数被省略时,会使用默认值。
一个 StorageClass 最多可以定义 512 个参数。这些参数对象的总长度不能超过 256 KiB, 包括参数的键和值。
AWS EBS
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
type: io1
iopsPerGB: "10"
fsType: ext4
type
:io1
,gp2
,sc1
,st1
。详细信息参见 AWS 文档。默认值:gp2
。zone
(弃用):AWS 区域。如果没有指定zone
和zones
, 通常卷会在 Kubernetes 集群节点所在的活动区域中轮询调度分配。zone
和zones
参数不能同时使用。zones
(弃用):以逗号分隔的 AWS 区域列表。 如果没有指定zone
和zones
,通常卷会在 Kubernetes 集群节点所在的活动区域中轮询调度分配。zone
和zones
参数不能同时使用。iopsPerGB
:只适用于io1
卷。每 GiB 每秒 I/O 操作。 AWS 卷插件将其与请求卷的大小相乘以计算 IOPS 的容量, 并将其限制在 20000 IOPS(AWS 支持的最高值,请参阅 AWS 文档)。 这里需要输入一个字符串,即"10"
,而不是10
。fsType
:受 Kubernetes 支持的文件类型。默认值:"ext4"
。encrypted
:指定 EBS 卷是否应该被加密。合法值为"true"
或者"false"
。 这里需要输入字符串,即"true"
, 而非true
。kmsKeyId
:可选。加密卷时使用密钥的完整 Amazon 资源名称。 如果没有提供,但encrypted
值为 true,AWS 生成一个密钥。关于有效的 ARN 值,请参阅 AWS 文档。
zone
和 zones
已被弃用并被允许的拓扑结构取代。
GCE PD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
fstype: ext4
replication-type: none
type
:pd-standard
或者pd-ssd
。默认:pd-standard
zone
(弃用):GCE 区域。如果没有指定zone
和zones
, 通常卷会在 Kubernetes 集群节点所在的活动区域中轮询调度分配。zone
和zones
参数不能同时使用。zones
(弃用):逗号分隔的 GCE 区域列表。如果没有指定zone
和zones
, 通常卷会在 Kubernetes 集群节点所在的活动区域中轮询调度(round-robin)分配。zone
和zones
参数不能同时使用。fstype
:ext4
或xfs
。 默认:ext4
。宿主机操作系统必须支持所定义的文件系统类型。replication-type
:none
或者regional-pd
。默认值:none
。
如果 replication-type
设置为 none
,会制备一个常规(当前区域内的)持久化磁盘。
如果 replication-type
设置为 regional-pd
,
会制备一个区域性持久化磁盘(Regional Persistent Disk)。
强烈建议设置 volumeBindingMode: WaitForFirstConsumer
,这样设置后,
当你创建一个 Pod,它使用的 PersistentVolumeClaim 使用了这个 StorageClass,
区域性持久化磁盘会在两个区域里制备。 其中一个区域是 Pod 所在区域。
另一个区域是会在集群管理的区域中任意选择。磁盘区域可以通过 allowedTopologies
加以限制。
zone
和 zones
已被弃用并被 allowedTopologies 取代。
NFS
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: example-nfs
provisioner: example.com/external-nfs
parameters:
server: nfs-server.example.com
path: /share
readOnly: "false"
server
:NFS 服务器的主机名或 IP 地址。path
:NFS 服务器导出的路径。readOnly
:是否将存储挂载为只读的标志(默认为 false)。
Kubernetes 不包含内部 NFS 驱动。你需要使用外部驱动为 NFS 创建 StorageClass。 这里有些例子:
OpenStack Cinder
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gold
provisioner: kubernetes.io/cinder
parameters:
availability: nova
availability
:可用区域。如果没有指定,通常卷会在 Kubernetes 集群节点所在的活动区域中轮转调度。
Kubernetes 1.11 [deprecated]
OpenStack 的内部驱动已经被弃用。请使用 OpenStack 的外部云驱动。
vSphere
vSphere 存储类有两种制备器:
树内制备器已经被 弃用。 更多关于 CSI 制备器的详情,请参阅 Kubernetes vSphere CSI 驱动 和 vSphereVolume CSI 迁移。
CSI 制备器
vSphere CSI StorageClass 制备器在 Tanzu Kubernetes 集群下运行。示例请参阅 vSphere CSI 仓库。
vCP 制备器
以下示例使用 VMware Cloud Provider (vCP) StorageClass 制备器。
-
使用用户指定的磁盘格式创建一个 StorageClass。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/vsphere-volume parameters: diskformat: zeroedthick
diskformat
:thin
、zeroedthick
和eagerzeroedthick
。默认值:"thin"
。
-
在用户指定的数据存储上创建磁盘格式的 StorageClass。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/vsphere-volume parameters: diskformat: zeroedthick datastore: VSANDatastore
datastore
:用户也可以在 StorageClass 中指定数据存储。 卷将在 storage class 中指定的数据存储上创建,在这种情况下是VSANDatastore
。 该字段是可选的。 如果未指定数据存储,则将在用于初始化 vSphere Cloud Provider 的 vSphere 配置文件中指定的数据存储上创建该卷。
-
Kubernetes 中的存储策略管理
-
使用现有的 vCenter SPBM 策略
vSphere 用于存储管理的最重要特性之一是基于策略的管理。 基于存储策略的管理(SPBM)是一个存储策略框架,提供单一的统一控制平面的跨越广泛的数据服务和存储解决方案。 SPBM 使得 vSphere 管理员能够克服先期的存储配置挑战,如容量规划、差异化服务等级和管理容量空间。
SPBM 策略可以在 StorageClass 中使用
storagePolicyName
参数声明。
-
Kubernetes 内的 Virtual SAN 策略支持
Vsphere Infrastructure(VI)管理员将能够在动态卷配置期间指定自定义 Virtual SAN 存储功能。你现在可以在动态制备卷期间以存储能力的形式定义存储需求,例如性能和可用性。 存储能力需求会转换为 Virtual SAN 策略,之后当持久卷(虚拟磁盘)被创建时, 会将其推送到 Virtual SAN 层。虚拟磁盘分布在 Virtual SAN 数据存储中以满足要求。
你可以参考基于存储策略的动态制备卷管理, 进一步了解有关持久卷管理的存储策略的详细信息。
-
有几个 vSphere 例子供你在 Kubernetes for vSphere 中尝试进行持久卷管理。
Ceph RBD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/rbd
parameters:
monitors: 10.16.153.105:6789
adminId: kube
adminSecretName: ceph-secret
adminSecretNamespace: kube-system
pool: kube
userId: kube
userSecretName: ceph-secret-user
userSecretNamespace: default
fsType: ext4
imageFormat: "2"
imageFeatures: "layering"
monitors
:Ceph monitor,逗号分隔。该参数是必需的。adminId
:Ceph 客户端 ID,用于在池 ceph 池中创建映像。默认是 "admin"。adminSecret
:adminId
的 Secret 名称。该参数是必需的。 提供的 secret 必须有值为 "kubernetes.io/rbd" 的 type 参数。adminSecretNamespace
:adminSecret
的命名空间。默认是 "default"。pool
:Ceph RBD 池。默认是 "rbd"。userId
:Ceph 客户端 ID,用于映射 RBD 镜像。默认与adminId
相同。
-
userSecretName
:用于映射 RBD 镜像的userId
的 Ceph Secret 的名字。 它必须与 PVC 存在于相同的 namespace 中。该参数是必需的。 提供的 secret 必须具有值为 "kubernetes.io/rbd" 的 type 参数,例如以这样的方式创建:kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" \ --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' \ --namespace=kube-system
userSecretNamespace
:userSecretName
的命名空间。fsType
:Kubernetes 支持的 fsType。默认:"ext4"
。imageFormat
:Ceph RBD 镜像格式,"1" 或者 "2"。默认值是 "1"。imageFeatures
:这个参数是可选的,只能在你将imageFormat
设置为 "2" 才使用。 目前支持的功能只是layering
。默认是 "",没有功能打开。
Azure 磁盘
Azure Unmanaged Disk Storage Class(非托管磁盘存储类)
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: slow
provisioner: kubernetes.io/azure-disk
parameters:
skuName: Standard_LRS
location: eastus
storageAccount: azure_storage_account_name
skuName
:Azure 存储帐户 Sku 层。默认为空。location
:Azure 存储帐户位置。默认为空。storageAccount
:Azure 存储帐户名称。 如果提供存储帐户,它必须位于与集群相同的资源组中,并且location
是被忽略的。如果未提供存储帐户,则会在与集群相同的资源组中创建新的存储帐户。
Azure 磁盘 Storage Class(从 v1.7.2 开始)
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: slow
provisioner: kubernetes.io/azure-disk
parameters:
storageaccounttype: Standard_LRS
kind: managed
storageaccounttype
:Azure 存储帐户 Sku 层。默认为空。kind
:可能的值是shared
、dedicated
和managed
(默认)。 当kind
的值是shared
时,所有非托管磁盘都在集群的同一个资源组中的几个共享存储帐户中创建。 当kind
的值是dedicated
时,将为在集群的同一个资源组中新的非托管磁盘创建新的专用存储帐户。resourceGroup
:指定要创建 Azure 磁盘所属的资源组。必须是已存在的资源组名称。 若未指定资源组,磁盘会默认放入与当前 Kubernetes 集群相同的资源组中。
- Premium VM 可以同时添加 Standard_LRS 和 Premium_LRS 磁盘,而 Standard 虚拟机只能添加 Standard_LRS 磁盘。
- 托管虚拟机只能连接托管磁盘,非托管虚拟机只能连接非托管磁盘。
Azure 文件
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: azurefile
provisioner: kubernetes.io/azure-file
parameters:
skuName: Standard_LRS
location: eastus
storageAccount: azure_storage_account_name
skuName
:Azure 存储帐户 Sku 层。默认为空。location
:Azure 存储帐户位置。默认为空。storageAccount
:Azure 存储帐户名称。默认为空。 如果不提供存储帐户,会搜索所有与资源相关的存储帐户,以找到一个匹配skuName
和location
的账号。 如果提供存储帐户,它必须存在于与集群相同的资源组中,skuName
和location
会被忽略。secretNamespace
:包含 Azure 存储帐户名称和密钥的密钥的名字空间。 默认值与 Pod 相同。secretName
:包含 Azure 存储帐户名称和密钥的密钥的名称。 默认值为azure-storage-account-<accountName>-secret
readOnly
:指示是否将存储安装为只读的标志。默认为 false,表示"读/写"挂载。 该设置也会影响VolumeMounts中的ReadOnly
设置。
在存储制备期间,为挂载凭证创建一个名为 secretName
的 Secret。如果集群同时启用了
RBAC
和控制器角色,
为 system:controller:persistent-volume-binder
的 clusterrole 添加
Secret
资源的 create
权限。
在多租户上下文中,强烈建议显式设置 secretNamespace
的值,否则其他用户可能会读取存储帐户凭据。
Portworx 卷
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: portworx-io-priority-high
provisioner: kubernetes.io/portworx-volume
parameters:
repl: "1"
snap_interval: "70"
priority_io: "high"
fs
:选择的文件系统:none/xfs/ext4
(默认:ext4
)。block_size
:以 Kbytes 为单位的块大小(默认值:32
)。repl
:同步副本数量,以复制因子1..3
(默认值:1
)的形式提供。 这里需要填写字符串,即,"1"
而不是1
。io_priority
:决定是否从更高性能或者较低优先级存储创建卷high/medium/low
(默认值:low
)。snap_interval
:触发快照的时钟/时间间隔(分钟)。 快照是基于与先前快照的增量变化,0 是禁用快照(默认:0
)。 这里需要填写字符串,即,是"70"
而不是70
。aggregation_level
:指定卷分配到的块数量,0 表示一个非聚合卷(默认:0
)。 这里需要填写字符串,即,是"0"
而不是0
。ephemeral
:指定卷在卸载后进行清理还是持久化。emptyDir
的使用场景可以将这个值设置为 true,persistent volumes
的使用场景可以将这个值设置为 false (例如 Cassandra 这样的数据库)true/false
(默认为false
)。这里需要填写字符串,即, 是"true"
而不是true
。
本地
Kubernetes v1.14 [stable]
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
本地卷还不支持动态制备,然而还是需要创建 StorageClass 以延迟卷绑定,
直到完成 Pod 的调度。这是由 WaitForFirstConsumer
卷绑定模式指定的。
延迟卷绑定使得调度器在为 PersistentVolumeClaim 选择一个合适的 PersistentVolume 时能考虑到所有 Pod 的调度限制。
6 - 动态卷制备
动态卷制备允许按需创建存储卷。
如果没有动态制备,集群管理员必须手动地联系他们的云或存储提供商来创建新的存储卷,
然后在 Kubernetes 集群创建
PersistentVolume
对象来表示这些卷。
动态制备功能消除了集群管理员预先配置存储的需要。相反,它在用户请求时自动制备存储。
背景
动态卷制备的实现基于 storage.k8s.io
API 组中的 StorageClass
API 对象。
集群管理员可以根据需要定义多个 StorageClass
对象,每个对象指定一个卷插件(又名 provisioner),
卷插件向卷制备商提供在创建卷时需要的数据卷信息及相关参数。
集群管理员可以在集群中定义和公开多种存储(来自相同或不同的存储系统),每种都具有自定义参数集。 该设计也确保终端用户不必担心存储制备的复杂性和细微差别,但仍然能够从多个存储选项中进行选择。
点击这里查阅有关存储类的更多信息。
启用动态卷制备
要启用动态制备功能,集群管理员需要为用户预先创建一个或多个 StorageClass
对象。
StorageClass
对象定义当动态制备被调用时,哪一个驱动将被使用和哪些参数将被传递给驱动。
StorageClass 对象的名字必须是一个合法的
DNS 子域名。
以下清单创建了一个 StorageClass
存储类 "slow",它提供类似标准磁盘的永久磁盘。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
以下清单创建了一个 "fast" 存储类,它提供类似 SSD 的永久磁盘。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
使用动态卷制备
用户通过在 PersistentVolumeClaim
中包含存储类来请求动态制备的存储。
在 Kubernetes v1.9 之前,这通过 volume.beta.kubernetes.io/storage-class
注解实现。
然而,这个注解自 v1.6 起就不被推荐使用了。
用户现在能够而且应该使用 PersistentVolumeClaim
对象的 storageClassName
字段。
这个字段的值必须能够匹配到集群管理员配置的 StorageClass
名称(见下面)。
例如,要选择 “fast” 存储类,用户将创建如下的 PersistentVolumeClaim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: claim1
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast
resources:
requests:
storage: 30Gi
该声明会自动制备一块类似 SSD 的永久磁盘。 在删除该声明后,这个卷也会被销毁。
设置默认值的行为
可以在集群上启用动态卷制备,以便在未指定存储类的情况下动态设置所有声明。 集群管理员可以通过以下方式启用此行为:
- 标记一个
StorageClass
为 默认; - 确保
DefaultStorageClass
准入控制器在 API 服务器端被启用。
管理员可以通过向其添加
storageclass.kubernetes.io/is-default-class
注解
来将特定的 StorageClass
标记为默认。
当集群中存在默认的 StorageClass
并且用户创建了一个未指定 storageClassName
的 PersistentVolumeClaim
时,
DefaultStorageClass
准入控制器会自动向其中添加指向默认存储类的 storageClassName
字段。
请注意,集群上最多只能有一个 默认 存储类,否则无法创建没有明确指定
storageClassName
的 PersistentVolumeClaim
。
拓扑感知
在多可用区集群中,Pod 可以被分散到某个区域的多个可用区。 单可用区存储后端应该被制备到 Pod 被调度到的可用区。 这可以通过设置卷绑定模式来实现。
7 - 卷快照
在 Kubernetes 中,卷快照 是一个存储系统上卷的快照,本文假设你已经熟悉了 Kubernetes 的持久卷。
介绍
与 PersistentVolume
和 PersistentVolumeClaim
这两个 API 资源用于给用户和管理员制备卷类似,
VolumeSnapshotContent
和 VolumeSnapshot
这两个 API 资源用于给用户和管理员创建卷快照。
VolumeSnapshotContent
是从一个卷获取的一种快照,该卷由管理员在集群中进行制备。
就像持久卷(PersistentVolume)是集群的资源一样,它也是集群中的资源。
VolumeSnapshot
是用户对于卷的快照的请求。它类似于持久卷声明(PersistentVolumeClaim)。
VolumeSnapshotClass
允许指定属于 VolumeSnapshot
的不同属性。在从存储系统的相同卷上获取的快照之间,
这些属性可能有所不同,因此不能通过使用与 PersistentVolumeClaim
相同的 StorageClass
来表示。
卷快照能力为 Kubernetes 用户提供了一种标准的方式来在指定时间点复制卷的内容,并且不需要创建全新的卷。 例如,这一功能使得数据库管理员能够在执行编辑或删除之类的修改之前对数据库执行备份。
当使用该功能时,用户需要注意以下几点:
- API 对象
VolumeSnapshot
,VolumeSnapshotContent
和VolumeSnapshotClass
是 CRD, 不属于核心 API。 VolumeSnapshot
支持仅可用于 CSI 驱动。- 作为
VolumeSnapshot
部署过程的一部分,Kubernetes 团队提供了一个部署于控制平面的快照控制器, 并且提供了一个叫做csi-snapshotter
的边车(Sidecar)辅助容器,和 CSI 驱动程序一起部署。 快照控制器监视VolumeSnapshot
和VolumeSnapshotContent
对象, 并且负责创建和删除VolumeSnapshotContent
对象。 边车 csi-snapshotter 监视VolumeSnapshotContent
对象, 并且触发针对 CSI 端点的CreateSnapshot
和DeleteSnapshot
的操作。 - 还有一个验证性质的 Webhook 服务器,可以对快照对象进行更严格的验证。 Kubernetes 发行版应将其与快照控制器和 CRD(而非 CSI 驱动程序)一起安装。 此服务器应该安装在所有启用了快照功能的 Kubernetes 集群中。
- CSI 驱动可能实现,也可能没有实现卷快照功能。CSI 驱动可能会使用 csi-snapshotter 来提供对卷快照的支持。详见 CSI 驱动程序文档
- Kubernetes 负责 CRD 和快照控制器的安装。
卷快照和卷快照内容的生命周期
VolumeSnapshotContents
是集群中的资源。VolumeSnapshots
是对于这些资源的请求。
VolumeSnapshotContents
和 VolumeSnapshots
之间的交互遵循以下生命周期:
制备卷快照
快照可以通过两种方式进行制备:预制备或动态制备。
预制备
集群管理员创建多个 VolumeSnapshotContents
。它们带有存储系统上实际卷快照的详细信息,可以供集群用户使用。
它们存在于 Kubernetes API 中,并且能够被使用。
动态制备
可以从 PersistentVolumeClaim
中动态获取快照,而不用使用已经存在的快照。
在获取快照时,卷快照类
指定要用的特定于存储提供程序的参数。
绑定
在预制备和动态制备场景下,快照控制器处理绑定 VolumeSnapshot
对象和其合适的 VolumeSnapshotContent
对象。
绑定关系是一对一的。
在预制备快照绑定场景下,VolumeSnapshotContent
对象创建之后,才会和 VolumeSnapshot
进行绑定。
快照源的持久性卷声明保护
这种保护的目的是确保在从系统中获取快照时,不会将正在使用的 PersistentVolumeClaim API 对象从系统中删除(因为这可能会导致数据丢失)。
在为某 PersistentVolumeClaim
生成快照时,该 PersistentVolumeClaim
处于被使用状态。
如果删除一个正作为快照源使用的 PersistentVolumeClaim
API 对象,该 PersistentVolumeClaim
对象不会立即被移除。
相反,移除 PersistentVolumeClaim
对象的动作会被推迟,直到快照状态变为 ReadyToUse 或快照操作被中止时再执行。
删除
删除 VolumeSnapshot
对象触发删除 VolumeSnapshotContent
操作,并且 DeletionPolicy
会紧跟着执行。
如果 DeletionPolicy
是 Delete
,那么底层存储快照会和 VolumeSnapshotContent
一起被删除。
如果 DeletionPolicy
是 Retain
,那么底层快照和 VolumeSnapshotContent
都会被保留。
卷快照
每个 VolumeSnapshot
包含一个 spec 和一个 status。
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: new-snapshot-test
spec:
volumeSnapshotClassName: csi-hostpath-snapclass
source:
persistentVolumeClaimName: pvc-test
persistentVolumeClaimName
是 PersistentVolumeClaim
数据源对快照的名称。
这个字段是动态制备快照中的必填字段。
卷快照可以通过指定 VolumeSnapshotClass
使用 volumeSnapshotClassName
属性来请求特定类。如果没有设置,那么使用默认类(如果有)。
如下面例子所示,对于预制备的快照,需要给快照指定 volumeSnapshotContentName
作为来源。
对于预制备的快照 source
中的volumeSnapshotContentName
字段是必填的。
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: test-snapshot
spec:
source:
volumeSnapshotContentName: test-content
卷快照内容
每个 VolumeSnapshotContent 对象包含 spec 和 status。
在动态制备时,快照通用控制器创建 VolumeSnapshotContent
对象。下面是例子:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
name: snapcontent-72d9a349-aacd-42d2-a240-d775650d2455
spec:
deletionPolicy: Delete
driver: hostpath.csi.k8s.io
source:
volumeHandle: ee0cfb94-f8d4-11e9-b2d8-0242ac110002
sourceVolumeMode: Filesystem
volumeSnapshotClassName: csi-hostpath-snapclass
volumeSnapshotRef:
name: new-snapshot-test
namespace: default
uid: 72d9a349-aacd-42d2-a240-d775650d2455
volumeHandle
是存储后端创建卷的唯一标识符,在卷创建期间由 CSI 驱动程序返回。
动态设置快照需要此字段。它指出了快照的卷源。
对于预制备快照,你(作为集群管理员)要按如下命令来创建 VolumeSnapshotContent
对象。
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
name: new-snapshot-content-test
spec:
deletionPolicy: Delete
driver: hostpath.csi.k8s.io
source:
snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
sourceVolumeMode: Filesystem
volumeSnapshotRef:
name: new-snapshot-test
namespace: default
snapshotHandle
是存储后端创建卷的唯一标识符。对于预制备的快照,这个字段是必需的。
它指定此 VolumeSnapshotContent
表示的存储系统上的 CSI 快照 ID。
sourceVolumeMode
是创建快照的卷的模式。sourceVolumeMode
字段的值可以是
Filesystem
或 Block
。如果没有指定源卷模式,Kubernetes 会将快照视为未知的源卷模式。
volumeSnapshotRef
字段是对相应的 VolumeSnapshot
的引用。
请注意,当 VolumeSnapshotContent
被创建为预配置快照时。
volumeSnapshotRef
中引用的 VolumeSnapshot
可能还不存在。
转换快照的卷模式
如果在你的集群上安装的 VolumeSnapshots
API 支持 sourceVolumeMode
字段,则该 API 可以防止未经授权的用户转换卷的模式。
要检查你的集群是否具有此特性的能力,可以运行如下命令:
kubectl get crd volumesnapshotcontent -o yaml
如果你希望允许用户从现有的 VolumeSnapshot
创建 PersistentVolumeClaim
,
但是使用与源卷不同的卷模式,则需要添加注解
snapshot.storage.kubernetes.io/allowVolumeModeChange: "true"
到对应 VolumeSnapshot
的 VolumeSnapshotContent
中。
对于预制备的快照,spec.SourceVolumeMode
需要由集群管理员填充。
启用此特性的 VolumeSnapshotContent
资源示例如下所示:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
name: new-snapshot-content-test
annotations:
- snapshot.storage.kubernetes.io/allowVolumeModeChange: "true"
spec:
deletionPolicy: Delete
driver: hostpath.csi.k8s.io
source:
snapshotHandle: 7bdd0de3-aaeb-11e8-9aae-0242ac110002
sourceVolumeMode: Filesystem
volumeSnapshotRef:
name: new-snapshot-test
namespace: default
从快照制备卷
你可以制备一个新卷,该卷预填充了快照中的数据,在 PersistentVolumeClaim
对象中使用 dataSource 字段。
更多详细信息, 请参阅卷快照和从快照还原卷。
8 - 卷快照类
本文档描述了 Kubernetes 中 VolumeSnapshotClass 的概念。建议熟悉 卷快照(Volume Snapshots)和 存储类(Storage Class)。
介绍
就像 StorageClass 为管理员提供了一种在配置卷时描述存储“类”的方法, VolumeSnapshotClass 提供了一种在配置卷快照时描述存储“类”的方法。
VolumeSnapshotClass 资源
每个 VolumeSnapshotClass 都包含 driver
、deletionPolicy
和 parameters
字段,
在需要动态配置属于该类的 VolumeSnapshot 时使用。
VolumeSnapshotClass 对象的名称很重要,是用户可以请求特定类的方式。 管理员在首次创建 VolumeSnapshotClass 对象时设置类的名称和其他参数, 对象一旦创建就无法更新。
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:
管理员可以为未请求任何特定类绑定的 VolumeSnapshots 指定默认的 VolumeSnapshotClass,
方法是设置注解 snapshot.storage.kubernetes.io/is-default-class: "true"
:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-hostpath-snapclass
annotations:
snapshot.storage.kubernetes.io/is-default-class: "true"
driver: hostpath.csi.k8s.io
deletionPolicy: Delete
parameters:
驱动程序
卷快照类有一个驱动程序,用于确定配置 VolumeSnapshot 的 CSI 卷插件。 此字段必须指定。
删除策略
卷快照类具有 deletionPolicy
属性。用户可以配置当所绑定的 VolumeSnapshot
对象将被删除时,如何处理 VolumeSnapshotContent 对象。
卷快照类的这个策略可以是 Retain
或者 Delete
。这个策略字段必须指定。
如果删除策略是 Delete
,那么底层的存储快照会和 VolumeSnapshotContent 对象
一起删除。如果删除策略是 Retain
,那么底层快照和 VolumeSnapshotContent
对象都会被保留。
参数
卷快照类具有描述属于该卷快照类的卷快照的参数,可根据 driver
接受不同的参数。
9 - CSI 卷克隆
本文档介绍 Kubernetes 中克隆现有 CSI 卷的概念。阅读前建议先熟悉 卷。
介绍
CSI 卷克隆功能增加了通过在
dataSource
字段中指定存在的
PVC,
来表示用户想要克隆的 卷(Volume)。
克隆(Clone),意思是为已有的 Kubernetes 卷创建副本,它可以像任何其它标准卷一样被使用。 唯一的区别就是配置后,后端设备将创建指定完全相同的副本,而不是创建一个“新的”空卷。
从 Kubernetes API 的角度看,克隆的实现只是在创建新的 PVC 时, 增加了指定一个现有 PVC 作为数据源的能力。源 PVC 必须是 bound 状态且可用的(不在使用中)。
用户在使用该功能时,需要注意以下事项:
- 克隆支持(
VolumePVCDataSource
)仅适用于 CSI 驱动。 - 克隆支持仅适用于 动态供应器。
- CSI 驱动可能实现,也可能未实现卷克隆功能。
- 仅当 PVC 与目标 PVC 存在于同一命名空间(源和目标 PVC 必须在相同的命名空间)时,才可以克隆 PVC。
- 支持用一个不同存储类进行克隆。
- 目标卷和源卷可以是相同的存储类,也可以不同。
- 可以使用默认的存储类,也可以在 spec 中省略 storageClassName 字段。
- 克隆只能在两个使用相同 VolumeMode 设置的卷中进行 (如果请求克隆一个块存储模式的卷,源卷必须也是块存储模式)。
制备
克隆卷与其他任何 PVC 一样配置,除了需要增加 dataSource 来引用同一命名空间中现有的 PVC。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: clone-of-pvc-1
namespace: myns
spec:
accessModes:
- ReadWriteOnce
storageClassName: cloning
resources:
requests:
storage: 5Gi
dataSource:
kind: PersistentVolumeClaim
name: pvc-1
你必须为 spec.resources.requests.storage
指定一个值,并且你指定的值必须大于或等于源卷的值。
结果是一个名称为 clone-of-pvc-1
的新 PVC 与指定的源 pvc-1
拥有相同的内容。
使用
一旦新的 PVC 可用,被克隆的 PVC 像其他 PVC 一样被使用。 可以预期的是,新创建的 PVC 是一个独立的对象。 可以独立使用、克隆、快照或删除它,而不需要考虑它的原始数据源 PVC。 这也意味着,源没有以任何方式链接到新创建的 PVC,它也可以被修改或删除,而不会影响到新创建的克隆。
10 - 存储容量
存储容量是有限的,并且会因为运行 Pod 的节点不同而变化: 网络存储可能并非所有节点都能够访问,或者对于某个节点存储是本地的。
Kubernetes v1.24 [stable]
本页面描述了 Kubernetes 如何跟踪存储容量以及调度程序如何为了余下的尚未挂载的卷使用该信息将 Pod 调度到能够访问到足够存储容量的节点上。 如果没有跟踪存储容量,调度程序可能会选择一个没有足够容量来提供卷的节点,并且需要多次调度重试。
准备开始
Kubernetes v1.26 包含了对存储容量跟踪的集群级 API 支持。 要使用它,你还必须使用支持容量跟踪的 CSI 驱动程序。请查阅你使用的 CSI 驱动程序的文档, 以了解此支持是否可用,如果可用,该如何使用它。如果你运行的不是 Kubernetes v1.26,请查看对应版本的 Kubernetes 文档。
API
这个特性有两个 API 扩展接口:
- CSIStorageCapacity 对象:这些对象由 CSI 驱动程序在安装驱动程序的命名空间中产生。 每个对象都包含一个存储类的容量信息,并定义哪些节点可以访问该存储。
CSIDriverSpec.StorageCapacity
字段: 设置为 true 时,Kubernetes 调度程序将考虑使用 CSI 驱动程序的卷的存储容量。
调度
如果有以下情况,存储容量信息将会被 Kubernetes 调度程序使用:
- Pod 使用的卷还没有被创建,
- 卷使用引用了 CSI 驱动的 StorageClass,
并且使用了
WaitForFirstConsumer
卷绑定模式, - 驱动程序的
CSIDriver
对象的StorageCapacity
被设置为 true。
在这种情况下,调度程序仅考虑将 Pod 调度到有足够存储容量的节点上。这个检测非常简单,
仅将卷的大小与 CSIStorageCapacity
对象中列出的容量进行比较,并使用包含该节点的拓扑。
对于具有 Immediate
卷绑定模式的卷,存储驱动程序将决定在何处创建该卷,而不取决于将使用该卷的 Pod。
然后,调度程序将 Pod 调度到创建卷后可使用该卷的节点上。
对于 CSI 临时卷, 调度总是在不考虑存储容量的情况下进行。 这是基于这样的假设:该卷类型仅由节点本地的特殊 CSI 驱动程序使用,并且不需要大量资源。
重新调度
当为带有 WaitForFirstConsumer
的卷的 Pod 来选择节点时,该决定仍然是暂定的。
下一步是要求 CSI 存储驱动程序创建卷,并提示该卷在被选择的节点上可用。
因为 Kubernetes 可能会根据已经过时的存储容量信息来选择一个节点,因此可能无法真正创建卷。 然后就会重置节点选择,Kubernetes 调度器会再次尝试为 Pod 查找节点。
限制
存储容量跟踪增加了调度器第一次尝试即成功的机会,但是并不能保证这一点,因为调度器必须根据可能过期的信息来进行决策。 通常,与没有任何存储容量信息的调度相同的重试机制可以处理调度失败。
当 Pod 使用多个卷时,调度可能会永久失败:一个卷可能已经在拓扑段中创建,而该卷又没有足够的容量来创建另一个卷, 要想从中恢复,必须要进行手动干预,比如通过增加存储容量或者删除已经创建的卷。
接下来
- 想要获得更多该设计的信息,查看 Storage Capacity Constraints for Pod Scheduling KEP。
11 - 特定于节点的卷数限制
此页面描述了各个云供应商可关联至一个节点的最大卷数。
谷歌、亚马逊和微软等云供应商通常对可以关联到节点的卷数量进行限制。 Kubernetes 需要尊重这些限制。 否则,在节点上调度的 Pod 可能会卡住去等待卷的关联。
Kubernetes 的默认限制
The Kubernetes 调度器对关联于一个节点的卷数有默认限制:
云服务 | 每节点最大卷数 |
---|---|
Amazon Elastic Block Store (EBS) | 39 |
Google Persistent Disk | 16 |
Microsoft Azure Disk Storage | 16 |
自定义限制
你可以通过设置 KUBE_MAX_PD_VOLS
环境变量的值来设置这些限制,然后再启动调度器。
CSI 驱动程序可能具有不同的过程,关于如何自定义其限制请参阅相关文档。
如果设置的限制高于默认限制,请谨慎使用。请参阅云提供商的文档以确保节点可支持你设置的限制。
此限制应用于整个集群,所以它会影响所有节点。
动态卷限制
Kubernetes v1.17 [stable]
以下卷类型支持动态卷限制。
- Amazon EBS
- Google Persistent Disk
- Azure Disk
- CSI
对于由内建插件管理的卷,Kubernetes 会自动确定节点类型并确保节点上可关联的卷数目合规。 例如:
-
在 Google Compute Engine环境中, 根据节点类型最多可以将127个卷关联到节点。
-
对于 M5、C5、R5、T3 和 Z1D 类型实例的 Amazon EBS 磁盘,Kubernetes 仅允许 25 个卷关联到节点。 对于 ec2 上的其他实例类型 Amazon Elastic Compute Cloud (EC2), Kubernetes 允许 39 个卷关联至节点。
-
在 Azure 环境中, 根据节点类型,最多 64 个磁盘可以关联至一个节点。 更多详细信息,请参阅Azure 虚拟机的数量大小。
-
如果 CSI 存储驱动程序(使用
NodeGetInfo
)为节点通告卷数上限,则 kube-scheduler 将遵守该限制值。 参考 CSI 规范 获取更多详细信息。 -
对于由已迁移到 CSI 驱动程序的树内插件管理的卷,最大卷数将是 CSI 驱动程序报告的卷数。
12 - 卷健康监测
Kubernetes v1.21 [alpha]
CSI 卷健康监测支持 CSI 驱动从底层的存储系统着手, 探测异常的卷状态,并以事件的形式上报到 PVCs 或 Pods.
卷健康监测
Kubernetes 卷健康监测 是 Kubernetes 容器存储接口(CSI)实现的一部分。 卷健康监测特性由两个组件实现:外部健康监测控制器和 kubelet。
如果 CSI 驱动器通过控制器的方式支持卷健康监测特性,那么只要在 CSI 卷上监测到异常卷状态,就会在 PersistentVolumeClaim (PVC) 中上报一个事件。
外部健康监测控制器也会监测节点失效事件。
如果要启动节点失效监测功能,你可以设置标志 enable-node-watcher
为 true
。
当外部健康监测器检测到节点失效事件,控制器会报送一个事件,该事件会在 PVC 上继续上报,
以表明使用此 PVC 的 Pod 正位于一个失效的节点上。
如果 CSI 驱动程序支持节点测的卷健康检测,那当在 CSI 卷上检测到异常卷时,
会在使用该 PVC 的每个Pod 上触发一个事件。
此外,卷运行状况信息作为 Kubelet VolumeStats 指标公开。
添加了一个新的指标 kubelet_volume_stats_health_status_abnormal。
该指标包括两个标签:namespace
和 persistentvolumeclaim
。
计数为 1 或 0。1 表示卷不正常,0 表示卷正常。更多信息请访问KEP。
CSIVolumeHealth
特性门控,
才能在节点上使用此特性。
接下来
参阅 CSI 驱动程序文档, 可以找出有哪些 CSI 驱动程序实现了此特性。
13 - Windows 存储
此页面提供特定于 Windows 操作系统的存储概述。
持久存储
Windows 有一个分层文件系统驱动程序用来挂载容器层和创建基于 NTFS 的文件系统拷贝。 容器中的所有文件路径仅在该容器的上下文中解析。
- 使用 Docker 时,卷挂载只能是容器中的目录,而不能是单个文件。此限制不适用于 containerd。
- 卷挂载不能将文件或目录映射回宿主文件系统。
- 不支持只读文件系统,因为 Windows 注册表和 SAM 数据库始终需要写访问权限。不过,Windows 支持只读的卷。
- 不支持卷的用户掩码和访问许可,因为宿主与容器之间并不共享 SAM,二者之间不存在映射关系。 所有访问许可都是在容器上下文中解析的。
因此,Windows 节点不支持以下存储功能:
- 卷子路径挂载:只能在 Windows 容器上挂载整个卷
- Secret 的子路径挂载
- 宿主挂载映射
- 只读的根文件系统(映射的卷仍然支持
readOnly
) - 块设备映射
- 内存作为存储介质(例如
emptyDir.medium
设置为Memory
) - 类似 UID/GID、各用户不同的 Linux 文件系统访问许可等文件系统特性
- 使用 DefaultMode 设置 Secret 权限 (因为该特性依赖 UID/GID)
- 基于 NFS 的存储和卷支持
- 扩展已挂载卷(resizefs)
使用 Kubernetes 卷, 对数据持久性和 Pod 卷共享有需求的复杂应用也可以部署到 Kubernetes 上。 管理与特定存储后端或协议相关的持久卷时,相关的操作包括:对卷的制备(Provisioning)、 去配(De-provisioning)和调整大小,将卷挂接到 Kubernetes 节点或从节点上解除挂接, 将卷挂载到需要持久数据的 Pod 中的某容器上或从容器上卸载。
卷管理组件作为 Kubernetes 卷插件发布。 Windows 支持以下类型的 Kubernetes 卷插件:
FlexVolume plugins
- 请注意自 1.23 版本起,FlexVolume 已被弃用
CSI Plugins
树内(In-Tree)卷插件
以下树内(In-Tree)插件支持 Windows 节点上的持久存储: