스테이트풀한 애플리케이션을 운영하다보면 정기적인 백업이 필요하며, 백업된 데이터를 복구하는 작업도 필요하다. 이를 위해서는 볼륨 스냅샷이라는 기능을 사용해야 한다. 관련된 리소스는 VolumeSnapshot, VolumeSnapshotClass, VolumeSnapshotContent 가 있고 각각 아래와 같은 역할을 한다.
| 리소스명 |
설명 |
볼륨 관련 리소스와 비교 |
| VolumeSnapshot(VS) |
사용자가 스냅샷 생성을 요청하는 선언적 리소스 |
PersistentVolumeClaim 과 같은 역할 |
| VolumeSnapshotClass(VSClass) |
스냅샷을 생성할 때 사용할 드라이버(local.csi.openebs.io 와 같은)와 삭제 정책(deletionPolicy)을 정의한 리소스 |
StorageClass 와 같은 역할 |
| VolumeSnapshotContent(VSC) |
스냅샷의 실제 데이터 위치와 정보를 담고 있는 클러스터 단위의 리소스. 사용자가 직접 만들기보다는 CSI 컨트롤러에 의해 자동 생성됨 |
PersistentVolume 과 같은 역할 |
영구 볼륨을 보통 직접 생성하지 않고 PersistentVolumeClaim 이라는 "볼륨 요청 명세서"와 StorageClass 라는 "볼륨 프로비저닝 정책서"를 통해 자동 생성하듯이, 스냅샷도 VolumeSnapshot 이라는 "스냅샷 요청 명세서"와 VolumeSnapshotClass 라는 "스냅샷핸들링 정책서"를 통해 자동 생성한다.
생성된 스냅샷(VSC)을 볼륨(PV)으로 만드는 방법(즉, 복구하는 방법)은 PVC 의 매니페스트 spec.dataSource 항목에 스냅샷을 생성할 때 사용한 VolumeSnapshot 을 지정하여 생성하면 된다.
요약하면 스냅샷 저장/복구는 아래와 같다.
- 저장: PVC 가 있는 상태(당연히 PV가 있음)에서 VS 을 생성하면 VSC 가 생성된다.
- 복구: VS 가 있는 상태(당연히 VSC가 있음)에서 복구용 PVC를 생성하면 복구된 PV 가 생성된다.
앞서 동적 프로비저닝을 위해 Static NFS 타입의 스토리지를 구축해봤고, Block 장치를 영구 볼륨 사용하기 위해 OpenEBS LocalPV-LVM 방식의 스토리지를 구축해봤다. 하지만 스냅샷 기능을 파일시스템 기반 스토리지에서 실습하기 위해서는 CSI NFS 스토리지를 사용해야 하므로, 아래 실습을 따라하며 환경 설치부터 진행해야 한다.(앞서 구축한 Static NFS 는 스냅샷을 지원하지 않는 프로비저너를 사용했다.)
1. 사전 준비
1.1 헬름 설치
아래를 따라하며 Helm(쿠버네티스 패키지 관리 도구)을 설치한다.
# ARM64용 파일 다운로드(사용자 환경에 따라 다르게 지정)
$ curl -fsSL -o helm-v3.14.0-linux-arm64.tar.gz https://get.helm.sh/helm-v3.14.0-linux-arm64.tar.gz
# 압축 해제
$ tar -zxvf helm-v3.14.0-linux-arm64.tar.gz
# 파일 이동
$ mkdir -p $HOME/bin
$ cp linux-arm64/helm $HOME/bin/helm
# 설정 적용
$ echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc
$ source ~/.bashrc
# 설치 확인
$ helm version
version.BuildInfo{Version:"v3.14.0", GitCommit:"3fc9f4b2638e76f26739cd77c7017139be81d0ea", GitTreeState:"clean", GoVersion:"go1.21.5"}
1.2 NFS CSI 드라이버 설치
아래를 따라하며 NFS CSI 드라이버를 설치한다.
# 저장소(Repository) 추가 및 업데이트
$ helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts
$ helm repo update
# 드라이버 설치
$ helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --version v4.6.0
# 설치 상태 확인
$ kubectl --namespace=default get pods --selector="app.kubernetes.io/instance=csi-driver-nfs" --watch
NAME READY STATUS RESTARTS AGE
csi-nfs-controller-dc4b79d4c-25tzc 4/4 Running 0 42s
csi-nfs-node-dpn22 3/3 Running 0 42s
csi-nfs-node-fx7nj 3/3 Running 0 42s
csi-nfs-node-ldx9v 3/3 Running 0 42s
2. Snapshot Controller 설치
# RBAC(Role-Based Access Control, 역할 기반 액세스 제어) 및 컨트롤러 배포
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
아래 커맨드로 설치를 확인한다. Running 상태가 될 때까지 기다린다.
$ kubectl get pods -n kube-system | grep snapshot-controller
snapshot-controller-d4969fd45-749fk 1/1 Running 0 92s
snapshot-controller-d4969fd45-kkxfc 1/1 Running 0 92s
3. 스토리지 클래스 생성
아래와 같은 매니페스트로 스토리지 클래스를 생성한다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: sample-nfs-csi-snapshot-source-storageclass
provisioner: nfs.csi.k8s.io
parameters:
server: 192.168.0.36 # 마스터 노드(NFS 서버) IP
share: /srv/nfs/kubedata # NFS 공유 디렉토리 경로
reclaimPolicy: Delete
volumeBindingMode: Immediate
아래 커맨드로 생성된 스토리지 클래스를 확인한다.
$ kubectl get sc sample-nfs-csi-snapshot-source-storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
sample-nfs-csi-snapshot-source-storageclass nfs.csi.k8s.io Delete Immediate false 4s
4. PVC 생성
아래와 같은 매니페스트로 원본 볼륨을 요청할 PVC 를 생성한다. spec.storageClassName 에는 위에서 생성한 스토리지 클래스 이름을 지정한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sample-nfs-csi-snapshot-source-pvc
spec:
storageClassName: sample-nfs-csi-snapshot-source-storageclass
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
아래 커맨드로 생성된 PVC와 동적 프로비저닝된 PV를 확인한다. 스토리지 클래스의 volumeBindingMode: Immediate 이기 때문에 바로 PV가 생성 및 바인딩 된다.
$ kubectl get pvc sample-nfs-csi-snapshot-source-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
sample-nfs-csi-snapshot-source-pvc Bound pvc-ce7ee910-d765-4a39-a804-796bb9845309 200Mi RWX sample-nfs-csi-snapshot-source-storageclass <unset> 20s
$ kubectl get pv pvc-ce7ee910-d765-4a39-a804-796bb9845309
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-ce7ee910-d765-4a39-a804-796bb9845309 200Mi RWX Delete Bound default/sample-nfs-csi-snapshot-source-pvc sample-nfs-csi-snapshot-source-storageclass <unset> 26s
5. Pod 생성
아래와 같은 매니페스트로 실제 PVC 를 사용할 Pod 를 생성한다. command 와 args 를 통해 컨테이너 구동시 컨테이너 내의 /data/time.txt 경로에 시간정보를 기록한다.
apiVersion: v1
kind: Pod
metadata:
name: sample-nfs-csi-snapshot-source-pod
spec:
containers:
- name: tools-container
image: busybox
command: ["/bin/sh", "-c"]
args: ["date > /data/time.txt && sleep infinity"]
volumeMounts:
- name: data-volume
mountPath: /data
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: sample-nfs-csi-snapshot-source-pvc
readOnly: false
아래와 같은 커맨드로 실제 컨테이너 내에 생성된 시간정보 파일을 확인해본다. 나중에 스냅샷을 만든 후 다시 복구했을 때 이 파일이 그대로 복구되어야 한다.
$ kubectl exec -it sample-nfs-csi-snapshot-source-pod -- cat /data/time.txt
Mon Mar 2 08:54:02 UTC 2026
여기까지 확인되면, 동적 프로비저닝 방식으로 영구 볼륨 생성까지 진행한 것이다. 스냅샷을 지원하는 CSI NFS 를 드라이버를 사용한다는 점 이외에는 일반적인 동적 프로비저닝 방식과 차이가 없다.
다음 단계부터는 영구 볼륨을 스냅샷으로 백업하고 복구하는 방법이다.
6. VSClass 생성
아래와 같은 매니페스트로 VolumeSnapshotClass 를 생성한다. driver 에는 스냅샷을 지원하는 CSI 드라이버 명칭을 지정한다.
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: sample-nfs-csi-snapshot-volumesnapshotclass
driver: nfs.csi.k8s.io # CSI 드라이버 명칭
deletionPolicy: Delete
아래 커맨드로 생성된 VSClass 를 확인한다.
$ kubectl get vsclass sample-nfs-csi-snapshot-volumesnapshotclass
NAME DRIVER DELETIONPOLICY AGE
sample-nfs-csi-snapshot-volumesnapshotclass nfs.csi.k8s.io Delete 7s
7. VS 생성(VSC 자동생성)
아래와 같은 매니페스트로 VolumeSnapshot 을 생성한다. spec.volumeSnapshotClassName 에는 사용할 VSClass 이름을, spec.source.persistentVolumeClaimName 에는 스냅샷으로 저장할 원본 볼륨에 대한 PVC 이름을 지정한다.
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: sample-nfs-csi-snapshot-volumesnapshot
spec:
volumeSnapshotClassName: sample-nfs-csi-snapshot-volumesnapshotclass # 사용할 VSClass 이름
source:
persistentVolumeClaimName: sample-nfs-csi-snapshot-source-pvc # 원본 PVC 이름
아래 커맨드로 생성된 VS와 자동으로 생성된 VSC(VolumeSnapshotContent)를 확인한다. 둘 다 READYTOUSE 항목이 true 로 조회되어야 한다.
$ kubectl get vs sample-nfs-csi-snapshot-volumesnapshot
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
sample-nfs-csi-snapshot-volumesnapshot true sample-nfs-csi-snapshot-source-pvc 173 sample-nfs-csi-snapshot-volumesnapshotclass snapcontent-b2b577a0-aeda-42b4-8f20-dafa81496496 6s 6s
$ kubectl get vsc snapcontent-b2b577a0-aeda-42b4-8f20-dafa81496496
NAME READYTOUSE RESTORESIZE DELETIONPOLICY DRIVER VOLUMESNAPSHOTCLASS VOLUMESNAPSHOT VOLUMESNAPSHOTNAMESPACE AGE
snapcontent-b2b577a0-aeda-42b4-8f20-dafa81496496 true 173 Delete nfs.csi.k8s.io sample-nfs-csi-snapshot-volumesnapshotclass sample-nfs-csi-snapshot-volumesnapshot default 75s
8. 스냅샷 복구용 PVC 생성
아래와 같은 매니페스트로 스냅샷을 영구 볼륨으로 복구할 PVC 를 생성한다. spec.storageClassName 에는 원본 볼륨을 생성할 때 사용한 스토리지 클래스(SC) 이름을 지정하며, spec.dataSource 에는 복구할 스냅샷을 생성할 때 사용한 볼륨 스냅샷(VS) 이름을 지정한다. spec.resources.requests.storage 항목은 원본보다 크거나 같아야 한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sample-nfs-csi-snapshot-restore-pvc
spec:
storageClassName: sample-nfs-csi-snapshot-source-storageclass # 원본과 동일한 StorageClass
accessModes:
- ReadWriteMany
resources:
requests:
storage: 300Mi # 원본보다 크거나 같아야 함
dataSource: # 핵심: 스냅샷을 원본으로 지정
kind: VolumeSnapshot
name: sample-nfs-csi-snapshot-volumesnapshot # 복구할 VS 이름
apiGroup: snapshot.storage.k8s.io
아래 커맨드로 생성된 PVC를 확인한다.
$ kubectl get pvc sample-nfs-csi-snapshot-restore-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
sample-nfs-csi-snapshot-restore-pvc Bound pvc-ef300ede-72ed-454f-afdb-25c4dc981df9 300Mi RWX sample-nfs-csi-snapshot-source-storageclass <unset> 30s
정상적으로 PVC가 생성되면, 자동으로 스냅샷을 복구한 PV가 생성된다.
9. 복구된 PV 조회
아래 커맨드로 스냅샷이 복구된 PV를 조화한다. 위에서 확인한 VOLUME 명칭으로 생성된다.
$ kubectl get pv pvc-ef300ede-72ed-454f-afdb-25c4dc981df9
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-ef300ede-72ed-454f-afdb-25c4dc981df9 300Mi RWX Delete Bound default/sample-nfs-csi-snapshot-restore-pvc sample-nfs-csi-snapshot-source-storageclass <unset> 79s
10. 복구된 PV 를 사용하는 Pod 를 생성하여 실제 복구 데이터 확인
아래와 같은 매니페스트로 복구된 PV를 사용하는 파드를 띄운다. 컨테이너 기동시 파일 내용을 출력하는 커맨드가 담겨있다.
apiVersion: v1
kind: Pod
metadata:
name: sample-nfs-csi-snapshot-restore-pod
spec:
containers:
- name: tools-container
image: busybox
command: ["/bin/sh", "-c"]
args: ["cat /data/time.txt && sleep infinity"]
volumeMounts:
- name: data-volume
mountPath: /data
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: sample-nfs-csi-snapshot-restore-pvc
readOnly: false
아래처럼 파드의 로그를 확인한다. 원본 볼륨에 저장되었던 내용이 그대로 복구되었음을 알 수 있다.
$ kubectl logs sample-nfs-csi-snapshot-restore-pod
Mon Mar 2 08:54:02 UTC 2026