Kubernetes 基础体系 · 第 39/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。

Kubernetes VolumeSnapshot:一致性、Class、恢复、克隆和备份边界

VolumeSnapshot 是 Kubernetes 对 CSI(Container Storage Interface)存储快照能力的声明式抽象。它允许用户为一个 PVC(PersistentVolumeClaim)创建某个时间点的存储副本,并将该副本用于恢复新 PVC、创建卷克隆,或作为备份系统的数据源。

VolumeSnapshot 不是 Kubernetes 内置的文件复制器,也不等于完整备份。它能否创建、快照包含什么、数据是否可跨集群恢复,分别取决于 CSI 驱动、底层存储、应用写入状态和备份系统的设计。

本文使用稳定的 snapshot.storage.k8s.io/v1 API。具体 CSI 驱动的参数、快照性能、跨区域能力和一致性保证必须以驱动及存储厂商文档为准。


一、先建立对象模型:VolumeSnapshot 不是一个孤立资源

VolumeSnapshot 相关的核心对象有四个:

  • PersistentVolumeClaim:应用使用的卷声明。
  • VolumeSnapshot:用户请求创建或引用一个快照。
  • VolumeSnapshotClass:描述由哪个 CSI 驱动创建快照,以及删除策略。
  • VolumeSnapshotContent:集群中某个实际快照的绑定对象,类似 PV 与 PVC 的关系。

关系可以简化为:

flowchart LR
    Pod --> PVC
    PVC --> PV
    PV --> CSI[(CSI Driver)]
    VS[VolumeSnapshot] --> VSC[VolumeSnapshotContent]
    VSC --> CSI
    VSC --> Backend[(Storage Backend)]
    RestorePVC[恢复后的 PVC] --> VS
    ClonePVC[克隆后的 PVC] --> PVC

VolumeSnapshot 是命名空间级资源;VolumeSnapshotClassVolumeSnapshotContent 是集群级资源。

创建快照时,用户通常只创建 VolumeSnapshot。快照控制器会根据它选择合适的 VolumeSnapshotClass,调用 CSI 驱动创建后端快照,并创建或绑定 VolumeSnapshotContent。最终,VolumeSnapshot.status.readyToUse 表示该快照是否已经可以作为恢复源使用。

1. VolumeSnapshot

一个基本的快照声明如下:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: orders-db-snapshot-001
  namespace: production
spec:
  volumeSnapshotClassName: csi-example-snapshots
  source:
    persistentVolumeClaimName: orders-db

这里的含义是:

  1. production 命名空间中创建名为 orders-db-snapshot-001 的快照。
  2. 快照源是同一命名空间中的 PVC orders-db
  3. 使用名为 csi-example-snapshotsVolumeSnapshotClass
  4. 控制器最终会把该请求转换为 CSI 驱动能够识别的后端快照操作。

source 也可以引用已有的 VolumeSnapshotContent,这主要用于预先存在的快照导入或静态绑定:

spec:
  source:
    volumeSnapshotContentName: imported-content

不能同时填写 persistentVolumeClaimNamevolumeSnapshotContentName

2. VolumeSnapshotClass

一个快照类示例:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-example-snapshots
driver: csi.example.com
deletionPolicy: Delete
parameters:
  snapshotType: standard

字段含义如下:

  • driver:必须与实际卷使用的 CSI 驱动名称匹配。
  • deletionPolicy
    • Delete:删除 VolumeSnapshot 后,控制器也请求删除底层快照。
    • Retain:删除 Kubernetes 快照对象后,保留底层快照,便于人工管理或后续导入。
  • parameters:传给 CSI 驱动的参数,名称和含义完全由驱动定义。

VolumeSnapshotClass 不指定存储容量,也不决定恢复后 PVC 的容量。它主要决定“由哪个驱动、以什么参数和删除策略创建快照”。

生产环境经常同时存在多个快照类,例如普通快照、加密快照、跨区域复制快照。默认类可以通过标准注解标记:

metadata:
  annotations:
    snapshot.storage.kubernetes.io/is-default-class: "true"

如果用户没有设置 volumeSnapshotClassName,控制器会尝试选择适用的默认类。实际环境中应避免同一 CSI 驱动存在多个含义冲突的默认类,否则会出现选择歧义或行为不符合预期。

3. VolumeSnapshotContent

VolumeSnapshotContent 表示后端的实际快照:

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  name: snapcontent-example
spec:
  deletionPolicy: Retain
  driver: csi.example.com
  source:
    snapshotHandle: backend-snapshot-id-123
  volumeSnapshotRef:
    name: orders-db-snapshot-001
    namespace: production

