Kubernetes 1.32:卷组快照进入 Beta 阶段

卷组快照在 Kubernetes 1.27 版本中作为 Alpha 特性被引入。最近发布的 Kubernetes v1.32 将该支持提升到了 beta 阶段。对卷组快照的支持依赖于一组用于组快照的扩展 API。这些 API 允许用户为一组卷创建崩溃一致性快照。在后台,Kubernetes 使用标签选择器将多个 PersistentVolumeClaim 分组以进行快照。一个关键目标是允许你将这组快照恢复到新的卷中,并基于一个崩溃一致的恢复点来恢复你的工作负载。

这项新功能仅支持 CSI 卷驱动。

卷组快照概述

一些存储系统提供了创建多个卷的崩溃一致性快照的能力。组快照表示在同一时间点从多个卷创建的**副本**。组快照既可以用于重新生成新卷(预填充了快照数据),也可以用于将现有卷恢复到之前的状态(由快照表示)。

为何向 Kubernetes 添加卷组快照?

Kubernetes 卷插件系统已经提供了一个强大的抽象,可以自动执行块存储和文件存储的制备、挂接、挂载、调整大小和快照操作。

支撑所有这些特性的是 Kubernetes 的工作负载可移植性目标:Kubernetes 旨在在分布式应用程序和底层集群之间创建一个抽象层,以便应用程序可以与它们运行的集群的具体细节无关,并且应用程序部署不需要特定于集群的知识。

已经有一个 VolumeSnapshot API,它提供了为持久卷创建快照以防止数据丢失或数据损坏的能力。然而,还有其他快照功能未被 VolumeSnapshot API 覆盖。

一些存储系统支持一致性组快照,允许在同一时间点从多个卷创建快照,以实现写操作顺序一致性。这对于包含多个卷的应用程序非常有用。例如,一个应用程序可能将数据存储在一个卷中,将日志存储在另一个卷中。如果数据卷和日志卷的快照在不同时间创建,当灾难发生时,如果从这些快照恢复,应用程序将不一致,无法正常工作。

诚然,你可以先静默应用程序,然后依次为作为应用程序一部分的每个卷创建单个快照,在所有单个快照都创建完毕后再取消静默应用程序。这样,你将获得应用一致的快照。

然而,有时应用程序静默可能非常耗时,以至于你希望减少静默的频率,或者可能根本无法静默应用程序。例如,用户可能希望每周运行一次带有应用程序静默的备份,而每晚运行一次不带应用程序静默但具有一致性组支持的备份,这种支持提供了组内所有卷的崩溃一致性。

用于卷组快照的 Kubernetes API

Kubernetes 对**卷组快照**的支持依赖于三种用于管理快照的 API Kind:

VolumeGroupSnapshot
由 Kubernetes 用户(或者可能由你自己的自动化)创建,用于请求为多个持久卷声明创建一个卷组快照。它包含有关卷组快照操作的信息,例如卷组快照的创建时间戳以及是否准备就绪。此对象的创建和删除表示希望创建或删除集群资源(一个组快照)。
VolumeGroupSnapshotContent
由快照控制器为动态创建的 VolumeGroupSnapshot 创建。它包含有关卷组快照的信息,包括卷组快照 ID。此对象代表集群上已制备的资源(一个组快照)。VolumeGroupSnapshotContent 对象与为其创建的 VolumeGroupSnapshot 存在一对一的绑定关系。
VolumeGroupSnapshotClass
由集群管理员创建,用于描述应如何创建卷组快照,包括驱动信息、删除策略等。

这三个 API Kind 被定义为CustomResourceDefinitions (CRD)。为了让 CSI 驱动支持卷组快照,这些 CRD 必须安装在 Kubernetes 集群中。

支持卷组快照需要哪些组件

卷组快照在 external-snapshotter 仓库中实现。实现卷组快照意味着添加或更改了几个组件:

  • 为 VolumeGroupSnapshot 和两个支持性 API 添加了新的 CustomResourceDefinitions。
  • 卷组快照控制器逻辑被添加到通用快照控制器中。
  • 在 snapshotter sidecar 控制器中添加了调用 CSI 的逻辑。

卷快照控制器和 CRD 在每个集群部署一次,而 sidecar 则与每个 CSI 驱动捆绑在一起。

因此,将卷快照控制器和 CRD 作为集群插件进行部署是合理的。

Kubernetes 项目建议 Kubernetes 发行版将卷快照控制器和 CRD 作为其 Kubernetes 集群管理过程的一部分进行捆绑和部署(独立于任何 CSI 驱动)。

