ephemeral 볼륨과 데이터 관리 패턴 — emptyDir부터 projected 볼륨까지
모든 데이터가 영구 저장이 필요한 것은 아닌데, 캐시 데이터나 임시 파일은 어떻게 관리하는 것이 효율적일까요?
PersistentVolume은 영구 데이터에 적합하지만, 캐시, 임시 파일, 빌드 아티팩트처럼 Pod 수명과 함께 사라져도 되는 데이터에는 과합니다. Kubernetes는 이런 용도를 위해 emptyDir, hostPath, projected 같은 다양한 볼륨 타입을 제공합니다.
emptyDir — 가장 많이 쓰는 임시 볼륨
emptyDir은 Pod이 생성될 때 빈 디렉토리로 시작하고, Pod이 삭제될 때 함께 사라지는 볼륨입니다.
apiVersion: v1
kind: Pod
metadata:
name: shared-data
spec:
containers:
- name: writer
image: busybox:1.36
command: ['sh', '-c', 'while true; do date >> /data/log.txt; sleep 5; done']
volumeMounts:
- name: shared
mountPath: /data
- name: reader
image: busybox:1.36
command: ['sh', '-c', 'tail -f /data/log.txt']
volumeMounts:
- name: shared
mountPath: /data
volumes:
- name: shared
emptyDir: {}
emptyDir의 핵심 특성
- Pod 내 여러 컨테이너가 공유 가능합니다
- 컨테이너 재시작에도 유지됩니다 (Pod이 삭제될 때만 삭제)
- 기본적으로 노드의 디스크를 사용합니다
대표적인 사용 사례
| 사용 사례 | 설명 |
|---|---|
| 사이드카와 데이터 공유 | 앱이 로그를 쓰고 사이드카가 수집 |
| 캐시 | 이미지 처리, 빌드 아티팩트 |
| 스크래치 공간 | 대용량 정렬, 임시 파일 |
| 체크포인트 | 장기 연산의 중간 결과 저장 |
memory 미디엄 — RAM 기반 tmpfs
volumes:
- name: cache
emptyDir:
medium: Memory # tmpfs 사용
sizeLimit: 256Mi # 크기 제한
RAM에 저장하므로 매우 빠르지만, 주의할 점이 있습니다.
- Pod의 메모리 리소스에 포함됩니다 — limits을 초과하면 OOMKill
- 노드 재시작 시 데이터 소실
- sizeLimit을 반드시 설정하세요
sizeLimit
volumes:
- name: temp
emptyDir:
sizeLimit: 1Gi # 1Gi 초과 시 Pod이 evict될 수 있음
sizeLimit을 초과하면 kubelet이 Pod을 추방(evict)할 수 있습니다.
hostPath — 호스트 파일시스템 접근
hostPath는 노드의 파일시스템을 Pod에 직접 마운트합니다.
volumes:
- name: docker-socket
hostPath:
path: /var/run/docker.sock
type: Socket
hostPath type 옵션
| type | 설명 |
|---|---|
DirectoryOrCreate | 디렉토리가 없으면 생성 |
Directory | 디렉토리가 존재해야 함 |
FileOrCreate | 파일이 없으면 생성 |
File | 파일이 존재해야 함 |
Socket | Unix 소켓이 존재해야 함 |
"" (빈 문자열) | 체크 없음 (기본값) |
hostPath의 적합한 사용 사례
- DaemonSet에서 노드 로그 수집 (
/var/log) - 노드 모니터링 에이전트 (
/proc,/sys) - Docker Socket 접근 (CI/CD 파이프라인)
hostPath의 위험
hostPath는 보안 위험이 크므로 일반 워크로드에는 사용하지 마세요.
- 컨테이너가 호스트 파일시스템에 직접 접근 가능
- Pod이 다른 노드로 이동하면 데이터가 다름
- PodSecurity에서 Baseline/Restricted 프로파일은 hostPath를 제한
projected 볼륨 — 여러 소스를 하나로 결합
projected 볼륨은 ConfigMap, Secret, downwardAPI, serviceAccountToken을 하나의 디렉토리에 합칩니다.
volumes:
- name: all-in-one
projected:
sources:
- configMap:
name: app-config
items:
- key: config.yml
path: config.yml
- secret:
name: app-secret
items:
- key: api-key
path: credentials/api-key
- downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
- path: cpu-request
resourceFieldRef:
containerName: app
resource: requests.cpu
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: vault
마운트된 디렉토리 구조는 다음과 같습니다.
/etc/projected/
├── config.yml # ConfigMap
├── credentials/
│ └── api-key # Secret
├── labels # downwardAPI
├── cpu-request # downwardAPI
└── token # ServiceAccountToken
downwardAPI — Pod 메타데이터 노출
Pod 자신의 정보를 컨테이너 내부에서 참조할 수 있습니다.
volumes:
- name: podinfo
downwardAPI:
items:
- path: "name"
fieldRef:
fieldPath: metadata.name
- path: "namespace"
fieldRef:
fieldPath: metadata.namespace
- path: "node"
fieldRef:
fieldPath: spec.nodeName
- path: "memory-limit"
resourceFieldRef:
containerName: app
resource: limits.memory
VolumeSnapshot — 볼륨 스냅샷
PV의 특정 시점 스냅샷을 생성하고 복원할 수 있습니다. CSI 드라이버가 스냅샷을 지원해야 합니다.
# 스냅샷 클래스 정의
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: ebs-snapshot
driver: ebs.csi.aws.com
deletionPolicy: Retain
---
# 스냅샷 생성
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: db-snapshot-20260319
spec:
volumeSnapshotClassName: ebs-snapshot
source:
persistentVolumeClaimName: db-data # 원본 PVC
스냅샷에서 PVC 복원
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-data-restored
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
dataSource:
name: db-snapshot-20260319
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
ephemeral 볼륨 (Generic Ephemeral Volumes)
PVC처럼 StorageClass를 사용하지만 Pod 수명에 맞춰 자동으로 생성/삭제되는 볼륨입니다.
apiVersion: v1
kind: Pod
metadata:
name: scratch-pod
spec:
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: scratch
mountPath: /scratch
volumes:
- name: scratch
ephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
emptyDir과 달리 실제 블록 스토리지를 사용하므로, 대용량 임시 데이터에 적합합니다.
볼륨 타입 선택 가이드
| 용도 | 볼륨 타입 | 수명 |
|---|---|---|
| 컨테이너 간 데이터 공유 | emptyDir | Pod |
| 고속 캐시 | emptyDir (memory) | Pod |
| 노드 데이터 접근 | hostPath | 노드 |
| 설정/시크릿/메타데이터 결합 | projected | 실시간 업데이트 |
| 대용량 임시 데이터 | Generic Ephemeral | Pod |
| 영구 데이터 | PVC | PV 정책에 따름 |
| 백업/복원 | VolumeSnapshot | 명시적 삭제까지 |
실무에서 주의할 점
- emptyDir의 sizeLimit을 설정하세요: 제한 없으면 노드 디스크를 가득 채울 수 있습니다
- hostPath는 DaemonSet에서만: 일반 워크로드에서는 보안 위험이 큽니다
- Memory 미디엄은 리소스 제한에 포함: OOMKill 원인이 될 수 있습니다
- VolumeSnapshot을 정기적으로: CronJob과 조합하면 자동 백업이 가능합니다
정리
모든 데이터가 PV가 필요한 것은 아닙니다. emptyDir로 컨테이너 간 공유와 캐싱을, projected로 여러 설정 소스를 결합하고, VolumeSnapshot으로 데이터를 백업합니다. 각 볼륨 타입의 수명과 특성을 이해하고 용도에 맞게 선택하는 것이 핵심입니다.