在动态创建场景中,通常不需要手工写这个对象。快照控制器会在 CSI 驱动返回快照句柄后创建它。

source.snapshotHandle 是后端快照的标识符,不是一个可以跨存储厂商通用解释的 Kubernetes ID。把某个厂商的快照句柄直接复制到另一套集群,通常不能恢复,除非目标集群使用同一个驱动、能够访问同一个后端,并且驱动支持这种导入方式。


二、快照创建时到底发生了什么

创建 VolumeSnapshot 后,典型数据流如下:

sequenceDiagram
    participant U as 用户
    participant API as Kubernetes API
    participant SC as Snapshot Controller
    participant Sidecar as CSI Snapshotter
    participant CSI as CSI Driver
    participant Store as 存储后端

    U->>API: 创建 VolumeSnapshot
    SC->>API: 查找 PVC、PV 和 VolumeSnapshotClass
    SC->>Sidecar: 观察到待处理快照
    Sidecar->>CSI: CreateSnapshot(volume_id, name, parameters)
    CSI->>Store: 创建后端快照
    Store-->>CSI: snapshot_id, size_bytes, ready
    CSI-->>Sidecar: 返回快照信息
    Sidecar->>API: 创建/更新 VolumeSnapshotContent
    Sidecar->>API: 更新 VolumeSnapshot.status

这里涉及的组件通常不是 Kubernetes API Server 自带的全部功能:

  1. Kubernetes 集群需要安装 VolumeSnapshot CRD。
  2. 需要运行外部 snapshot-controller。
  3. CSI 驱动侧通常需要运行 csi-snapshotter sidecar。
  4. CSI 驱动本身必须实现相应的快照能力。

因此,只有 CRD 存在并不代表集群真的支持快照。CRD 只能让 API Server 接受对象,不能替代 CSI 驱动的后端操作。

可以先检查环境:

kubectl api-resources | grep -i volumesnapshot

kubectl get crd \
  volumesnapshots.snapshot.storage.k8s.io \
  volumesnapshotcontents.snapshot.storage.k8s.io \
  volumesnapshotclasses.snapshot.storage.k8s.io

kubectl get volumesnapshotclass
kubectl get pods -A | grep -E 'snapshot-controller|csi'

不同发行版可能把 snapshot-controller 放在不同命名空间,Pod 名称也可能不同。检查结果只能证明组件存在,还需要确认它们使用的版本和 CSI 驱动兼容。

创建快照后,应同时查看对象状态和事件:

kubectl -n production get volumesnapshot orders-db-snapshot-001 -o yaml
kubectl -n production describe volumesnapshot orders-db-snapshot-001
kubectl get volumesnapshotcontent
kubectl get events -n production --sort-by=.lastTimestamp

一个可用快照通常应看到类似状态:

status:
  boundVolumeSnapshotContentName: snapcontent-...
  creationTime: "2025-01-01T12:00:00Z"
  readyToUse: true
  restoreSize: "107374182400"

其中:

  • readyToUse: true 表示控制器认为该快照可以作为恢复源。
  • restoreSize 是恢复所需的最小容量提示,不一定等于源 PVC 的请求容量,也不等于底层存储实际占用空间。
  • creationTime 是控制器或驱动报告的快照时间,不应简单理解为业务事务提交时间。

如果 readyToUse 长时间为 false,常见原因包括:

  • CSI 驱动未实现快照;
  • VolumeSnapshotClass.driver 与 PV 的 CSI 驱动不匹配;
  • snapshot-controller 或 csi-snapshotter 未运行;
  • 后端配额不足;
  • 快照操作仍在进行;
  • CSI 驱动返回错误;
  • 底层卷类型不支持快照;
  • 权限、网络或云 API 调用失败。

此时不能只反复 kubectl apply。应检查 VolumeSnapshot 事件、VolumeSnapshotContent、snapshot-controller 日志和 CSI controller 日志。


三、一致性:快照保存了哪个时间点的什么状态

“快照是一致的”至少有三种不同含义:

  1. 崩溃一致性(crash consistency):类似突然断电后,文件系统能够通过日志恢复到可挂载状态。
  2. 文件系统一致性(filesystem consistency):文件系统的元数据和写入顺序得到合理处理。
  3. 应用一致性(application consistency):数据库、消息队列或业务应用内部的事务状态满足应用自己的恢复规则。

