HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📖
공부한 책
/쿠버네티스 교과서/
5장 볼륨, 마운트, 클레임을 이용한 데이터 퍼시스턴시

5장 볼륨, 마운트, 클레임을 이용한 데이터 퍼시스턴시

쿠버네티스에서 컨테이너 파일 시스템이 구축되는 과정EmptyDir(공디렉터리)볼륨과 마운트로 노드에 데이터 저장하기 (호스트경로 HostPath)노드의 파일 시스템을 최소한으로 노출전체에서 접근 가능하도록 데이터 저장하기 : 영구볼륨과 클레임로컬 볼륨 정의 후, 노드와 연결, PVC 정의스토리지의 유형과 동적 볼륨 프로비저닝스토리지 유형스토리지를 선택할 때 고려할 점연습문제

쿠버네티스에서 컨테이너 파일 시스템이 구축되는 과정

파드 속 컨테이너의 파일 시스템은 여러 가지의 출처를 합쳐 구성됨
먼저 컨테이너 이미지가 파일 시스템의 초기 내용을 제공하고, 이 위에 컨테이너가 기록 가능한 레이어(writable layer)가 얹혀진다. 이미지에 들어 있던 파일을 수정한다거나 새로운 파일을 기록하는 작업이 바로 이 레이어에서 일어난다. ( 도커 이미지는 읽기 전용이기에 이미지에 들어 있던 파일을 수정하는 것은 이 기록 가능 레이어에서 해당 파일의 사본을 수정하는 것)
notion image
notion image
  • 컨테이너의 파일 시스템은 컨피그맵이나 비밀값 등을 특정 경로에 마운트하는 방식으로 확장할 수 있다.
  • 볼륨도 컨테이너 파일 시스템을 구성하는 수단 중 하나로, 볼륨은 파드 수준에서 정의되며 읽기 전용 또는 쓰기 가능으로 설정될 수 있다.

EmptyDir(공디렉터리)

  • 컨테이너 안에서 빈 디렉터리로 초기화되는 유형의 볼륨인 공디렉터리. 폴더 같지만 파드 수준의 스토리지임(이미지나 컨테이너 레이어에 속하지 않음)
  • 저장된 데이터는 컨테이너가 재시작되더라도 유지됨
  • 파드가 종료되었다가 다시 시작하면 데이터가 날아감
apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}

볼륨과 마운트로 노드에 데이터 저장하기 (호스트경로 HostPath)

💡
호스트경로 볼륨은 유상태 애플리케이션을 쿠버네티스로 처음 도입할 때 유리함
사용하기 쉽고, 모든 클러스터에서 동일하게 동작하는 것이 정상
그러나 상태가 임시 저장될 수 있기에 영구적으로 저장이 필요하면 클러스터 내 임의의 노드에서 접근할 수 있는 유형의 볼륨을 사용해야 함
이 상황에서는 데이터를 특정 노드에 고정시킬지 말지를 결정해야 하기 때문에 데이터를 다루기가 좀 더 까다로워짐
데이터가 특정 노드에 고정된다는 것은 대체 파드가 이전 파드와 동일한 노드에만 배치되도록 해야 한다는 의미임(반대로 데이터를 특정 노드에 고정시키지 않는다면 어떤 노드에도 파드를 배치할 수 있다)
  • 호스트경로 볼륨 역시 파드에 정의되며 컨테이너 파일 시스템에 마운트되는 형태로 쓰이고 실제 데이터는 노드의 디스크에 기록됨 → 파드가 교체되더라도 대체 파드가 동일한 노드에 배치된다면 데이터를 그대로 사용할 수 있음
apiVersion: apps/v1 kind: Deployment metadata: name: pi-proxy labels: app: pi-proxy spec: selector: matchLabels: app: pi-proxy template: metadata: labels: app: pi-proxy spec: containers: - image: nginx:1.17-alpine name: nginx ports: - containerPort: 80 name: http volumeMounts: - name: config mountPath: "/etc/nginx/" readOnly: true - name: cache-volume mountPath: /data/nginx/cache # 프록시의 캐시 저장 경로 volumes: - name: config configMap: name: pi-proxy-configmap - name: cache-volume hostPath: # 노드의 디렉터리를 사용함 path: /volumes/nginx/cache # 사용할 노드의 디렉터리 type: DirectoryOrCreate # 디렉터리가 없으면 생성
  • 노드가 하나뿐일 때는 이 데이터가 계속 저장되어 있음. 새로 생성된 대체 파드는 시작할 때 호스트경로 볼륨을 읽어들임
  • 호스트경로 볼륨의 가장 큰 문제는 노드가 두 개 이상인 클러스터에서 생김
  • 또한 호스트경로 볼륨을 잘못 사용하면 노드의 파일 시스템 전체가 노출되는 문제가 생길 수 있음

노드의 파일 시스템을 최소한으로 노출

apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: containers: - name: sleep image: kiamol/ch03-sleep volumeMounts: - name: node-root mountPath: /pod-logs subPath: var/log/pods - name: node-root mountPath: /container-logs subPath: var/log/containers volumes: - name: node-root hostPath: path: / type: Directory
  • subPath를 활용함으로써 volumes의 모든 경로에 대해 다 접근하는 것이 아닌 그 안에서 특정 하위 디렉터리만 대상할 수 있음