Beta 版本有哪些新功能?

  • CSI 规范中的 VolumeGroupSnapshot 特性在 v1.11.0 版本中进入了 GA 阶段。

  • 快照验证 webhook 在 external-snapshotter v8.0.0 中被弃用,现已被移除。大部分验证 webhook 逻辑已作为验证规则添加到 CRD 中。这些验证规则要求的最低 Kubernetes 版本是 1.25。验证 webhook 中有一项功能未移至 CRD,即防止为同一 CSI 驱动创建多个默认的卷快照类和多个默认的卷组快照类。随着验证 webhook 的移除,当为同一 CSI 驱动存在多个默认卷快照类或多个默认卷组快照类时,动态制备 VolumeSnapshot 或 VolumeGroupSnapshot 时仍会引发错误。

  • snapshot-controller 和 CSI snapshotter sidecar 中的 enable-volumegroup-snapshot 标志已被特性门控取代。由于 VolumeGroupSnapshot 是一个新 API,该特性进入 Beta 阶段,但特性门控默认是禁用的。要使用此特性,请在启动 snapshot-controller 和 CSI snapshotter sidecar 时添加标志 --feature-gates=CSIVolumeGroupSnapshot=true 来启用该特性门控。

  • 动态创建 VolumeGroupSnapshot 及其对应的单个 VolumeSnapshot 和 VolumeSnapshotContent 对象的逻辑已从 CSI snapshotter 移至通用 snapshot-controller。新的 RBAC 规则已添加到通用 snapshot-controller 中,并且相应地从 CSI snapshotter sidecar 中删除了一些 RBAC 规则。

我如何使用 Kubernetes 卷组快照

使用 Kubernetes 创建一个新的组快照

一旦定义了 VolumeGroupSnapshotClass 对象,并且你有一些想要一起快照的卷,你可以通过创建一个 VolumeGroupSnapshot 对象来请求一个新的组快照。

组快照的源(source)指定了底层的组快照是应该动态创建,还是应该使用一个预先存在的 VolumeGroupSnapshotContent。

预先存在的 VolumeGroupSnapshotContent 由集群管理员创建。它包含了存储系统上真实卷组快照的详细信息,可供集群用户使用。

必须设置组快照的源中的以下成员之一。

  • selector - 一个针对要组合在一起进行快照的 PersistentVolumeClaim 的标签查询。此选择器将用于匹配添加到 PVC 的标签。
  • volumeGroupSnapshotContentName - 指定一个代表现有卷组快照的预先存在的 VolumeGroupSnapshotContent 对象的名称。

动态制备组快照

在以下示例中,有两个 PVC。

NAME    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      VOLUMEATTRIBUTESCLASS   AGE
pvc-0   Bound    pvc-6e1f7d34-a5c5-4548-b104-01e72c72b9f2   100Mi      RWO            csi-hostpath-sc   <unset>                 2m15s
pvc-1   Bound    pvc-abc640b3-2cc1-4c56-ad0c-4f0f0e636efa   100Mi      RWO            csi-hostpath-sc   <unset>                 2m7s

为 PVC 添加标签。

% kubectl label pvc pvc-0 group=myGroup
persistentvolumeclaim/pvc-0 labeled

% kubectl label pvc pvc-1 group=myGroup
persistentvolumeclaim/pvc-1 labeled

对于动态制备,必须设置一个选择器,以便快照控制器可以找到带有匹配标签的 PVC,将它们一起进行快照。

apiVersion: groupsnapshot.storage.k8s.io/v1beta1
kind: VolumeGroupSnapshot
metadata:
  name: snapshot-daily-20241217
  namespace: demo-namespace
spec:
  volumeGroupSnapshotClassName: csi-groupSnapclass
  source:
    selector:
      matchLabels:
        group: myGroup

在 VolumeGroupSnapshot 规约中,用户可以指定 VolumeGroupSnapshotClass,其中包含有关应使用哪个 CSI 驱动来创建组快照的信息。动态制备需要一个 VolumGroupSnapshotClass。

apiVersion: groupsnapshot.storage.k8s.io/v1beta1
kind: VolumeGroupSnapshotClass
metadata:
  name: csi-groupSnapclass
  annotations:
    kubernetes.io/description: "Example group snapshot class"
driver: example.csi.k8s.io
deletionPolicy: Delete

卷组快照创建的结果是,将创建一个相应的 VolumeGroupSnapshotContent 对象,其 volumeGroupSnapshotHandle 指向存储系统上的一个资源。

作为卷组快照创建的一部分,将创建两个单独的卷快照。

NAME                                                                        READYTOUSE   SOURCEPVC   RESTORESIZE   SNAPSHOTCONTENT                                                                AGE
snapshot-0962a745b2bf930bb385b7b50c9b08af471f1a16780726de19429dd9c94eaca0   true         pvc-0       100Mi         snapcontent-0962a745b2bf930bb385b7b50c9b08af471f1a16780726de19429dd9c94eaca0   16m
snapshot-da577d76bd2106c410616b346b2e72440f6ec7b12a75156263b989192b78caff   true         pvc-1       100Mi         snapcontent-da577d76bd2106c410616b346b2e72440f6ec7b12a75156263b989192b78caff   16m