Kubernetes VolumeSnapshot API 本身并不保证第三种一致性。

1. 一个形式化描述

设卷上的写入操作序列为:

W={w1,w2,,wn}W = \{w_1, w_2, \ldots, w_n\}

后端在时间 tst_s 创建快照。若写入 wiw_itst_s 前已经被存储系统持久化,则快照可能包含它;若尚未持久化,则可能不包含它。

可以把快照内容表示为:

S(ts)={wicommit(wi)ts}S(t_s) = \{w_i \mid commit(w_i) \leq t_s\}

但对应用而言,单个业务事务往往包含多个写入。例如转账事务可能执行:

账户 A 扣款
账户 B 加款
写入事务日志
更新索引

如果快照发生在中间状态,可能得到:

账户 A 已扣款
账户 B 尚未加款
事务日志尚未完整写入

存储系统认为这可能是一个合法的块级快照;数据库却可能认为它不是一个可以直接使用的事务状态。

因此,“快照在某个时间点生成”不等于“应用在某个事务边界生成了备份”。

2. 写缓存是关键边界

数据可能经过多层缓存:

应用进程
  -> 文件系统页缓存
  -> 内核块层
  -> 节点或存储网络缓存
  -> 存储阵列缓存
  -> 持久介质

一个快照能否包含尚未落盘的数据,取决于应用是否执行 fsync、文件系统和内核是否正确处理写入顺序、CSI 驱动及后端是否提供持久化保证。

对数据库而言,WAL(Write-Ahead Log,预写日志)通常能让数据库在崩溃后恢复,但这不等于任何快照都能无条件安全恢复:

  • 数据页可能只写了一部分;
  • WAL 和数据文件可能处于不同时间点;
  • 数据库可能需要正确识别并回放 WAL;
  • 外部对象存储、密钥服务或配置资源不在卷快照中。

数据库自身的恢复机制可以把“崩溃一致快照”提升为可恢复状态,但是否满足业务备份要求,仍需执行真实恢复测试。

3. 如何接近应用一致性

常见流程是:

停止或暂停写入
    -> 执行应用 flush / checkpoint / backup mode
    -> 确认应用进入稳定状态
    -> 创建 VolumeSnapshot
    -> 确认 readyToUse=true
    -> 解除暂停

例如数据库可能提供:

  • 将数据库切换到备份模式;
  • 执行 checkpoint;
  • 暂停写入;
  • 刷新 WAL;
  • 记录快照对应的日志位置。

Kubernetes 本身不会自动理解这些数据库语义。可以通过运维脚本、Operator 或 Velero Hook 执行操作,但 Hook 成功也不自动证明应用一致性。必须明确:

  • Hook 是否真的停止了所有写入;
  • 快照请求是否在 Hook 完成后才发出;
  • 快照完成前是否会恢复写入;
  • 应用异常退出时如何解除暂停;
  • 是否需要同时备份 WAL、密钥和外部依赖。

四、从快照恢复 PVC

恢复的本质不是“把原 PVC 改回去”,而是以快照为数据源创建一个新的 PVC。

示例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: orders-db-restored
  namespace: recovery
spec:
  storageClassName: csi-example
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  dataSource:
    name: orders-db-snapshot-001
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io

创建前有几个重要前提:

  1. VolumeSnapshot 必须位于 recovery 命名空间,或者实际使用的恢复机制必须支持相应的跨命名空间处理。PVC 的 dataSource 不能任意引用其他命名空间的快照。
  2. storageClassName 应使用支持该快照驱动恢复的 StorageClass。
  3. 目标 StorageClass 的 CSI 驱动通常必须与快照的驱动匹配。
  4. 目标 PVC 容量不能小于 status.restoreSize
  5. CSI 驱动必须实现从快照创建卷的能力。

先检查快照:

kubectl -n recovery get volumesnapshot orders-db-snapshot-001 \
  -o jsonpath='{.status.readyToUse}{"\n"}{.status.restoreSize}{"\n"}'

然后创建 PVC:

kubectl apply -f restored-pvc.yaml
kubectl -n recovery get pvc orders-db-restored
kubectl -n recovery describe pvc orders-db-restored

PVC 进入 Bound 后,还不代表应用已经可用。后续还要验证:

  1. Pod 能否挂载卷;
  2. 文件系统是否能够挂载;
  3. 数据库是否能启动;
  4. 数据库是否完成日志恢复;
  5. 应用数据是否满足业务校验;
  6. 权限、UID、密钥和配置是否匹配。