전체에서 접근 가능하도록 데이터 저장하기 : 영구볼륨과 클레임

💡
Pod → PVC → PV (PV는 로컬 스토리지가 될수도, 리모트 스토리지가 될수도 있음)
PV 에는 스토리지 시스템에 대한 볼륨 정의가 들어가 있음
PVC에 필요한 요구사항을 기술하면 그에 맞는 PV가 할당됨
  • 쿠버네티스 클러스터는 리소스가 가득 담긴 수영장과 같다. 여러 개의 노드가 있고 각각의 노드에는 클러스터에 cpu 및 메모리용량을 제공함. 그리고 이 CPU 및 메모리 용량을 사용하여 쿠버네티스에서 애플리케이션을 실행함
  • 파드는 일반적인 volumes, volumeMounts 형태로 분산 스토리지를 사용함
  • 모든 노드가 같은 분산 스토리지에 연결되고, 분산 스토리지로는 온프레미스(NFS, 글러스터FS(GlusterFS)), AKS 클러스터( 애저 파일스), EKS 클러스터 (Elastic Block Store)등을 사용할 수 있다. 따라서 파드는 어떤 노드에 실행되더라도 볼륨에 접근할 수 있다.
    • 이들 시스템은 필수 설정이 각각 달라서 이 설정을 파드 정의에 기술할 수도 있다. 그러나 이러면 특정 스토리지 솔루션에 대한 의존도가 높아지기에 쿠버네티스에서는 스토리지 솔루션과의 결합을 느슨하게 유지하는 유연성 있는 수단을 제공함
  • Pod는 컴퓨팅 계층의 추상, Service는 네트워크 계층의 추상, 스토리지 계층의 추상 → PV(Persistence Volume), PVC(Persistence VolumeClaim)
    • 영구 볼륨(PV)은 사용 가능한 스토리지의 조각을 정의한 쿠버네티스 리소스이고, 각각의 영구볼륨에는 이를 구현하는 스토리지 시스템에 대한 볼륨 정의가 들어가 있다.
apiVersion: v1 kind: PersistentVolume metadata: name: pv01 spec: capacity: storage: 50Mi # 볼륨 용량 accessModes: # 파드의 접근 유형 - ReadWriteOnce # 파드 하나에서만 사용 가능 nfs: # NFS 스토리지를 사용하는 볼륨 server: nfs.my.network # NFS 서버의 도메인 네임 path: "/kubernetes-volumes" # 스토리지 경로
NFS 스토리지를 사용하는 볼륨의 정의

로컬 볼륨 정의 후, 노드와 연결, PVC 정의

kubectl label node $(kubectl get nodes -o jsonpath='{.items[0].metadata.name}' ) kiamol=ch05 kubectl get nodes -l kiamol=ch05
node에 label 추가하기
apiVersion: v1 kind: PersistentVolume metadata: name: pv01 spec: capacity: storage: 50Mi accessModes: - ReadWriteOnce local: path: /volumes/pv01 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kiamol operator: In values: - ch05
로컬 볼륨을 사용하는 영구볼륨 배치. 운영용 클러스터라면 영구볼륨의 정의에 공유 스토리지가 쓰임
  • 이렇게 영구볼륨을 정의했지만 파드가 영구볼륨을 직접 사용하지는 못하고 영구볼륨클레임 이란 것을 사용하여 볼륨 사용을 요청해야 함
    • 영구볼륨클레임(PVC)은 파드가 사용하는 스토리지의 추상임
    • 애플리케이션에서 사용할 스토리지를 요청하는 역할을 함
    • 쿠버네티스에서 PVC는 요구 조건이 일치하는 PV와 함께 사용됨. 다만 상세한 볼륨 정보는 PV에 맡김
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc # 애플리케이션은 PVC를 통해 PV를 사용함 spec: accessModes: # 접근 유형은 필수 설정 - ReadWriteOnce resources: requests: storage: 40Mi # 요청하는 스토리지 용량 storageClassName: "" # 스토리지 유형을 지정하지 않음
  • PVC에 스토리지 유형을 지정하지 않으면 쿠버네티스가 현존하는 PV 중 요구 사항과 일치하는 것을 찾아 줌. PV와 PVC의 관계는 일대일이며, PVC에 연결된 PV는 다른 PVC와 추가로 연결될 수 없음
    • # PVC 배치하기 전 PV kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pv01 50Mi RWO Retain Available <unset> 82s # PVC(postgres-pvc) 배치하고 난 후 PV kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pv01 50Mi RWO Retain Bound default/postgres-pvc <unset> 9m44s
  • 이러한 방식은 PV를 명시적으로 생성해야 하는 정적 프로비저닝 방식으로, 요구사항이 일치하는 PV가 없을 때 PVC를 배치하면 PVC가 생성되기는 하지만 스토리지는 사용할 수 없다. 사용불가능한 PVC 를 사용하려는 파드는 보류 상태로 남아있게 된다.
    • kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE postgres-pvc Bound pv01 50Mi RWO <unset> 3m22s postgres-pvc-toobig Pending <unset> 4s
      apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc-toobig spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi storageClassName: ""
  • PVC를 사용하는 파드 정의
    • apiVersion: v1 kind: Service metadata: name: todo-db spec: ports: - port: 5432 targetPort: 5432 selector: app: todo-db type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: todo-db spec: selector: matchLabels: app: todo-db template: metadata: labels: app: todo-db spec: containers: - name: db image: postgres:11.6-alpine env: - name: POSTGRES_PASSWORD_FILE value: /secrets/postgres_password volumeMounts: - name: secret mountPath: "/secrets" - name: data mountPath: /var/lib/postgresql/data volumes: - name: secret secret: secretName: todo-db-secret defaultMode: 0400 items: - key: POSTGRES_PASSWORD path: postgres_password - name: data persistentVolumeClaim: claimName: postgres-pvc
    • 애플리케이션 설계자는 파드와 PVC의 정의만 담당하면 되고, PV는 신경쓰지 않아도 됨
    • 그러나 실습에서는 노드에서 볼륨이 사용할 디렉터리 경로 만들어 주어야 함
      • 실제 클러스터에서는 노드에 로그인할 권한이 없는 경우가 있기에, 호스트경로 마운트로 노드의 루트 디렉터리를 마운트한 파드를 이용하여 노드의 파일 시스템에 디렉터리 생성

