本文已超过一年。较早的文章可能包含过时内容。请检查页面信息自发布以来是否已变得不正确。
引入 PersistentVolumes 的单个 Pod 访问模式
上个月发布的 Kubernetes v1.22 引入了一个新的 ReadWriteOncePod 访问模式,用于 PersistentVolumes 和 PersistentVolumeClaims。借助此 Alpha 功能,Kubernetes 允许你将卷访问限制为集群中的单个 Pod。
什么是访问模式?为何它们很重要?
使用存储时,可以有不同的方式来建模该存储的使用方式。
例如,像网络文件共享这样的存储系统可以有许多用户同时读写数据。在其他情况下,也许所有人都被允许读取数据但不能写入。对于高度敏感的数据,也许只有一位用户被允许读写数据,而其他人都不可以。
在 Kubernetes 的世界里,访问模式是你定义持久存储如何被使用的方式。这些访问模式是 PersistentVolumes (PV) 和 PersistentVolumeClaims (PVC) 规约(spec)的一部分。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: shared-cache
spec:
accessModes:
- ReadWriteMany # Allow many nodes to access shared-cache simultaneously.
resources:
requests:
storage: 1Gi
在 v1.22 之前,Kubernetes 为 PV 和 PVC 提供了三种访问模式
- ReadWriteOnce – 卷可以被单个节点以读写模式挂载
- ReadOnlyMany – 卷可以被多个节点以只读模式挂载
- ReadWriteMany – 卷可以被多个节点以读写模式挂载
这些访问模式由 Kubernetes 组件(如 kube-controller-manager
和 kubelet
)强制执行,以确保只有特定的 Pod 被允许访问给定的 PersistentVolume。
这个新的访问模式是什么?它是如何工作的?
Kubernetes v1.22 为 PV 和 PVC 引入了第四种访问模式,你可以将其用于 CSI 卷
- ReadWriteOncePod – 卷可以被单个 Pod 以读写模式挂载
如果你使用采用 ReadWriteOncePod 访问模式的 PVC 创建一个 Pod,Kubernetes 将确保该 Pod 是整个集群中唯一可以读写该 PVC 的 Pod。
如果你创建另一个 Pod,并用此访问模式引用相同的 PVC,该 Pod 将无法启动,因为该 PVC 已被另一个 Pod 使用。例如
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 1s default-scheduler 0/1 nodes are available: 1 node has pod using PersistentVolumeClaim with the same name and ReadWriteOncePod access mode.
这与 ReadWriteOnce 访问模式有何不同?
ReadWriteOnce 访问模式将卷访问限制为单个 节点,这意味着同一节点上的多个 Pod 可以读写同一个卷。对于某些应用而言,这可能是一个大问题,尤其是当它们需要至多一个写入者来保证数据安全时。
使用 ReadWriteOncePod,这些问题就迎刃而解了。在你的 PVC 上设置该访问模式,Kubernetes 将保证只有一个 Pod 可以访问。
如何使用它?
ReadWriteOncePod 访问模式在 Kubernetes v1.22 中处于 Alpha 阶段,并且仅支持 CSI 卷。首先,你需要为 kube-apiserver
、kube-scheduler
和 kubelet
启用 ReadWriteOncePod 特性门控(feature gate)。你可以通过设置命令行参数来启用此特性
--feature-gates="...,ReadWriteOncePod=true"
你还需要将以下 CSI 边车(sidecar)更新到这些版本或更高版本
创建 PersistentVolumeClaim
为了对 PV 和 PVC 使用 ReadWriteOncePod 访问模式,你需要创建一个具有该访问模式的新 PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: single-writer-only
spec:
accessModes:
- ReadWriteOncePod # Allow only a single pod to access single-writer-only.
resources:
requests:
storage: 1Gi
如果你的存储插件支持动态供应,则将创建应用了 ReadWriteOncePod 访问模式的新 PersistentVolumes。
迁移现有的 PersistentVolumes
如果你有现有的 PersistentVolumes,可以将它们迁移以使用 ReadWriteOncePod。
在此示例中,我们已经有一个名为 "cat-pictures-pvc" 的 PersistentVolumeClaim,它绑定到一个名为 "cat-pictures-pv" 的 PersistentVolume,并且有一个使用此 PersistentVolumeClaim 的 "cat-pictures-writer" Deployment。
第一步,你需要编辑 PersistentVolume 的 spec.persistentVolumeReclaimPolicy
并将其设置为 Retain
。这确保当你删除相应的 PersistentVolumeClaim 时,你的 PersistentVolume 不会被删除。
kubectl patch pv cat-pictures-pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
接下来,你需要停止任何正在使用绑定到你想要迁移的 PersistentVolume 的 PersistentVolumeClaim 的工作负载,然后删除该 PersistentVolumeClaim。
完成后,你需要清除 PersistentVolume 的 spec.claimRef.uid
,以确保在重新创建时 PersistentVolumeClaims 可以绑定到它。
kubectl scale --replicas=0 deployment cat-pictures-writer
kubectl delete pvc cat-pictures-pvc
kubectl patch pv cat-pictures-pv -p '{"spec":{"claimRef":{"uid":""}}}'
之后,你需要将 PersistentVolume 的访问模式替换为 ReadWriteOncePod
kubectl patch pv cat-pictures-pv -p '{"spec":{"accessModes":["ReadWriteOncePod"]}}'
注意
ReadWriteOncePod 访问模式不能与其他访问模式组合使用。更新时,请确保 ReadWriteOncePod 是 PersistentVolume 上唯一的访问模式,否则请求将失败。接下来,你需要修改你的 PersistentVolumeClaim,将 ReadWriteOncePod 设置为唯一的访问模式。你还应该将 PersistentVolumeClaim 的 spec.volumeName
设置为你的 PersistentVolume 的名称。
完成此操作后,你可以重新创建 PersistentVolumeClaim 并启动你的工作负载
# IMPORTANT: Make sure to edit your PVC in cat-pictures-pvc.yaml before applying. You need to:
# - Set ReadWriteOncePod as the only access mode
# - Set spec.volumeName to "cat-pictures-pv"
kubectl apply -f cat-pictures-pvc.yaml
kubectl apply -f cat-pictures-writer-deployment.yaml
最后,如果你之前更改了 PersistentVolume 的 spec.persistentVolumeReclaimPolicy
,可以将其改回 Delete
。
kubectl patch pv cat-pictures-pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'
你可以阅读为 Pod 配置 PersistentVolume 存储以获取更多关于 PersistentVolumes 和 PersistentVolumeClaims 的详细信息。
哪些卷插件支持此功能?
唯一支持此功能的卷插件是 CSI 驱动。SIG Storage 不打算在树内插件中支持此功能,因为它们正作为CSI 迁移的一部分被弃用。对于喜欢在使用 CSI 迁移启用的情况下使用传统树内卷 API 的用户,可能会考虑在 Beta 版本中提供支持。
作为存储厂商,我如何为我的 CSI 驱动添加对这种访问模式的支持?
ReadWriteOncePod 访问模式无需对 CSI 驱动进行任何更新即可开箱即用,但确实需要更新 CSI 边车。话虽如此,如果你想了解 CSI 规范(v1.5.0+)的最新变化,请继续阅读。
为了区分旧的 SINGLE_NODE_WRITER
访问模式,CSI 规范引入了两种新的访问模式。它们是 SINGLE_NODE_SINGLE_WRITER
和 SINGLE_NODE_MULTI_WRITER
。为了向边车(例如 external-provisioner)表明你的驱动理解并接受这两种新的 CSI 访问模式,你的驱动还需要为 控制器服务 和 节点服务 声明 SINGLE_NODE_MULTI_WRITER
能力。
如果你想了解这些访问模式和能力位的动机,还可以阅读 KEP-2485 (ReadWriteOncePod PersistentVolume 访问模式) 中的 CSI 规范变更,卷能力 部分。
更新你的 CSI 驱动以使用新接口
第一步,你需要将驱动的 container-storage-interface
依赖更新到 v1.5.0+ 版本,该版本包含对这些新访问模式和能力的支持。
接受新的 CSI 访问模式
如果你的 CSI 驱动包含验证请求的 CSI 访问模式的逻辑,则可能需要更新。如果它目前接受 SINGLE_NODE_WRITER
,则应更新以同时接受 SINGLE_NODE_SINGLE_WRITER
和 SINGLE_NODE_MULTI_WRITER
。
以 GCP PD CSI 驱动验证逻辑 为例,下面是如何对其进行扩展的方法。
diff --git a/pkg/gce-pd-csi-driver/utils.go b/pkg/gce-pd-csi-driver/utils.go
index 281242c..b6c5229 100644
--- a/pkg/gce-pd-csi-driver/utils.go
+++ b/pkg/gce-pd-csi-driver/utils.go
@@ -123,6 +123,8 @@ func validateAccessMode(am *csi.VolumeCapability_AccessMode) error {
case csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY:
case csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY:
case csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER:
+ case csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER:
+ case csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER:
default:
return fmt.Errorf("%v access mode is not supported for for PD", am.GetMode())
}
声明新的 CSI 控制器和节点服务能力
你的 CSI 驱动还需要在 ControllerGetCapabilities
和 NodeGetCapabilities
RPC 中返回新的 SINGLE_NODE_MULTI_WRITER
能力。
以 GCP PD CSI 驱动能力声明逻辑 为例,下面是如何对其进行扩展的方法。
diff --git a/pkg/gce-pd-csi-driver/gce-pd-driver.go b/pkg/gce-pd-csi-driver/gce-pd-driver.go
index 45903f3..0d7ea26 100644
--- a/pkg/gce-pd-csi-driver/gce-pd-driver.go
+++ b/pkg/gce-pd-csi-driver/gce-pd-driver.go
@@ -56,6 +56,8 @@ func (gceDriver *GCEDriver) SetupGCEDriver(name, vendorVersion string, extraVolu
csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
+ csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
+ csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
}
gceDriver.AddVolumeCapabilityAccessModes(vcam)
csc := []csi.ControllerServiceCapability_RPC_Type{
@@ -67,12 +69,14 @@ func (gceDriver *GCEDriver) SetupGCEDriver(name, vendorVersion string, extraVolu
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
csi.ControllerServiceCapability_RPC_LIST_VOLUMES_PUBLISHED_NODES,
+ csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
}
gceDriver.AddControllerServiceCapabilities(csc)
ns := []csi.NodeServiceCapability_RPC_Type{
csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
+ csi.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
}
gceDriver.AddNodeServiceCapabilities(ns)
实现 NodePublishVolume
行为
CSI 规范概述了对同一卷多次调用 NodePublishVolume
RPC 但参数不同(例如目标路径)时的预期行为。有关在你驱动中实现时的预期行为的更多详细信息,请参阅 CSI 规范 NodePublishVolume 部分中的第二个表。
更新你的 CSI 边车
部署 CSI 驱动时,你必须将以下 CSI 边车更新到依赖于 CSI spec v1.5.0+ 和 Kubernetes v1.22 API 的版本。最低要求的版本是
下一步是什么?
作为此功能晋升 Beta 的一部分,SIG Storage 计划更新 Kubernetes 调度器,以支持与 ReadWriteOncePod 存储相关的 Pod 抢占。这意味着如果两个 Pod 请求具有 ReadWriteOncePod 的 PersistentVolumeClaim,则优先级最高的 Pod 将获得对 PersistentVolumeClaim 的访问权,而任何优先级较低的 Pod 将被从节点中抢占,并且无法访问 PersistentVolumeClaim。
我如何了解更多信息?
有关 ReadWriteOncePod 访问模式和 CSI 规范更改动机的更多详细信息,请参阅 KEP-2485。
我如何参与?
Kubernetes #csi Slack 频道以及任何 SIG Storage 标准沟通渠道 都是联系 SIG Storage 和 CSI 团队的好方式。
特别感谢以下人员提供的富有洞察力的审阅和设计考量
- Abdullah Gharaibeh (ahg-g)
- Aldo Culquicondor (alculquicondor)
- Ben Swartzlander (bswartz)
- Deep Debroy (ddebroy)
- Hemant Kumar (gnufied)
- Humble Devassy Chirammal (humblec)
- James DeFelice (jdef)
- Jan Šafránek (jsafrane)
- Jing Xu (jingxu97)
- Jordan Liggitt (liggitt)
- Michelle Au (msau42)
- Saad Ali (saad-ali)
- Tim Hockin (thockin)
- Xing Yang (xing-yang)
如果你对参与 CSI 或 Kubernetes 存储系统的任何部分的设计和开发感兴趣,请加入 Kubernetes 存储特别兴趣小组 (SIG)。我们正在快速发展,随时欢迎新的贡献者。