Kubernetes 中的应用如果使用持久卷保存数据,迟早会面对误删、升级失败或需要复制测试环境的场景。把数据导出一份再导入,或者直接复制 PV 目录,往往效率很低,而且很难保证一致性。Kubernetes 从 1.17 开始逐渐稳定下来的 VolumeSnapshot 能力,允许用声明式对象触发底层存储系统创建快照,再基于快照恢复或克隆出新的 PVC。接下来从机制到命令一步步操作。

存储卷快照的底层机制
Kubernetes 的快照功能并不是把所有数据复制一份然后压缩成一个文件,它更像是在存储层面对某个时间点的卷状态做一次标记。真正执行快照动作的是 CSI 驱动,因此你的存储类型是否支持 CREATE_SNAPSHOT 能力是关键。可以通过 kubectl get csidriver 查看驱动列表,或查看 StorageClass 的参数确认。如果驱动不支持快照,创建 VolumeSnapshot 时会出现控制器无法找到快照供应方的错误。
Kubernetes 通过几个 CRD 把快照操作声明化。VolumeSnapshotClass 定义快照由哪个 CSI 驱动提供、删除策略是什么,类似 StorageClass 之于 PV。VolumeSnapshot 是用户创建的请求,表示对某个 PVC 生成快照。系统会在后台创建 VolumeSnapshotContent 作为集群级资源,记录快照在存储系统上的唯一标识。快照完成后,底层存储通常会以写时复制方式记录差异,所以创建速度很快,但后续大量写入可能带来额外开销。
apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotClass metadata: name: csi-hostpath-snapclass driver: hostpath.csi.k8s.io deletionPolicy: Delete
上面这段 YAML 中的 driver 必须与你的 CSI 驱动名称一致,deletionPolicy 设为 Delete 表示当 VolumeSnapshot 删除时,底层快照也会被回收;如果希望保留底层快照,可以改成 Retain。在创建快照前,还需要确认集群已经部署 snapshot-controller 和相关 CRD,因为很多发行版默认不安装。使用 kubectl api-resources | grep snapshot 可以快速检查 API 资源是否存在。
从创建快照到恢复 PVC
先创建快照类,然后针对源 PVC 创建 VolumeSnapshot。source.persistentVolumeClaimName 指定源卷。快照请求提交后,控制器会调用 CSI 驱动创建快照,并把状态同步回 VolumeSnapshot。可以用命令查看 readyToUse 字段,只有 readyToUse 变为 true 才表示快照真正可用。
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: app-data-snapshot
spec:
volumeSnapshotClassName: csi-hostpath-snapclass
source:
persistentVolumeClaimName: app-data-pvc
kubectl get volumesnapshot kubectl get volumesnapshotcontent kubectl describe volumesnapshot app-data-snapshot kubectl get pvc -n default
当 readyToUse 为 true 时,说明快照已经可用于恢复。恢复时创建新的 PVC,在 spec.dataSource 中引用快照名称和 apiGroup。需要注意,新 PVC 的 storageClassName 与 accessModes 必须和快照兼容,requested storage 不能小于快照对应卷的容量。恢复出的 PVC 是一份独立数据副本,和原来的 PVC 没有任何关联,删除原卷或快照不会影响新卷。
这里有一个常见的误解:有人认为从快照恢复出的 PVC 只读或者只能读不能写,实际上恢复出的 PVC 与普通 PVC 一致,挂载给 Pod 后可以进行完整的读写操作。不过底层存储如果采用写时复制,第一次写入时需要从原快照复制数据块,延迟可能略高,属于正常现象。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restored-pvc
spec:
dataSource:
name: app-data-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
克隆卷的两种实现方式
克隆卷通常有两种思路。第一种是先创建快照,再从快照恢复 PVC,适合需要保留某个时间点版本或跨存储类迁移的场景。第二种是直接让 PVC 的 dataSource 指向另一个 PVC,Kubernetes 会调用 CSI 的 CREATE_VOLUME 能力,以源卷为基准生成一个新卷。这种方式省去显式快照对象,但底层仍然依赖存储系统的克隆能力。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: clone-pvc
spec:
dataSource:
name: source-pvc
kind: PersistentVolumeClaim
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
直接克隆在语义上类似从最新状态复制,但很多 CSI 驱动在实现时仍然会先内部快照再创建新卷。因此不要在数据库大量写入的同时执行克隆,除非驱动和应用支持一致性保证。对于 MySQL、PostgreSQL 等有状态应用,最好先执行 FLUSH TABLES WITH READ LOCK 或者暂停写入,再触发快照或克隆。
跨命名空间克隆也是常见需求。VolumeSnapshot 属于命名空间资源,默认不能跨命名空间引用,必须先把快照从源命名空间复制或创建到目标命名空间,或者使用支持跨命名空间的方案。直接克隆 PVC 同样要求 dataSource 指向同命名空间的 PVC。容量方面,克隆卷的请求容量不能小于源卷容量,否则控制面会拒绝请求。存储类型和访问模式不一致时,也可能出现绑定失败,需要提前确认 StorageClass 是否一致。
排错与生产环境建议
生产环境排查快照问题,先看 VolumeSnapshot 是否长时间没有 readyToUse。kubectl describe volumesnapshot 的 Events 中通常会显示错误原因。常见问题是缺少 VolumeSnapshotClass、驱动不支持快照、或者 snapshot-controller 未正常运行。检查 kube-system 或部署命名空间中的 snapshot-controller Pod 日志,往往能定位到具体的 CSI 错误码。
删除快照卡在 Terminating 是另一个高频问题。这是因为 VolumeSnapshotContent 上有 finalizer,控制器需要先告知存储系统删除底层快照。如果 CSI 驱动响应超时或存储后端不可达,资源会一直无法删除。可以通过 kubectl patch volumesnapshotcontent 移除 finalizer 强制清理,但这样做会让底层快照变成孤儿资源,需要到存储管理界面手动删除。
快照数量也需要制定策略。虽然快照创建快,但底层差异块会持续占用空间,尤其是写入频繁的数据库卷。建议结合 CronJob 自动创建定时快照,同时设置保留数量。快照不能替代异地备份,若存储集群本身损坏,本机快照也会丢失,所以关键数据仍然需要有独立的备份副本。
Kubernetes存储卷快照卷克隆修改时间:2026-09-29 14:19:48