使用 Kubernetes 导入现有组快照

要将一个预先存在的卷组快照导入 Kubernetes,你还必须导入相应的单个卷快照。

识别单个卷快照句柄,首先手动构造一个 VolumeSnapshotContent 对象,然后创建一个指向该 VolumeSnapshotContent 对象的 VolumeSnapshot 对象。对每个单个卷快照重复此操作。

然后手动创建一个 VolumeGroupSnapshotContent 对象,指定存储系统上已存在的 volumeGroupSnapshotHandle 和单个 volumeSnapshotHandles。

apiVersion: groupsnapshot.storage.k8s.io/v1beta1
kind: VolumeGroupSnapshotContent
metadata:
  name: static-group-content
spec:
  deletionPolicy: Delete
  driver: hostpath.csi.k8s.io
  source:
    groupSnapshotHandles:
      volumeGroupSnapshotHandle: e8779136-a93e-11ef-9549-66940726f2fd
      volumeSnapshotHandles:
      - e8779147-a93e-11ef-9549-66940726f2fd
      - e8783cd0-a93e-11ef-9549-66940726f2fd
  volumeGroupSnapshotRef:
    name: static-group-snapshot
    namespace: demo-namespace

之后,创建一个指向该 VolumeGroupSnapshotContent 对象的 VolumeGroupSnapshot 对象。

apiVersion: groupsnapshot.storage.k8s.io/v1beta1
kind: VolumeGroupSnapshot
metadata:
  name: static-group-snapshot
  namespace: demo-namespace
spec:
  source:
    volumeGroupSnapshotContentName: static-group-content

如何在 Kubernetes 中使用组快照进行恢复

在恢复时,用户可以请求从作为 VolumeGroupSnapshot 一部分的 VolumeSnapshot 对象创建一个新的 PersistentVolumeClaim。这将触发制备一个新卷,该卷预先填充了来自指定快照的数据。用户应重复此操作,直到从属于组快照的所有快照中创建出所有卷。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: examplepvc-restored-2024-12-17
  namespace: demo-namespace
spec:
  storageClassName: example-foo-nearline
  dataSource:
    name: snapshot-0962a745b2bf930bb385b7b50c9b08af471f1a16780726de19429dd9c94eaca0
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteOncePod
  resources:
    requests:
      storage: 100Mi # must be enough storage to fit the existing snapshot

作为存储供应商,我如何为我的 CSI 驱动添加对组快照的支持?

要实现卷组快照功能,CSI 驱动**必须**:

  • 实现一个新的组控制器服务。
  • 实现组控制器 RPC:`CreateVolumeGroupSnapshot`、`DeleteVolumeGroupSnapshot` 和 `GetVolumeGroupSnapshot`。
  • 添加组控制器能力 `CREATE_DELETE_GET_VOLUME_GROUP_SNAPSHOT`。

更多详细信息,请参阅 CSI 规范Kubernetes-CSI 驱动开发者指南

如前所述,强烈建议 Kubernetes 发行版将卷快照控制器和 CRD 作为其 Kubernetes 集群管理过程的一部分进行捆绑和部署(独立于任何 CSI 驱动)。

作为此推荐部署过程的一部分,Kubernetes 团队提供了许多 sidecar(辅助)容器,包括已更新以支持卷组快照的 external-snapshotter sidecar 容器

external-snapshotter 监视 Kubernetes API 服务器上的 VolumeGroupSnapshotContent 对象,并触发针对 CSI 端点的 `CreateVolumeGroupSnapshot` 和 `DeleteVolumeGroupSnapshot` 操作。

有哪些限制?

Kubernetes 卷组快照的 Beta 实现有以下限制:

  • 不支持将现有 PVC 恢复到由快照表示的早期状态(仅支持从快照制备新卷)。
  • 除了存储系统提供的任何保证(例如崩溃一致性)之外,不提供应用程序一致性保证。有关应用程序一致性的更多讨论,请参阅此文档

下一步是什么?

根据反馈和采用情况,Kubernetes 项目计划在未来版本中将卷组快照实现推向正式发布(GA)。

我如何了解更多信息?

我如何参与?

这个项目,和所有 Kubernetes 项目一样,是许多来自不同背景的贡献者共同努力的成果。我谨代表 SIG Storage,向在过去几个季度中挺身而出,帮助项目达到 Beta 阶段的贡献者们表示衷心的感谢:

对于有兴趣参与 CSI 或 Kubernetes 存储系统任何部分的设计和开发的人,欢迎加入 Kubernetes 存储特别兴趣小组 (SIG)。我们随时欢迎新的贡献者。

我们还定期举行数据保护工作组会议。欢迎新成员加入我们的讨论。