소개
컨테이너는 쓰기 가능한 파일 시스템을 가지고 있지만, 해당 스토리지는 컨테이너에 종속되어 있습니다. Kubernetes 볼륨은 Pod 가 파일을 더 효율적으로 관리할 수 있는 방법을 제공합니다. 일부 볼륨은 ephemeral(일시적) 하며 단일 Pod 내의 임시 데이터를 위해 설계되었습니다. 반면, persistent(영구적) 볼륨은 Pod 가 삭제되고 재생성되어도 데이터를 유지할 수 있습니다.
이번 실습에서는 Pod 로컬 임시 데이터를 위해 emptyDir 볼륨을 사용해 보고, PersistentVolumeClaim 을 생성하여 두 개의 서로 다른 Pod 에 마운트함으로써 Pod 가 교체되어도 데이터가 유지됨을 확인합니다.
볼륨 실습을 위한 네임스페이스 준비
이 단계에서는 실습을 위한 깨끗한 네임스페이스를 준비하고, 클러스터에 필요한 스토리지 구성 요소가 준비되었는지 확인합니다.
/home/labex/project 디렉토리에서 시작합니다. 이 디렉토리에 Pod 및 PVC 매니페스트를 작성하게 됩니다.
mkdir -p /home/labex/project
cd /home/labex/project
Kubernetes API 서버에 접근 가능한지 확인합니다. VM 이 막 시작되어 노드가 아직 준비되지 않았다면, 잠시 기다린 후 명령어를 다시 실행하세요.
kubectl get nodes --request-timeout=5s
예상 출력:
NAME STATUS ROLES AGE VERSION
labex-v135 Ready control-plane ... v1.35...
클러스터 네트워킹 Pod 가 준비 (Ready) 상태가 될 때까지 기다립니다:
kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s
예상 출력:
pod/calico-node-... condition met
이 실습에서는 나중에 PersistentVolumeClaim 을 사용합니다. 기본 standard StorageClass 가 존재하는지 확인한 후, minikube 스토리지 프로비저너 Pod 가 준비될 때까지 기다립니다.
kubectl get storageclass standard
kubectl wait --namespace kube-system --for=condition=Ready pod/storage-provisioner --timeout=120s
예상 출력:
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ...
standard (default) k8s.io/minikube-hostpath Delete Immediate ...
pod/storage-provisioner condition met
ckad-prep-volumes라는 이름의 네임스페이스를 생성합니다:
kubectl create namespace ckad-prep-volumes
이 네임스페이스를 현재 컨텍스트 네임스페이스로 설정합니다:
kubectl config set-context --current --namespace=ckad-prep-volumes
현재 네임스페이스를 확인합니다:
kubectl config view --minify --output 'jsonpath={..namespace}'; echo
예상 출력:
ckad-prep-volumes
Pod 로컬 임시 데이터를 위한 emptyDir 사용
이 단계에서는 emptyDir 볼륨을 사용하는 Pod 를 생성합니다.
emptyDir 볼륨은 Pod 가 생성될 때 비어 있는 상태로 시작됩니다. 동일한 Pod 내의 컨테이너들은 이를 공유 스크래치 공간, 캐시 공간 또는 임시 작업 디렉토리로 사용할 수 있습니다. 이는 Pod 가 제거된 후 데이터를 보존하기 위한 용도가 아닙니다.
emptydir-pod.yaml 파일을 생성합니다:
cat > emptydir-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: scratch-worker
labels:
app: scratch-worker
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: scratch-space
emptyDir: {}
containers:
- name: worker
image: busybox:1.36
command:
- /bin/sh
- -c
- 'echo scratch-ready > /scratch/status.txt; sleep 3600'
volumeMounts:
- name: scratch-space
mountPath: /scratch
EOF
매니페스트를 적용합니다:
kubectl apply -f emptydir-pod.yaml
예상 출력:
pod/scratch-worker created
Pod 가 준비될 때까지 기다립니다:
kubectl wait --for=condition=Ready pod/scratch-worker --timeout=120s
예상 출력:
pod/scratch-worker condition met
마운트된 emptyDir 볼륨에서 파일을 읽어옵니다:
kubectl exec scratch-worker -c worker -- cat /scratch/status.txt
예상 출력:
scratch-ready
Pod 의 볼륨 타입을 확인합니다. jsonpath 표현식은 볼륨 이름을 출력하고 이것이 emptyDir임을 확인해 줍니다.
kubectl get pod scratch-worker -o 'jsonpath={.spec.volumes[0].name}{" emptyDir\n"}'
예상 출력:
scratch-space emptyDir
PersistentVolumeClaim 에 데이터 쓰기
이 단계에서는 PersistentVolumeClaim(PVC) 을 생성하고 이를 작성자 (writer) Pod 에 마운트합니다.
PersistentVolumeClaim(PVC) 은 애플리케이션의 스토리지 요청입니다. 이 실습에서 standard StorageClass 는 해당 요청에 대해 동적으로 스토리지를 제공합니다. Pod 는 이 클레임을 /data 경로에 마운트하고 파일을 작성합니다.
notes-pvc.yaml 파일을 생성합니다:
cat > notes-pvc.yaml <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: notes-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 1Mi
EOF
PVC 를 적용합니다:
kubectl apply -f notes-pvc.yaml
예상 출력:
persistentvolumeclaim/notes-pvc created
PVC 가 Bound 상태가 될 때까지 기다립니다:
kubectl wait --for=jsonpath='{.status.phase}'=Bound pvc/notes-pvc --timeout=120s
예상 출력:
persistentvolumeclaim/notes-pvc condition met
PVC 를 마운트하는 작성자 Pod 를 생성합니다:
cat > pvc-writer-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pvc-writer
labels:
app: pvc-writer
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: notes-storage
persistentVolumeClaim:
claimName: notes-pvc
containers:
- name: writer
image: busybox:1.36
command:
- /bin/sh
- -c
- 'echo persistent-note > /data/note.txt; sleep 3600'
volumeMounts:
- name: notes-storage
mountPath: /data
EOF
작성자 Pod 를 적용하고 준비될 때까지 기다립니다:
kubectl apply -f pvc-writer-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-writer --timeout=120s
예상 출력:
pod/pvc-writer created
pod/pvc-writer condition met
PVC 마운트에서 파일을 읽어옵니다:
kubectl exec pvc-writer -c writer -- cat /data/note.txt
예상 출력:
persistent-note
새 Pod 에서 PVC 재사용하기
이 단계에서는 작성자 Pod 를 삭제하고, 동일한 PVC 를 마운트하는 새로운 읽기 (reader) Pod 를 생성하여 파일이 그대로 남아 있는지 확인합니다.
이것이 Pod 로컬 임시 데이터와 영구 스토리지의 실질적인 차이점입니다. 작성자 Pod 는 제거될 수 있지만, PVC 는 그대로 유지됩니다. 새로운 Pod 는 동일한 클레임을 마운트하여 데이터를 읽을 수 있습니다.
작성자 Pod 를 삭제합니다:
kubectl delete pod pvc-writer
예상 출력:
pod "pvc-writer" deleted
동일한 PVC 를 마운트하는 읽기 Pod 를 생성합니다:
cat > pvc-reader-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pvc-reader
labels:
app: pvc-reader
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: notes-storage
persistentVolumeClaim:
claimName: notes-pvc
containers:
- name: reader
image: busybox:1.36
command:
- /bin/sh
- -c
- 'cat /data/note.txt; sleep 3600'
volumeMounts:
- name: notes-storage
mountPath: /data
EOF
읽기 Pod 를 적용하고 준비될 때까지 기다립니다:
kubectl apply -f pvc-reader-pod.yaml
kubectl wait --for=condition=Ready pod/pvc-reader --timeout=120s
예상 출력:
pod/pvc-reader created
pod/pvc-reader condition met
새로운 Pod 에서 파일을 읽어옵니다:
kubectl exec pvc-reader -c reader -- cat /data/note.txt
예상 출력:
persistent-note
PVC 를 확인합니다. 원래의 작성자 Pod 가 사라졌음에도 불구하고 여전히 Bound 상태여야 합니다.
kubectl get pvc notes-pvc
예상 출력:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
notes-pvc Bound ... 1Mi RWO standard <unset> ...
요약
Pod 로컬 임시 데이터를 위해 emptyDir 볼륨을 사용하고, 영구 스토리지를 위해 PVC 를 생성했습니다. 한 Pod 를 통해 데이터를 작성하고 해당 Pod 를 삭제한 뒤, 동일한 클레임을 마운트한 새로운 Pod 에서 데이터를 읽어보았습니다. 이제 임시 Pod 데이터를 위해 언제 ephemeral 스토리지를 사용해야 하는지, 그리고 Pod 교체 후에도 유지되어야 하는 데이터를 위해 언제 영구 스토리지를 요청해야 하는지 이해하게 되었습니다.