如果 PVC 长时间处于 Pending,常见原因是:

  • 目标 StorageClass 不支持快照恢复;
  • CSI provisioner 没有处理 dataSource
  • 源快照未 readyToUse
  • 请求容量小于恢复所需容量;
  • 可用区或拓扑无法满足调度;
  • 存储后端配额不足;
  • 快照和目标卷不属于同一个驱动。

PVC 事件通常比 Pod 事件更接近根因:

kubectl -n recovery describe pvc orders-db-restored
kubectl get pv
kubectl get events -n recovery --sort-by=.lastTimestamp

恢复覆盖原 PVC 吗?

不会。Kubernetes 不会把快照直接“写回”现有 PVC。恢复过程创建的是新卷,新 PVC 绑定到新 PV。

这是一种安全边界:可以先挂载新卷进行校验,再通过应用切换、Service 切换或 StatefulSet 配置变更完成恢复。若必须替换原卷,应先停止写入并规划名称、PV、应用和回滚路径,不能依赖删除重建的偶然顺序。


五、克隆:从 PVC 复制卷,而不是从快照恢复

PVC 克隆使用另一个 PVC 作为 dataSource

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: orders-db-clone
  namespace: production
spec:
  storageClassName: csi-example
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  dataSource:
    name: orders-db
    kind: PersistentVolumeClaim
    apiGroup: v1

克隆和快照恢复的区别是:

操作 数据源 是否产生快照对象 典型用途
快照 PVC 的某个时间点 时间点恢复、回滚、备份来源
从快照恢复 VolumeSnapshot 否,产生新 PVC/PV 恢复新卷
PVC 克隆 现有 PVC 否,产生新 PVC/PV 测试副本、开发副本、快速复制

PVC 克隆的规范前提通常包括:

  • 源 PVC 与目标 PVC 在同一命名空间;
  • 使用支持克隆的 CSI 驱动;
  • 源和目标使用兼容的存储类型;
  • 目标容量不小于源卷需要的容量;
  • 后端支持相应的卷复制能力。

克隆是否是立即完成的零拷贝操作,取决于后端。某些存储系统使用写时复制,创建很快但共享底层块;另一些系统会执行完整数据复制。Kubernetes API 不保证具体实现和性能。

更重要的是,克隆不天然提供应用一致性。若源卷正在被数据库持续修改,克隆得到的仍可能只是某个存储层状态,而不是事务边界状态。若需要一致副本,应先让应用进入合适的备份状态,再执行克隆或先创建快照。


六、删除策略和生命周期:删除对象不一定删除数据

快照生命周期涉及两个层次:

VolumeSnapshot
    -> VolumeSnapshotContent
        -> 后端 Snapshot

deletionPolicy: Delete 时,删除用户对象通常会触发级联清理:

删除 VolumeSnapshot
    -> 删除或释放 VolumeSnapshotContent
        -> CSI DeleteSnapshot
            -> 删除后端快照

当策略为 Retain 时,Kubernetes 对象删除后,后端快照仍可能保留。这适合:

  • 需要独立保留的恢复点;
  • 集群迁移;
  • 事故调查;
  • 防止误删的长期保留策略。

Retain 会产生孤儿快照和存储费用。它也不会自动让快照可跨集群发现。保留的后端对象必须有清晰的清单、标签、所有权和导入流程。

删除快照前应确认:

kubectl -n production get volumesnapshot orders-db-snapshot-001
kubectl get volumesnapshotcontent

如果对象长期处于 Terminating,不要直接移除 finalizer。finalizer 通常用于阻止底层资源尚未清理时删除 Kubernetes 对象。强制删除可能留下后端快照,后续只能通过存储平台人工查找和清理。


七、快照的故障路径与诊断顺序

1. API 能创建,但永远不 Ready

先看:

kubectl -n production describe volumesnapshot orders-db-snapshot-001
kubectl -n production get volumesnapshot orders-db-snapshot-001 -o yaml
kubectl get volumesnapshotcontent

如果没有 VolumeSnapshotContent,重点检查 snapshot-controller、csi-snapshotter 和驱动选择。

如果有 VolumeSnapshotContent 但不 Ready,重点检查 CSI controller 日志和后端存储任务。

2. 快照已 Ready,但恢复 PVC Pending

重点看 PVC 事件:

kubectl -n recovery describe pvc orders-db-restored

典型因果链是:

快照驱动 = driver-a
目标 StorageClass = driver-b
    -> provisioner 无法识别 driver-a 的快照
    -> PVC 无法创建
    -> PVC 长期 Pending

