이 글에서는 쿠버네티스 환경에서 데이터를 영구적으로 저장하기 위한 핵심 개념인 PV(PersistentVolume)와 PVC(PersistentVolumeClaim)에 대해 상세히 알아보겠습니다. 특히 MinIO와 같은 스토리지 시스템에서 데이터 영속성을 확보하는 방법에 중점을 두고 설명하겠습니다.
📌 PV와 PVC의 개념 이해하기
✅ 쿠버네티스의 스토리지 문제
쿠버네티스 환경에서는 컨테이너가 언제든지 재시작되거나 다른 노드로 이동할 수 있습니다. 이런 특성은 유연성과 확장성을 제공하지만, 데이터 저장에는 큰 도전이 됩니다. 기본적으로 컨테이너 내부에 저장된 데이터는 컨테이너가 삭제되면 함께 사라집니다.
▶️ 예시: 컨테이너 재시작 시나리오
- MinIO 컨테이너가 오류로 재시작되면 내부 데이터가 모두 손실됨
- 사용자가 업로드한 파일, 버킷 구성 등이 모두 초기화됨
- 서비스 중단 및 데이터 손실 발생
✅ PV(PersistentVolume)란?
PersistentVolume은 관리자가 프로비저닝하거나 스토리지 클래스를 통해 동적으로 프로비저닝된 클러스터의 스토리지입니다. 컨테이너나 Pod의 생명주기와 별개로 존재하는 독립적인 리소스입니다.
▶️ PV의 주요 특징:
- 클러스터 수준의 리소스 (네임스페이스에 속하지 않음)
- 다양한 스토리지 백엔드 지원 (AWS EBS, Azure Disk, NFS, iSCSI 등)
- 용량, 접근 모드, 재사용 정책 등을 정의할 수 있음
- Pod의 생명주기와 독립적으로 유지됨
✅ PVC(PersistentVolumeClaim)란?
PersistentVolumeClaim은 사용자의 스토리지 요청입니다. Pod가 PV를 사용하기 위해서는 PVC를 통해 요청해야 합니다. PVC는 필요한 스토리지 용량과 접근 방식을 명시합니다.
▶️ PVC의 주요 특징:
- 네임스페이스에 속하는 리소스
- 필요한 스토리지 크기, 접근 모드 등을 지정
- 적절한 PV와 바인딩됨
- Pod에서 볼륨으로 마운트하여 사용
📌 PV/PVC 라이프사이클 이해하기
✅ 프로비저닝 (Provisioning)
PV를 생성하는 방법에는 정적 프로비저닝과 동적 프로비저닝 두 가지가 있습니다.
- 정적 프로비저닝 (Static Provisioning)
- 관리자가 미리 PV를 생성해 놓는 방식
- 클러스터에 사용 가능한 스토리지 풀을 준비
apiVersion: v1 # 쿠버네티스 API 버전으로, PV는 핵심 리소스이므로 v1 사용
kind: PersistentVolume # 리소스 종류를 PersistentVolume으로 지정
metadata:
name: minio-pv # 이 PV를 식별하기 위한 고유 이름 지정
spec:
capacity:
storage: 10Gi # 이 PV의 용량을 10GiB로 설정, MinIO의 데이터 저장에 충분한 공간 확보
accessModes:
- ReadWriteOnce # 하나의 노드에서만 읽기/쓰기가 가능하도록 설정, 대부분의 스토리지에서 지원하는 기본 모드
persistentVolumeReclaimPolicy: Retain # PVC가 삭제되어도 PV와 데이터를 유지하도록 설정, 중요 데이터 보존을 위함
hostPath: # Docker Desktop 환경에서 테스트용으로 사용하는 hostPath 타입 스토리지 지정
path: /data/minio-pv # 호스트 머신의 실제 경로, 데이터가 저장될 디렉토리 지정
- 동적 프로비저닝 (Dynamic Provisioning)
- StorageClass를 이용해 PVC 요청 시 자동으로 PV 생성
- 클라우드 환경에서 많이 사용됨
apiVersion: storage.k8s.io/v1 # 스토리지 관련 리소스는 storage.k8s.io API 그룹 사용
kind: StorageClass # 동적 볼륨 프로비저닝을 위한 StorageClass 정의
metadata:
name: minio-sc # 이 StorageClass를 식별하는 고유 이름
provisioner: k8s.io/minikube-hostpath # Docker Desktop/Minikube 환경에서 사용하는 프로비저너, 실제 환경에서는 클라우드 프로비저너 사용
reclaimPolicy: Retain # PVC 삭제 시 PV를 유지하도록 설정, 데이터 안전성 확보
volumeBindingMode: Immediate # PVC 생성 즉시 PV를 바인딩하도록 설정, 빠른 볼륨 할당을 위함
✅ 바인딩 (Binding)
사용자가 PVC를 생성하면 쿠버네티스는 적절한 PV를 찾아 바인딩합니다. PVC의 요구사항(크기, 접근 모드 등)과 일치하는 PV를 선택합니다.
apiVersion: v1 # PVC도 핵심 리소스이므로 v1 API 버전 사용
kind: PersistentVolumeClaim # 리소스 종류를 PersistentVolumeClaim으로 지정
metadata:
name: minio-data-claim # 이 PVC를 식별하는 고유 이름, Pod에서 이 이름으로 참조
namespace: minio-system # PVC가 속할 네임스페이스 지정, 같은 네임스페이스의 Pod만 접근 가능
spec:
accessModes:
- ReadWriteOnce # 단일 노드에서 읽기/쓰기 접근 모드 요청, MinIO 단일 인스턴스에 적합
resources:
requests:
storage: 8Gi # 필요한 스토리지 용량 8GiB 요청, MinIO 운영에 충분한 공간
storageClassName: minio-sc # 사용할 StorageClass 지정, 동적 프로비저닝에 사용됨
✅ 사용 (Using)
Pod에서는 볼륨 정의와 volumeMounts를 통해 PVC를 사용합니다.
apiVersion: v1 # Pod 정의에는 핵심 리소스 v1 API 버전 사용
kind: Pod # 리소스 종류를 Pod로 지정
metadata:
name: minio-server # Pod의 고유 이름 지정
namespace: minio-system # Pod가 속할 네임스페이스 지정, PVC와 같은 네임스페이스여야 함
spec:
containers:
- name: minio # 컨테이너 이름 지정, 로그 확인 등에 사용
image: minio/minio:RELEASE.2023-07-21T21-12-44Z # 안정적인 특정 버전의 MinIO 이미지 사용
args:
- server # MinIO를 서버 모드로 실행하는 인자
- /data # MinIO가 데이터를 저장할 경로 지정, 볼륨 마운트 지점과 일치해야 함
ports:
- containerPort: 9000 # MinIO의 API 서비스 포트 노출
- containerPort: 9001 # MinIO 콘솔 UI 포트 노출
volumeMounts:
- name: minio-data # 사용할 볼륨 이름, volumes 섹션에 정의된 이름과 일치해야 함
mountPath: /data # 컨테이너 내부에서 볼륨이 마운트될 경로, MinIO 데이터 저장 위치
volumes:
- name: minio-data # 볼륨 이름 정의, volumeMounts에서 참조됨
persistentVolumeClaim:
claimName: minio-data-claim # 사용할 PVC 이름 지정, 앞서 생성한 PVC와 일치해야 함
✅ 반환 (Reclaiming)
PVC가 삭제되면 PV는 재사용 정책(persistentVolumeReclaimPolicy)에 따라 처리됩니다.
- Retain: PV를 유지하고 수동으로 관리자가 처리해야 함
- Delete: PV와 관련 스토리지 자산을 자동으로 삭제
- Recycle: PV의 데이터를 삭제하고 재사용 가능하게 함 (deprecated)
📌 MinIO에서 PV/PVC 활용하기
✅ MinIO의 데이터 지속성 요구사항
MinIO는 객체 스토리지로 대용량 데이터를 저장하고 관리합니다. 이러한 데이터는 컨테이너가 재시작되더라도 유지되어야 합니다.
▶️ MinIO 데이터 유형:
- 사용자 업로드 파일 (객체)
- 버킷 메타데이터
- 사용자 인증 정보
- 액세스 정책 및 설정
✅ MinIO 단일 인스턴스 PVC 구성
단일 MinIO 인스턴스에서 PVC를 구성하는 방법을 알아봅시다.
- 네임스페이스 생성
apiVersion: v1 # 네임스페이스는 핵심 리소스이므로 v1 API 버전 사용
kind: Namespace # 리소스 종류를 Namespace로 지정
metadata:
name: minio-system # 네임스페이스 이름을 minio-system으로 지정, MinIO 관련 리소스를 그룹화
- PVC 생성
apiVersion: v1 # PVC는 핵심 리소스이므로 v1 API 버전 사용
kind: PersistentVolumeClaim # 리소스 종류를 PersistentVolumeClaim으로 지정
metadata:
name: minio-data-claim # PVC 이름 지정, Pod에서 이 이름으로 참조함
namespace: minio-system # PVC가 생성될 네임스페이스 지정
spec:
accessModes:
- ReadWriteOnce # 단일 노드에서만 마운트 가능한 접근 모드 지정, MinIO 단일 인스턴스에 적합
resources:
requests:
storage: 10Gi # 필요한 스토리지 용량 요청, 실제 사용량에 따라 조정 필요
storageClassName: standard # 사용할 스토리지 클래스 지정, 환경에 따라 다름(Docker Desktop에서는 standard가 기본값)
- MinIO Deployment 생성
apiVersion: apps/v1 # Deployment는 apps API 그룹의 v1 버전 사용
kind: Deployment # 리소스 종류를 Deployment로 지정, Pod 관리 및 복제 기능 제공
metadata:
name: minio # Deployment 이름 지정
namespace: minio-system # 배포될 네임스페이스 지정, PVC와 같은 네임스페이스여야 함
spec:
replicas: 1 # Pod 복제본 수를 1로 설정, PV가 ReadWriteOnce이므로 단일 복제본만 가능
selector:
matchLabels:
app: minio # Pod 선택을 위한 라벨 지정, template 섹션의 라벨과 일치해야 함
template:
metadata:
labels:
app: minio # Pod에 적용될 라벨 지정, selector의 matchLabels와 일치해야 함
spec:
containers:
- name: minio # 컨테이너 이름 지정
image: minio/minio:RELEASE.2023-07-21T21-12-44Z # 특정 버전의 MinIO 이미지 사용, 안정성 확보
args:
- server # MinIO 서버 모드 실행 인자
- /data # 데이터 저장 경로 지정
env:
- name: MINIO_ROOT_USER # MinIO 루트 사용자 환경 변수 설정
value: "minio" # 루트 사용자 이름 지정, 프로덕션에서는 Secret으로 관리해야 함
- name: MINIO_ROOT_PASSWORD # MinIO 루트 비밀번호 환경 변수 설정
value: "minio123" # 루트 비밀번호 지정, 프로덕션에서는 Secret으로 관리해야 함
ports:
- containerPort: 9000 # API 서비스 포트 노출
name: api # 포트 이름 지정, Service에서 참조 가능
- containerPort: 9001 # 콘솔 UI 포트 노출
name: console # 포트 이름 지정
volumeMounts:
- name: data # 볼륨 이름 지정, volumes 섹션의 이름과 일치해야 함
mountPath: /data # 컨테이너 내부 마운트 경로, MinIO 데이터 저장 위치
volumes:
- name: data # 볼륨 이름 정의
persistentVolumeClaim:
claimName: minio-data-claim # 사용할 PVC 이름 지정, 앞서 생성한 PVC와 일치해야 함
- MinIO 서비스 생성
apiVersion: v1 # Service는 핵심 리소스이므로 v1 API 버전 사용
kind: Service # 리소스 종류를 Service로 지정, 네트워크 접근 제공
metadata:
name: minio # 서비스 이름 지정
namespace: minio-system # 서비스가 생성될 네임스페이스 지정
spec:
ports:
- port: 9000 # 서비스에서 노출할 포트 번호
targetPort: api # Pod의 대상 포트 이름(containerPort 참조)
name: api # 서비스 포트 이름
- port: 9001 # 콘솔 UI용 포트 번호
targetPort: console # Pod의 대상 포트 이름
name: console # 서비스 포트 이름
selector:
app: minio # 트래픽을 라우팅할 Pod 선택 라벨, Deployment template 라벨과 일치
type: ClusterIP # 서비스 타입 지정, 클러스터 내부 IP 할당
✅ PVC 상태 확인 및 관리하기
PVC 및 PV 상태를 확인하고 관리하는 명령어를 알아봅시다.
- PVC 목록 확인
# 특정 네임스페이스의 모든 PVC 조회
kubectl get pvc -n minio-system
# 자세한 정보 확인
kubectl describe pvc minio-data-claim -n minio-system
- PV 목록 확인
# 모든 PV 조회
kubectl get pv
# 특정 PV 상세 정보 확인
kubectl describe pv <pv-name>
- 스토리지 용량 확인
# kubectl top 명령어로 스토리지 사용량 확인 (metrics-server 필요)
kubectl top pod -n minio-system
📌 Docker Desktop에서의 PV/PVC 특성
✅ Docker Desktop의 스토리지 제약사항
Docker Desktop은 개발 환경에 최적화된 쿠버네티스 환경을 제공합니다. 그러나 몇 가지 제약사항이 있습니다.
▶️ 주요 제약사항:
- 노드가 하나만 존재 (단일 노드 클러스터)
- PV의 실제 데이터는 VM 내부에 저장됨
- 호스트와 VM 간 볼륨 공유에 제한이 있음
✅ hostPath 기반 PV 사용하기
Docker Desktop에서는 주로 hostPath 타입의 PV를 사용합니다.
apiVersion: v1 # PV는 핵심 리소스이므로 v1 API 버전 사용
kind: PersistentVolume # 리소스 종류를 PersistentVolume으로 지정
metadata:
name: minio-local-pv # PV 이름 지정
spec:
capacity:
storage: 10Gi # 용량 설정, 실제 물리적 제한은 없지만 논리적 크기 지정
accessModes:
- ReadWriteOnce # 단일 노드에서 읽기/쓰기 접근 모드 설정
persistentVolumeReclaimPolicy: Retain # PVC 삭제 시 PV 유지 정책 설정
hostPath: # Docker Desktop에서 사용 가능한 hostPath 볼륨 타입 지정
path: /tmp/minio-data # VM 내부의 경로 지정, 컨테이너 재시작 시에도 데이터 유지
type: DirectoryOrCreate # 디렉토리가 없으면 생성하도록 설정
✅ Docker Desktop에서 데이터 백업하기
Docker Desktop에서 PV 데이터를 백업하는 방법을 알아봅시다.
- 데이터 백업 Job 생성
apiVersion: batch/v1 # Job은 batch API 그룹의 v1 버전 사용
kind: Job # 일회성 작업을 위한 Job 리소스 정의
metadata:
name: minio-backup # Job 이름 지정
namespace: minio-system # Job이 실행될 네임스페이스 지정
spec:
template:
spec:
containers:
- name: backup # 컨테이너 이름 지정
image: alpine:3.14 # 가벼운 Alpine Linux 이미지 사용, 백업 작업에 적합
command: ["sh", "-c"] # 실행할 명령어 지정
args:
- |
# 백업 대상 디렉토리 생성
mkdir -p /backup
# MinIO 데이터를 tar로 압축하여 백업
tar -czvf /backup/minio-data-$(date +%Y%m%d).tar.gz /data
# 백업 완료 메시지
echo "Backup completed at $(date)"
volumeMounts:
- name: minio-data # MinIO 데이터 볼륨 마운트
mountPath: /data # 마운트 경로 지정, 원본 데이터 접근
readOnly: true # 읽기 전용으로 마운트하여 데이터 보호
- name: backup-volume # 백업 저장 볼륨 마운트
mountPath: /backup # 백업 파일 저장 경로
restartPolicy: Never # Job 실패 시 재시작하지 않도록 설정
volumes:
- name: minio-data # MinIO 데이터 볼륨 정의
persistentVolumeClaim:
claimName: minio-data-claim # 백업할 데이터가 있는 PVC 이름
- name: backup-volume # 백업 저장 볼륨 정의
hostPath:
path: /tmp/minio-backups # 백업 파일이 저장될 호스트 경로
type: DirectoryOrCreate # 디렉토리가 없으면 생성
📌 PV/PVC 문제 해결 가이드
✅ 일반적인 문제와 해결 방법
- PVC가 Pending 상태에 머무는 경우
- 원인: 적절한 PV가 없거나 스토리지 클래스가 잘못 지정됨
- 해결: kubectl describe pvc <pvc-name> 명령으로 이벤트 확인
- 볼륨 마운트 오류
- 원인: 접근 권한 문제나 경로 오류
- 해결: kubectl describe pod <pod-name> 명령으로 이벤트 확인
- PV가 해제(Released)되지 않는 경우
- 원인: persistentVolumeReclaimPolicy가 Retain으로 설정됨
- 해결: PV를 수동으로 삭제하거나 재사용 정책 변경
✅ MinIO 특화 문제 해결
- MinIO 시작 시 권한 오류
- 원인: 볼륨 마운트 디렉토리 권한 문제
- 해결: 적절한 securityContext 설정
spec:
containers:
- name: minio
securityContext: # 컨테이너 보안 컨텍스트 설정
runAsUser: 1000 # UID 1000으로 실행하여 적절한 권한 부여
runAsGroup: 1000 # GID 1000으로 실행
fsGroup: 1000 # 볼륨에 대한 그룹 ID 설정
- 데이터 불일치 문제
- 원인: 여러 인스턴스가 동일한 PV에 접근 시도
- 해결: accessMode를 ReadWriteOnce로 설정하고 replicas를 1로 유지
📌 Summary
- PV와 PVC는 쿠버네티스에서 데이터 영속성을 제공하는 핵심 메커니즘
- 정적/동적 프로비저닝을 통해 스토리지 리소스 할당 가능
- MinIO에서는 PVC를 통해 객체 스토리지 데이터 보존 가능
- Docker Desktop에서는 hostPath 타입 PV를 사용하여 개발 환경 구성
- 적절한 접근 모드와 재사용 정책 설정으로 데이터 안전성 확보