스토리지의 유형과 동적 볼륨 프로비저닝

  • 지금까지의 방식은 명시적으로 PV와 pVC를 생성해서 연결하는 정적 볼륨 프로비저닝 방식
  • 정적 볼륨 프로비저닝 방식은 모든 쿠버네티스 클러스터에서 사용할 수 있다는 점이 장점, 스토리지 접근 제약이 큰 조직에서 선호함
  • 하지만 대부분의 쿠버네티스 플랫폼에서는 동적 볼륨 프로비저닝이라는 더 간단한 방식을 제공
    • 동적 볼륨 프로비저닝 은 PVC만 생성하면 그에 맞는 PV 를 클러스터에서 동적으로 생성해 주는 방식임
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc-dynamic spec: accessModes: - ReadWriteOnce resources: requests: storage: 100M스토리지 유형 # storageClassName 필드가 없으면 기본 유형이 사용됨
  • 쿠버네티스 플랫폼이 동적 볼륨 프로비저닝을 지원하고 기본 스토리지 유형이 지정되어 있다면, 이 스토리지 유형이 쓰인 영구볼륨이 생성되어 앞서 배치한 영구볼륨 클레임에 연결될 것
    • 도커 데스크텁의 기본 스토리지 유형은 호스트경로 볼륨

스토리지 유형

스토리지 유형이 어떻게 동작할지를 결정하는 세가지 필드
  • provisioner : 영구볼륨이 필요해질 때, PV를 만드는 주체. 플랫폼에 따라 관리 주체가 달라짐. 예로 AKS 는 함께 통합된 애저 파일스가 스토리지를 만듦
  • reclaimPolicy : 연결되었던 클레임이 삭제되었을 때 남아 있는 볼륨을 어떻게 처리할지 지정한다. 볼륨을 함께 삭제할 수도 있고, 그대로 남겨 둘 수도 있따.
  • volumeBindingMode: PVC가 생성되자마자 PV를 생성해서 연결할지, 아니면 해당 PVC를 사용하는 파드가 생성될 때 PV를 생성할지 선택할 수 있음
# 스토리지 유형 목록 확인. 이를 통해 기본 스토리지 유형이 어떻게 설정되었는지도 볼 수 있음 kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE hostpath (default) docker.io/hostpath Delete Immediate false 3d9h kiamol docker.io/hostpath Delete Immediate false 17s
스토리지 유형 추가
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc-kiamol spec: accessModes: - ReadWriteOnce storageClassName: kiamol resources: requests: storage: 100M
추가된 스토리지 유형을 사용한 PVC 정의

스토리지를 선택할 때 고려할 점

  • 데이터베이스 같은 유상태 애플리케이션도 쿠버네티스에서 실행해야 할까? 데이터 관리는 쿠버네티스에서도 쉬운 일이 아니며 유상태 애플리케이션은 이런 문제의 일부에 지나지 않음
  • 쿠버네티스로 데이터베이스 서버를 이전하더라도 여전히 어떻게 데이터를 백업하고 스냅샷을 남기며 또 이를 복원할지 고려해야 함. 반면 클라우드환경의 매니지드 데이터베이스 서버는 이런 고민의 대부분을 덜어줌
  • 그럼에도 전체 기술 스택을 쿠버네티스 매니페스트로 옮기고 싶다면 컨테이너 플랫폼을 위해 만들어진 몇 가지 현대식 데이터베이스 서버를 고려해 볼 수 있다(TiDB 나 CockroachDB)
 

연습문제

  1. PVC 부터 일단 만듦
  1. 해당 PVC를 Pod들에 Volume으로 설정해주고 VolumeMount 하여 PVC를 사용하도록 함