K8s Volume Snapshot이란?
Kubernetes VolumeSnapshot은 PersistentVolume의 특정 시점 스냅샷을 생성하고, 이를 기반으로 새 PVC를 복원하는 기능입니다. 데이터베이스 백업, 블루-그린 데이터 마이그레이션, 장애 복구(DR)에 필수적인 스토리지 운영 패턴입니다. Kubernetes 1.20에서 GA되었으며, CSI 드라이버가 스냅샷을 지원해야 합니다.
VolumeSnapshot 아키텍처
Volume Snapshot은 3개의 CRD로 구성됩니다:
| 리소스 | 역할 | 비유 |
|---|---|---|
VolumeSnapshotClass |
스냅샷 정책 정의 (CSI 드라이버, 삭제 정책) | StorageClass와 동일 역할 |
VolumeSnapshot |
스냅샷 요청 (사용자가 생성) | PVC와 동일 역할 |
VolumeSnapshotContent |
실제 스냅샷 데이터 (자동 생성) | PV와 동일 역할 |
VolumeSnapshotClass 설정
# AWS EBS CSI 드라이버용 VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: ebs-snapshot-class
annotations:
snapshot.storage.kubernetes.io/is-default-class: "true"
driver: ebs.csi.aws.com
deletionPolicy: Retain # Delete: PVC 삭제 시 스냅샷도 삭제
# Retain: PVC 삭제해도 스냅샷 보존
parameters:
tagSpecification_1: "Environment=production"
tagSpecification_2: "ManagedBy=kubernetes"
---
# GCP PD CSI 드라이버용
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: gcp-snapshot-class
driver: pd.csi.storage.gke.io
deletionPolicy: Retain
parameters:
storage-locations: asia-northeast3
---
# Ceph RBD CSI 드라이버용 (온프레미스)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: ceph-snapshot-class
driver: rbd.csi.ceph.com
deletionPolicy: Delete
parameters:
clusterID: ceph-cluster-1
csi.storage.k8s.io/snapshotter-secret-name: ceph-secret
csi.storage.k8s.io/snapshotter-secret-namespace: ceph-system
스냅샷 생성과 상태 확인
# PVC에서 스냅샷 생성
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-20260320
namespace: production
labels:
app: mysql
snapshot-type: scheduled
spec:
volumeSnapshotClassName: ebs-snapshot-class
source:
persistentVolumeClaimName: mysql-data # 원본 PVC
# 스냅샷 생성 및 상태 확인
kubectl apply -f mysql-snapshot.yaml
# 상태 확인
kubectl get volumesnapshot mysql-snapshot-20260320 -n production
# NAME READYTOUSE SOURCEPVC RESTORESIZE AGE
# mysql-snapshot-20260320 true mysql-data 50Gi 2m
# 상세 정보
kubectl describe volumesnapshot mysql-snapshot-20260320 -n production
# Status:
# Bound Volume Snapshot Content Name: snapcontent-abc123
# Creation Time: 2026-03-20T21:00:00Z
# Ready To Use: true
# Restore Size: 50Gi
# VolumeSnapshotContent 확인 (자동 생성됨)
kubectl get volumesnapshotcontent
# NAME READYTOUSE RESTORESIZE DELETIONPOLICY DRIVER
# snapcontent-abc123 true 53687091200 Retain ebs.csi.aws.com
스냅샷에서 PVC 복원
스냅샷을 dataSource로 지정하여 새 PVC를 생성합니다:
# 스냅샷에서 새 PVC 복원
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data-restored
namespace: production
spec:
accessModes:
- ReadWriteOnce
storageClassName: gp3-encrypted # 원본과 동일 StorageClass
resources:
requests:
storage: 50Gi # 스냅샷 크기 이상
dataSource:
name: mysql-snapshot-20260320 # 스냅샷 이름
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
# 복원 후 Pod에 마운트
apiVersion: v1
kind: Pod
metadata:
name: mysql-restored
namespace: production
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-data-restored # 복원된 PVC
자동 스냅샷: CronJob 패턴
CronJob으로 정기 스냅샷을 생성하고 오래된 스냅샷을 정리합니다:
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-snapshot-cron
namespace: production
spec:
schedule: "0 2 * * *" # 매일 새벽 2시
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
serviceAccountName: snapshot-manager
restartPolicy: OnFailure
containers:
- name: snapshot-creator
image: bitnami/kubectl:1.29
command:
- /bin/sh
- -c
- |
set -e
DATE=$(date +%Y%m%d-%H%M)
SNAPSHOT_NAME="mysql-snap-${DATE}"
# 스냅샷 생성
cat <
# RBAC: snapshot-manager ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: snapshot-manager
namespace: production
rules:
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshots"]
verbs: ["get", "list", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: snapshot-manager-binding
namespace: production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: snapshot-manager
subjects:
- kind: ServiceAccount
name: snapshot-manager
namespace: production
StatefulSet 데이터 복구 패턴
StatefulSet의 PVC를 스냅샷에서 복구하는 실전 절차입니다:
# 1. StatefulSet 스케일 다운
kubectl scale statefulset mysql --replicas=0 -n production
# 2. 기존 PVC 백업 (이름 변경)
kubectl get pvc mysql-data-mysql-0 -n production -o yaml > pvc-backup.yaml
# 3. 기존 PVC 삭제
kubectl delete pvc mysql-data-mysql-0 -n production
# 4. 스냅샷에서 동일 이름으로 PVC 복원
cat <
크로스 네임스페이스 복원
다른 네임스페이스의 스냅샷에서 복원하려면 VolumeSnapshotContent를 직접 참조합니다:
# production 네임스페이스의 스냅샷 → staging으로 복원
# 1. VolumeSnapshotContent 이름 확인
kubectl get volumesnapshot mysql-snapshot-20260320 -n production
-o jsonpath='{.status.boundVolumeSnapshotContentName}'
# snapcontent-abc123
# 2. staging에 VolumeSnapshot 생성 (snapshotContentName 참조)
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: mysql-snapshot-from-prod
namespace: staging
spec:
source:
volumeSnapshotContentName: snapcontent-abc123
# 3. staging에서 PVC 복원
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data-staging
namespace: staging
spec:
accessModes: [ReadWriteOnce]
storageClassName: gp3-encrypted
resources:
requests:
storage: 50Gi
dataSource:
name: mysql-snapshot-from-prod
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
PVC 클론: 스냅샷 없이 볼륨 복제
CSI 드라이버가 지원하면 스냅샷 단계 없이 PVC-to-PVC 직접 클론도 가능합니다:
# 기존 PVC를 직접 클론 (스냅샷 생성 단계 생략)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data-clone
namespace: production
spec:
accessModes: [ReadWriteOnce]
storageClassName: gp3-encrypted
resources:
requests:
storage: 50Gi
dataSource:
name: mysql-data # 원본 PVC 이름
kind: PersistentVolumeClaim # VolumeSnapshot이 아닌 PVC
모니터링과 트러블슈팅
# 스냅샷 이벤트 확인
kubectl get events -n production --field-selector reason=ProvisioningFailed
kubectl get events -n production --field-selector involvedObject.kind=VolumeSnapshot
# CSI 드라이버 로그 확인
kubectl logs -n kube-system -l app=ebs-csi-controller -c csi-snapshotter
# 스냅샷 용량 모니터링 (Prometheus 메트릭)
# volumesnapshot_readytouse: 스냅샷 준비 상태
# volumesnapshot_restoresize_bytes: 복원 크기
# 일반적인 에러와 해결법
# 1. "snapshot class not found"
# → VolumeSnapshotClass가 올바른 CSI 드라이버를 참조하는지 확인
kubectl get volumesnapshotclass
# 2. "failed to take snapshot: rpc error"
# → CSI 드라이버가 스냅샷을 지원하는지 확인
kubectl get csidriver -o jsonpath='{range .items[*]}{.metadata.name}: snapshot={.spec.volumeLifecycleModes}{"n"}{end}'
# 3. "snapshot is bound to a non-existing content"
# → VolumeSnapshotContent가 삭제됨, deletionPolicy: Retain 확인
kubectl get volumesnapshotcontent | grep -v READY
# 4. 복원 PVC가 Pending 상태
# → storage 크기가 스냅샷의 restoreSize 이상인지 확인
kubectl get volumesnapshot -o jsonpath='{.status.restoreSize}'
핵심 정리
| 작업 | 리소스 | 핵심 설정 |
|---|---|---|
| 정책 정의 | VolumeSnapshotClass | driver, deletionPolicy |
| 스냅샷 생성 | VolumeSnapshot | source.persistentVolumeClaimName |
| PVC 복원 | PVC + dataSource | dataSource.kind: VolumeSnapshot |
| 자동화 | CronJob | kubectl + 보존 정책 스크립트 |
| 직접 클론 | PVC + dataSource | dataSource.kind: PersistentVolumeClaim |
VolumeSnapshot은 K8s 스토리지 운영의 핵심 도구입니다. CronJob으로 자동 스냅샷을 생성하고 deletionPolicy: Retain으로 데이터 안전성을 확보하세요. StatefulSet 복구, 크로스 네임스페이스 마이그레이션, 개발 환경 데이터 복제 등 다양한 시나리오에 활용할 수 있습니다. K8s PV·PVC·StorageClass 심화와 K8s Velero 백업·복구 운영도 함께 참고하세요.