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

Kubernetes 1.25:使用 Secret 进行节点驱动的 CSI 卷扩展

Kubernetes v1.25 于本月早些时候发布,引入了一项新功能,允许你的集群扩展存储卷,即使访问这些卷需要 Secret(例如:访问 SAN 网络的凭据)来执行节点扩容操作。这项新行为处于 Alpha 阶段,你必须启用一个特性门控(CSINodeExpandSecret)才能使用它。你还必须使用 CSI 存储;此变更与 Kubernetes 内置的存储驱动程序无关。

要开启这个新的 Alpha 功能,你需要在 kube-apiserver 和 kubelet 中启用 CSINodeExpandSecret 特性门控。这将开启一个机制,在 NodeExpansion 期间将 secretRef 配置发送给 CSI 驱动程序,从而利用该机制通过底层存储系统执行节点侧的扩容操作。

这是什么意思?

在 Kubernetes v1.24 之前,你可以定义一个集群级别的 StorageClass 来使用 StorageClass Secret,但你没有任何机制来指定在存储挂载到节点以及需要在节点侧扩展卷时将用于操作的凭据。

Kubernetes CSI 已经为特定类型的卷大小调整实现了一个类似的机制;即针对 PersistentVolume 的大小调整,这种调整独立于任何节点进行,被称为控制器扩容(Controller Expansion)。在这种情况下,你将一个 PersistentVolume 与一个包含卷大小调整操作凭据的 Secret 关联起来,以便进行控制器扩容。CSI 还支持一个 nodeExpandVolume 操作,CSI 驱动程序可以独立于控制器扩容或与控制器扩容一起使用,这种情况下,大小调整是由卷所挂载的集群节点驱动的。请阅读 Kubernetes 1.24:卷扩容现已成为稳定特性

  • 有时,CSI 驱动程序需要在进行节点级文件系统扩展操作之前检查后端块存储(或映像)的实际大小。这可以避免在文件系统扩展期间从后端存储集群返回假阳性结果。

  • 当 PersistentVolume 代表加密的块存储(例如使用 LUKS)时,你需要提供一个密码来扩展设备,并使其能够在设备上扩展文件系统。

  • 为了在节点扩容时进行各种验证,CSI 驱动程序必须连接到后端存储集群。如果 nodeExpandVolume 请求包含一个 secretRef,那么 CSI 驱动程序就可以利用它来连接到存储集群以执行集群操作。

它是如何工作的?

为了从这个版本的 Kubernetes 中启用此功能,SIG Storage 引入了一个名为 CSINodeExpandSecret 的新特性门控。一旦在集群中启用了该特性门控,NodeExpandVolume 请求就可以包含一个 secretRef 字段。NodeExpandVolume 请求是 CSI 的一部分;例如,在从 Kubernetes 控制平面发送到 CSI 驱动程序的请求中。

作为集群操作员,你可以将这些 Secret 作为 StorageClass 中的一个不透明参数来指定,就像你已经可以指定其他 CSI Secret 数据一样。StorageClass 需要设置一些 CSI 特定的参数。以下是这些参数的示例:

csi.storage.k8s.io/node-expand-secret-name: test-secret
csi.storage.k8s.io/node-expand-secret-namespace: default

如果启用了特性门控并且存储类携带了上述 Secret 配置,CSI Provisioner 就会在 NodeExpansion 请求中从 Secret 中接收凭据。

需要 Secret 才能进行在线扩容的 CSI 卷将设置 NodeExpandSecretRef 字段。如果未设置,则 NodeExpandVolume CSI RPC 调用将在没有 Secret 的情况下进行。

试一试

  1. 启用 CSINodeExpandSecret 特性门控(请参阅 特性门控)。

  2. 创建一个 Secret,然后创建一个使用该 Secret 的 StorageClass。

这是一个包含凭据的 Secret 的清单示例

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
  namespace: default
data:
stringData:
  username: admin
  password: t0p-Secret

这是一个引用这些凭据的 StorageClass 的清单示例

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-blockstorage-sc
parameters:
  csi.storage.k8s.io/node-expand-secret-name: test-secret   # the name of the Secret
  csi.storage.k8s.io/node-expand-secret-namespace: default  # the namespace that the Secret is in
