컨테이너가 죽어도 데이터는 살아남아야 한다
Pod 안의 파일시스템은 기본적으로 그 Pod과 운명을 같이합니다. 컨테이너가 재시작되기만 해도 그 안에 쓴 파일은 사라집니다. 웹 서버나 API 서버처럼 상태를 갖지 않는(stateless) 애플리케이션이라면 이게 문제가 안 됩니다 — 오히려 매번 깨끗한 상태로 시작하는 게 정상입니다. 문제는 데이터베이스, 파일 업로드를 받는 서비스처럼 "다시 떴을 때도 이전 데이터를 그대로 봐야 하는" 워크로드입니다.
emptyDir — Pod 생존 기간만큼만 사는 임시 저장소
4장에서 사이드카 예제에 이미 등장했던 emptyDir은 같은 Pod 안의 컨테이너끼리 파일을 주고받는 용도로는 유용하지만, Pod이 죽으면 함께 사라집니다. 데이터를 진짜로 보존하려면 Pod의 생명주기와 분리된 저장소가 필요합니다.
PersistentVolume과 PersistentVolumeClaim — 저장소를 신청서로 다루기
쿠버네티스는 저장소 문제를 두 개의 리소스로 나눠서 다룹니다. **PersistentVolume(PV)**은 실제로 존재하는 저장 공간(클라우드 디스크, NFS 등)을 클러스터에 등록해둔 것이고, **PersistentVolumeClaim(PVC)**은 애플리케이션이 "이런 크기의 저장 공간이 필요하다"고 신청하는 요청서입니다. Pod은 PV를 직접 쓰지 않고, 항상 PVC를 통해 씁니다.
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-api-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi# deployment.yaml (일부)
volumeMounts:
- name: data
mountPath: /var/lib/data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-api-data이 구조가 처음엔 번거로워 보이지만, 이렇게 나눠 놓은 이유가 있습니다. 애플리케이션을 배포하는 사람은 "5Gi가 필요하다"는 것만 알면 되고, 그 5Gi가 실제로 어느 클라우드의 어떤 디스크 종류로 채워질지는 클러스터를 운영하는 사람(또는 클라우드 제공자)의 책임으로 분리됩니다. 이 분리를 실제로 이어주는 게 StorageClass입니다 — PVC에 storageClassName을 지정하면, 클러스터가 그에 맞는 PV를 자동으로 만들어줍니다(dynamic provisioning). 관리형 클러스터에는 대부분 기본 StorageClass가 이미 설정되어 있어서, 실무에서는 PV를 손으로 만드는 일이 거의 없습니다.
ReadWriteOnce의 함정
accessModes: ReadWriteOnce는 이 볼륨을 한 번에 노드 하나만 마운트할 수 있다는 뜻입니다. "여러 개"가 아니라 "하나"입니다. Deployment의 복제본을 3개로 늘렸는데 이 볼륨을 셋이 공유하려 하면, 두 번째, 세 번째 Pod은 볼륨을 마운트하지 못해 Pending 상태에 멈춰 있게 됩니다. 실제로 겪어보면 "Deployment의 replicas를 늘렸는데 Pod이 하나만 뜬다"는 증상으로 나타나서 원인을 찾기까지 시간이 걸리는 경우가 많습니다.
kubectl get pods
# my-api-xxx 1/1 Running
# my-api-yyy 0/1 Pending <- 여기서 막힌다
kubectl describe pod my-api-yyy
# Warning FailedAttachVolume ... Multi-Attach error for volume여러 Pod이 같은 볼륨을 동시에 쓰게 하려면 ReadWriteMany를 지원하는 스토리지(NFS 등)가 필요한데, 클라우드의 기본 블록 스토리지는 대부분 이걸 지원하지 않습니다.
진짜 상태를 가진 워크로드라면 Deployment가 아니다
여기까지 읽었다면 눈치챘겠지만, PVC를 Deployment에 붙이는 방식은 애초에 "복제본이 여러 개이고 서로 대체 가능하다"는 Deployment의 전제와 잘 안 맞습니다. 데이터베이스처럼 각 인스턴스가 자기만의 고유한 저장 공간과 고유한 정체성(어느 게 primary고 어느 게 replica인지)을 가져야 하는 워크로드에는 StatefulSet이라는 별도의 리소스가 있습니다. StatefulSet은 각 복제본에 안정적인 이름(db-0, db-1, db-2)과 각자의 PVC를 붙여줍니다. 다만 실무에서는 데이터베이스를 클러스터 안에 StatefulSet으로 직접 운영하는 것보다, 클라우드의 관리형 데이터베이스(RDS 등)를 클러스터 밖에 두고 쓰는 경우가 여전히 더 흔합니다 — 데이터베이스 운영 자체가 별도의 전문 영역이기 때문입니다. StatefulSet은 이 책에서 깊이 다루지는 않지만, "Deployment로는 안 되는 상황이 있다"는 것과 그 상황을 위한 리소스가 따로 존재한다는 것만은 기억해두면 좋습니다.