即使两个驱动都来自同一云厂商,也不能据此假设它们可以互相恢复。必须匹配 CSI 驱动和后端能力。

3. PVC 已 Bound,但 Pod 挂载失败

这时问题已经从“快照恢复”进入“卷发布和挂载”阶段,可能涉及:

  • 节点插件未运行;
  • Attach/Publish 失败;
  • 文件系统类型不匹配;
  • 节点没有权限访问恢复后的卷;
  • RWO 卷被其他节点占用;
  • 设备已挂载但路径或权限错误。

应结合关联 PV、Pod 事件和 CSI node/controller 日志排查:

kubectl -n recovery describe pod restored-db
kubectl get pv
kubectl describe pv <恢复后的PV名称>

快照控制器只负责快照相关生命周期,不负责替代 CSI 的 Node、Attach、Mount 路径。


八、VolumeSnapshot 和备份的边界

快照通常只覆盖卷中的块或文件系统数据,不自动覆盖以下资源:

  • PVC、PV、StorageClass、VolumeSnapshotClass;
  • Deployment、StatefulSet、Service;
  • Secret、ConfigMap;
  • ServiceAccount 和 RBAC;
  • 数据库用户、外部密钥、DNS;
  • 对象存储中的文件;
  • 云平台外部资源;
  • 应用恢复所需的 WAL、日志或配置。

因此,一个只有 VolumeSnapshot 的方案可以回答:

“这个存储卷在某个时间点有一个恢复点。”

但不能完整回答:

“整个应用能否在另一个集群、另一个区域或灾难后恢复并继续提供服务?”

完整备份通常要同时保存:

B=K+V+M+RB = K + V + M + R

其中:

  • KK:Kubernetes 对象,例如 Deployment、PVC、Secret;
  • VV:卷数据,例如快照或数据搬运后的对象;
  • MM:应用元数据,例如数据库版本、WAL 位置、加密密钥引用;
  • RR:恢复顺序和验证规则。

缺少任何一项,都可能导致“备份任务成功但恢复失败”。

1. 快照备份的三个独立条件

设备份目标是灾难恢复,则至少需要:

  1. 持久性:快照在源卷删除后仍可用;
  2. 可达性:灾难发生后,恢复环境能够访问保存快照的后端;
  3. 可解释性:新集群知道快照对应哪个驱动、哪个卷和哪个应用。

可以用一个简单条件表示:

Recoverable=DurableReachableInterpretableRecoverable = Durable \land Reachable \land Interpretable

例如:

  • deletionPolicy: Delete,源 PVC 删除后快照也被删除:不满足 Durable
  • 快照只存在于故障区域:不满足 Reachable
  • 只备份了 VolumeSnapshot YAML,但新集群没有原 CSI 后端或快照句柄无法导入:不满足 Interpretable

2. Velero 的职责边界

Velero 这类备份系统通常需要分别处理:

  • Kubernetes 资源清单;
  • CSI 快照;
  • 或将卷数据复制到独立备份存储的数据移动机制;
  • Hook、恢复顺序和应用校验。

使用 CSI 快照作为 Velero 备份数据源时,实际能力取决于 Velero 版本、插件、CSI 驱动和后端。仅备份 Kubernetes 资源并不等于复制卷数据;仅创建 CSI 快照也不等于备份了 Kubernetes 资源。

Hook 可以实现类似以下流程:

备份前 Hook
    -> 暂停数据库写入或执行 checkpoint
    -> 备份系统创建资源和卷快照
    -> 确认快照完成
备份后 Hook
    -> 恢复数据库写入

恢复时也需要反向设计:

恢复命名空间和权限
    -> 恢复 Secret / ConfigMap
    -> 恢复快照或搬运卷数据
    -> 创建 PVC
    -> 恢复 StatefulSet / Pod
    -> 执行数据库恢复
    -> 进行业务读写校验

实际恢复顺序不能只依赖 Kubernetes 对象的创建顺序。例如数据库 Pod 可能在 Secret、密钥或网络策略尚未恢复时启动;即使 Pod Running,数据库也可能仍在回放日志。


九、一个完整的最小演练流程

下面假设集群已经安装了兼容的 CSI 驱动、snapshot-controller 和相关 CRD,且已有:

  • StorageClass:csi-example
  • CSI 驱动:csi.example.com
  • PVC:production/orders-db

先确认源 PVC:

kubectl -n production get pvc orders-db
kubectl get pvc -n production orders-db \
  -o jsonpath='{.spec.storageClassName}{"\n"}'

确认其 PV 使用的驱动:

kubectl get pvc -n production orders-db \
  -o jsonpath='{.spec.volumeName}{"\n"}'

kubectl get pv <源PV名称> -o yaml

然后创建快照:

kubectl apply -f - <<'EOF'
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: orders-db-snapshot-001
  namespace: production
spec:
  volumeSnapshotClassName: csi-example-snapshots
  source:
    persistentVolumeClaimName: orders-db
EOF

等待快照就绪:

kubectl -n production wait \
  --for=jsonpath='{.status.readyToUse}'=true \
  volumesnapshot/orders-db-snapshot-001 \
  --timeout=10m

若命令超时,说明对象没有在规定时间内进入可用状态。此时应回到事件和 CSI 日志,而不是直接进入恢复步骤。

为了避免跨命名空间引用问题,可以在恢复命名空间中创建同名的静态导入对象,但具体导入方式依赖 VolumeSnapshotContent 绑定、快照句柄和 CSI 驱动能力。更常见、更清晰的做法是让备份系统负责跨命名空间或跨集群恢复,而不是手工复制 YAML。

若在同一命名空间恢复:

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: orders-db-restored
  namespace: production
spec:
  storageClassName: csi-example
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  dataSource:
    name: orders-db-snapshot-001
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
EOF

检查恢复结果:

kubectl -n production get pvc orders-db-restored
kubectl -n production describe pvc orders-db-restored

最后应启动临时校验 Pod,验证文件和数据库,而不是只检查 PVC 是否为 Bound。例如可以先挂载到一个只读或隔离的检查 Pod,确认目录结构、数据库版本、表数量和关键业务记录。直接让生产 Deployment 同时挂载源卷和恢复卷,可能导致数据库锁冲突、数据误写或两个实例同时使用同一份身份信息。


十、常见误解

误解一:快照一定是完整备份

错误。快照可能只存在于原存储系统,且不包含 Kubernetes 资源、密钥和应用恢复元数据。

误解二:readyToUse: true 表示应用一定一致

错误。它表示 CSI 控制链路认为快照可用于恢复,不表示数据库事务处于一致边界。

误解三:恢复后的 PVC 可以比源 PVC 更小

通常不可以。恢复所需最小容量由 restoreSize 和 CSI 驱动能力共同决定。目标容量不足时,PVC 可能无法绑定或创建失败。

误解四:所有 CSI 驱动都支持快照和克隆

错误。CSI 驱动可以只支持创建、挂载和扩容,而不支持快照或克隆。能力必须逐项验证。

误解五:删除 VolumeSnapshot 后一定没有后端数据

不一定。Retain 会保留后端快照;即使使用 Delete,删除请求也可能因后端故障而失败。

误解六:快照跨集群复制 YAML 就能恢复

错误。Kubernetes 对象中的绑定关系和快照句柄依赖具体 CSI 驱动及后端。跨集群恢复需要同时解决数据可达性、驱动兼容性、身份权限和对象导入。


十一、生产环境真正需要验证的内容

快照方案至少应通过以下演练,而不是只验证创建命令成功:

  1. 在持续写入场景下创建快照;
  2. 恢复新 PVC 并验证数据库启动;
  3. 验证关键事务和索引;
  4. 删除源 PVC 后确认快照仍可恢复;
  5. 在独立集群或独立可用区测试恢复;
  6. 验证 Secret、密钥和外部依赖是否可用;
  7. 测试快照保留期和自动清理;
  8. 模拟 CSI controller、节点和后端 API 故障;
  9. 测量从快照到业务恢复的实际 RTO;
  10. 验证备份系统能否发现、搬运和恢复底层数据。

其中最容易被忽视的是“源卷删除后的恢复”。如果快照使用 Delete 策略,删除 VolumeSnapshot、PVC 或关联资源时可能触发级联清理;如果备份系统只记录 Kubernetes 对象,却没有独立复制后端数据,那么集群丢失后清单本身无法重建卷内容。

VolumeSnapshot 的正确定位是:

它提供了由 CSI 存储系统完成的卷级时间点复制能力;应用一致性、跨故障域持久性、Kubernetes 资源恢复和业务可验证性,需要由应用、存储后端和备份系统共同补齐。

只有把这几层边界分别验证,快照才不会从“快速恢复工具”被误用成未经演练的完整灾备方案。


系列导航与关联阅读

官方资料

本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。