Créer des snapshots de volumes persistants (avec Rook)
Sur un de mes clusters Kubernetes de lab, j’utilise VolSync pour faire des backups de mes volumes persistants vers un bucket s3 présent… dans le même cluster (oui, c’est pas très smart, mais je veux juste éviter de perdre mes données en cas de mauvaise manip). C’est pratique et simple à mettre en place, mais je lui reproche de ne pas être compatible avec les snapshots. Si jamais j’ai un volume continuellement en écriture, les backups ne seront pas cohérentes en fonction du temps que la copie prend (puisque l’application continue d’écrire pendant le backup).
Kubernetes propose un mécanisme de snapshot pour les volumes persistants via le CSI compatible avec certains CSI, dont celui de Rook-Ceph.
Installation du CSI Snapshotter
kubectl kustomize https://github.com/kubernetes-csi/external-snapshotter/client/config/crd | kubectl create -f -
customresourcedefinition.apiextensions.k8s.io/volumegroupsnapshotclasses.groupsnapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumegroupsnapshotcontents.groupsnapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumegroupsnapshots.groupsnapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumesnapshotclasses.snapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumesnapshotcontents.snapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumesnapshots.snapshot.storage.k8s.io created
git clone git@github.com:kubernetes-csi/external-snapshotter
cd external-snapshotter/
kubectl create -k client/config/crd
kubectl create -k deploy/kubernetes/snapshot-controller -n kube-system
Maintenant que le CSI Snapshotter est installé, nous allons pouvoir créer une VolumeSnapshotClass
qui va contenir les paramètres de snapshot (et où trouver les secrets pour accéder au cluster Rook-Ceph).
cat <<EOF | kubectl apply -f -
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-cephfsplugin-snapclass
driver: rook-ceph.cephfs.csi.ceph.com
parameters:
clusterID: rook-ceph
csi.storage.k8s.io/snapshotter-secret-name: rook-csi-cephfs-provisioner
csi.storage.k8s.io/snapshotter-secret-namespace: rook-ceph
deletionPolicy: Delete
EOF
Dans mon cas, j’utilise le driver de CephFS, mais il existe aussi un driver pour RBD. Il faut juste adapter le driver
et les paramètres.
Création d’un snapshot
À présent, il faut bien quelque chose à snapshoter, donc nous allons créer un PVC et un Pod qui va l’utiliser.
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cephfs-pvc
labels:
group: snapshot-test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: rook-ceph
---
apiVersion: v1
kind: Pod
metadata:
name: csicephfs-demo-pod
spec:
containers:
- name: web-server
image: nginx
volumeMounts:
- name: mypvc
mountPath: /var/lib/www/html
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: cephfs-pvc
readOnly: false
EOF
On va créer un ptit fichier qu’on voudra snapshoter.
kubectl exec -it csicephfs-demo-pod -- sh -c "echo 'Hello World!' > /var/lib/www/html/index.html"
Il ne reste qu’à créer le snapshot du PVC.
$ cat <<EOF | kubectl apply -f -
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: cephfs-pvc-snapshot
spec:
volumeSnapshotClassName: csi-cephfsplugin-snapclass
source:
persistentVolumeClaimName: cephfs-pvc
EOF
On obtient un object VolumeSnapshot
qui contient le snapshot du PVC, celui-ci est stocké directement dans Rook avec la référence d’un VolumeSnapshotContent
.
kubectl get volumesnapshot
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
cephfs-pvc-snapshot true cephfs-pvc 1Gi csi-cephfsplugin-snapclass snapcontent-4384779d-f64b-4fa5-b373-3b16b2cb8d4a 11m 2m
Le VolumeSnapshot est l’objet qui va relier le PVC et le VolumeSnapshotContent. C’est un peu l’équivalent d’un PVC avec PV pour les snapshots.
# kubectl get volumesnapshotcontents.snapshot.storage.k8s.io snapcontent-4384779d-f64b-4fa5-b373-3b16b2cb8d4a
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
annotations:
snapshot.storage.kubernetes.io/deletion-secret-name: rook-csi-cephfs-provisioner
snapshot.storage.kubernetes.io/deletion-secret-namespace: rook-ceph
creationTimestamp: "2025-08-05T15:36:19Z"
finalizers:
- snapshot.storage.kubernetes.io/volumesnapshotcontent-bound-protection
generation: 1
name: snapcontent-4384779d-f64b-4fa5-b373-3b16b2cb8d4a
resourceVersion: "27480"
uid: 1b4e3960-e420-4db9-a4b0-9aae0bd9a4c0
spec:
deletionPolicy: Delete
driver: rook-ceph.cephfs.csi.ceph.com
source:
volumeHandle: 0001-0009-rook-ceph-0000000000000001-a654bdc4-9b5d-4a86-be17-46bd74a7810a
sourceVolumeMode: Filesystem
volumeSnapshotClassName: csi-cephfsplugin-snapclass
volumeSnapshotRef:
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
name: cephfs-pvc-snapshot
namespace: default
resourceVersion: "27464"
uid: 4384779d-f64b-4fa5-b373-3b16b2cb8d4a
status:
creationTime: 1754408180236699000
readyToUse: true
restoreSize: 1073741824
snapshotHandle: 0001-0009-rook-ceph-0000000000000001-80112a7f-e288-462e-81e4-70da6c579ea6
J’aime pas beaucoup l’interface web de Ceph, mais on peut bien y voir que le snapshot a bien été créé (a654bdc4-9b5d-4a86-be17-46bd74a7810a
correspond à l’ID du volume dans Ceph, tandis que 80112a7f-e288-462e-81e4-70da6c579ea6
correspond à l’ID du snapshot).
Si jamais je veux stocker ce snapshot vers un stockage distant (comme ce que je fais avec VolSync), il faut passer par un DataMover
comme Velero ou Volume-Snapshot-Mover.
Concrèment, ce sont des opérateurs qui vont créer des PVCs à partir de VolumeSnapshots, et déployer des pods qui vont se charger de faire la copie des données via Kopia ou Restic.
Mais pour l’instant je boycotte Velero, et j’ai pas encore pris le temps de tester Volume-Snapshot-Mover, voyons plutôt comment utiliser ces snapshots directement sur le cluster !
Restauration d’un snapshot
Pour restaurer un snapshot, il faut créer un PVC qui va utiliser le VolumeSnapshot
comme source. Je peux aussi en profiter pour changer l’accessMode
.
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cephfs-pvc
spec:
storageClassName: rook-ceph
dataSource:
name: cephfs-pvc-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
EOF
Après quelques secondes où le pod reste en Pending
, le PVC est créé ! Il y a aussi des Events qui indiquent les différentes étapes du processus de restauration.
$ kubectl describe pvc cephfs-pvc-clone
# ...
Events:
Normal ExternalProvisioning 15s persistentvolume-controller Waiting for a volume to be created either by the external provisioner 'rook-ceph.cephfs.csi.ceph.com' or manually by the system administrator. If volume creation is delayed, please verify that the provisioner is running and correctly registered.
Warning ProvisioningFailed 14s rook-ceph.cephfs.csi.ceph.com_csi-cephfsplugin-provisioner-57f6db877d-tj9d8_e7403518-7e80-48da-abb1-ed35ff68fbbe failed to provision volume with StorageClass "rook-ceph": rpc error: code = Aborted desc = clone from snapshot is pending
Warning ProvisioningFailed 10s rook-ceph.cephfs.csi.ceph.com_csi-cephfsplugin-provisioner-57f6db877d-tj9d8_e7403518-7e80-48da-abb1-ed35ff68fbbe failed to provision volume with StorageClass "rook-ceph": rpc error: code = Aborted desc = clone from snapshot is already in progress
Normal Provisioning 9s (x3 over 15s) rook-ceph.cephfs.csi.ceph.com_csi-cephfsplugin-provisioner-57f6db877d-tj9d8_e7403518-7e80-48da-abb1-ed35ff68fbbe External provisioner is provisioning volume for claim "default/cephfs-pvc-clone"
Normal ProvisioningSucceeded 4s rook-ceph.cephfs.csi.ceph.com_csi-cephfsplugin-provisioner-57f6db877d-tj9d8_e7403518-7e80-48da-abb1-ed35ff68fbbe Successfully provisioned volume pvc-54969b97-cfb5-4ec5-a318-e9b8ebe8fb28
Le PVC peut être bound dans à plusieurs pods sur des nodes différents, puisque j’ai utilisé ReadWriteMany
comme accessMode
.
Ce que j’aurais pu faire aussi si je voulais uniquement dupliquer le PVC, c’est de créer un PersistentVolumeClaim
qui utilise directement le PVC source comme dataSource
. Ça évite de passer par un VolumeSnapshot
mais on ne garde pas la snapshot après clonage.
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cephfs-pvc-clone
spec:
storageClassName: rook-ceph
dataSource:
name: cephfs-pvc
kind: PersistentVolumeClaim
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
EOF
En revanche (mais ça ne me choque pas trop), il ne semble pas possible de restaurer un snapshot à chaud vers le même PVC dont le snapshot a été créé, je dois d’abord supprimer le PVC avant de le restaurer (et ce qui implique un petit downtime).
Prochaine étape pour mon lab : tester le data-mover pour backup ces snapshots !