本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

为 PersistentVolume 引入单 Pod 访问模式

上个月发布的 Kubernetes v1.22 引入了 PersistentVolumes 和 PersistentVolumeClaims 的新 ReadWriteOncePod 访问模式。借助此 Alpha 功能,Kubernetes 允许您将卷访问权限限制为集群中的单个 Pod。

什么是访问模式以及它们为什么重要?

在使用存储时,有不同的方式来模拟存储的使用方式。

例如,像网络文件共享这样的存储系统可以有许多用户同时读取和写入数据。在其他情况下,也许每个人都允许读取数据但不允许写入。对于高度敏感的数据,也许只允许一个用户读取和写入数据,而其他人则不允许。

在 Kubernetes 世界中,访问模式是您定义持久化存储如何被使用的方式。这些访问模式是 PersistentVolumes (PVs) 和 PersistentVolumeClaims (PVCs) 规范的一部分。

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-managerkubelet)强制执行,以确保只有某些 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-apiserverkube-schedulerkubelet 启用 ReadWriteOncePod Feature Gate。您可以通过设置命令行参数来启用该功能

--feature-gates="...,ReadWriteOncePod=true"

您还需要将以下 CSI 边车更新到这些版本或更高版本

创建 PersistentVolumeClaim

为了将 ReadWriteOncePod 访问模式用于您的 PV 和 PVC,您需要创建一个带有该访问模式的新 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"]}}'

接下来,您需要修改 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 迁移的一部分被弃用。对于喜欢使用旧版树内卷 API 并启用 CSI 迁移的用户,可能会考虑对 Beta 版提供支持。

作为存储供应商,我如何为我的 CSI 驱动程序添加对此访问模式的支持?

ReadWriteOncePod 访问模式开箱即用,无需对 CSI 驱动程序进行任何更新,但确实需要更新 CSI 边车。话虽如此,如果您想及时了解 CSI 规范 (v1.5.0+) 的最新更改,请继续阅读。

CSI 规范中引入了两种新的访问模式,以消除旧版 SINGLE_NODE_WRITER 访问模式的歧义。它们是 SINGLE_NODE_SINGLE_WRITERSINGLE_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_WRITERSINGLE_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 驱动程序还需要在 ControllerGetCapabilitiesNodeGetCapabilities 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 规范 v1.5.0+ 和 Kubernetes v1.22 API 的版本。最低要求版本为

下一步是什么?

作为此功能 Beta 毕业的一部分,SIG Storage 计划更新 Kubernetes 调度器以支持与 ReadWriteOncePod 存储相关的 Pod 抢占。这意味着如果两个 Pod 请求一个具有 ReadWriteOncePod 的 PersistentVolumeClaim,则优先级最高的 Pod 将获得对 PersistentVolumeClaim 的访问权限,而任何优先级较低的 Pod 将从节点中抢占,并且无法访问 PersistentVolumeClaim。

我如何了解更多信息?

请参阅 KEP-2485 以获取有关 ReadWriteOncePod 访问模式和 CSI 规范更改动机的更多详细信息。

我如何参与?

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)。我们正在快速发展,并始终欢迎新的贡献者。