provisioner: blockstorage.cloudprovider.example
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true

输出示例

如果 PersistentVolumeClaim (PVC) 创建成功,你可以在 PersistentVolume 的 spec.csi 字段中看到该配置(查找 spec.csi.nodeExpandSecretRef)。通过运行 kubectl get persistentvolume <pv_name> -o yaml 来检查它是否正常工作。你应该会看到类似下面的内容。

apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: blockstorage.cloudprovider.example
  creationTimestamp: "2022-08-26T15:14:07Z"
  finalizers:
  - kubernetes.io/pv-protection
  name: pvc-95eb531a-d675-49f6-940b-9bc3fde83eb0
  resourceVersion: "420263"
  uid: 6fa824d7-8a06-4e0c-b722-d3f897dcbd65
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 6Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: csi-pvc
    namespace: default
    resourceVersion: "419862"
    uid: 95eb531a-d675-49f6-940b-9bc3fde83eb0
  csi:
    driver: blockstorage.cloudprovider.example
    nodeExpandSecretRef:
      name: test-secret
      namespace: default
    volumeAttributes:
      storage.kubernetes.io/csiProvisionerIdentity: 1648042783218-8081-blockstorage.cloudprovider.example
    volumeHandle: e21c7809-aabb-11ec-917a-2e2e254eb4cf
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.hostpath.csi/node
          operator: In
          values:
          - racknode01
  persistentVolumeReclaimPolicy: Delete
  storageClassName: csi-blockstorage-sc
  volumeMode: Filesystem
status:
  phase: Bound

如果你随后触发在线存储扩容,kubelet 会将适当的凭据传递给 CSI 驱动程序,通过加载该 Secret 并将数据传递给存储驱动程序。

以下是一个调试日志示例

I0330 03:29:51.966241       1 server.go:101] GRPC call: /csi.v1.Node/NodeExpandVolume
I0330 03:29:51.966261       1 server.go:105] GRPC request: {"capacity_range":{"required_bytes":7516192768},"secrets":"***stripped***","staging_target_path":"/var/lib/kubelet/plugins/kubernetes.io/csi/blockstorage.cloudprovider.example/f7c62e6e08ce21e9b2a95c841df315ed4c25a15e91d8fcaf20e1c2305e5300ab/globalmount","volume_capability":{"AccessType":{"Mount":{}},"access_mode":{"mode":7}},"volume_id":"e21c7809-aabb-11ec-917a-2e2e254eb4cf","volume_path":"/var/lib/kubelet/pods/bcb1b2c4-5793-425c-acf1-47163a81b4d7/volumes/kubernetes.io~csi/pvc-95eb531a-d675-49f6-940b-9bc3fde83eb0/mount"}
I0330 03:29:51.966360       1 nodeserver.go:459] req:volume_id:"e21c7809-aabb-11ec-917a-2e2e254eb4cf" volume_path:"/var/lib/kubelet/pods/bcb1b2c4-5793-425c-acf1-47163a81b4d7/volumes/kubernetes.io~csi/pvc-95eb531a-d675-49f6-940b-9bc3fde83eb0/mount" capacity_range:<required_bytes:7516192768 > staging_target_path:"/var/lib/kubelet/plugins/kubernetes.io/csi/blockstorage.cloudprovider.example/f7c62e6e08ce21e9b2a95c841df315ed4c25a15e91d8fcaf20e1c2305e5300ab/globalmount" volume_capability:<mount:<> access_mode:<mode:SINGLE_NODE_MULTI_WRITER > > secrets:<key:"XXXXXX" value:"XXXXX" > secrets:<key:"XXXXX" value:"XXXXXX" >

未来展望

由于此功能仍处于 Alpha 阶段,Kubernetes Storage SIG 期望从 CSI 驱动程序作者那里获得更多测试和实现的更新或反馈。社区计划在未来的版本中最终将此功能提升到 Beta 阶段。

参与或了解更多?

增强提案包含了关于此功能历史和技术实现的许多细节。

要了解更多关于 Kubernetes 中基于 StorageClass 的动态供应,请参阅 存储类持久卷

请加入 Kubernetes 存储 SIG (特别兴趣小组) 来帮助我们增强此功能。已经有很多好主意了,我们非常欢迎更多人参与进来!