⚓️

스토리지(쿠버네티스 스터디 5주차)

최민석·2025-03-31

Chap 10. 스토리지

ka8main.png

쿠버네티스에서는 단순히 호스트 서버의 볼륨을 연결하는 것 외에 다양한 형태로 데이터를 저장 및 참조하는 방법들을 제공한다.
이 장에서는 다음과 같은 리소스를 살펴본다:

  • PersistentVolume
  • PersistentVolumeClaim
  • StorageClass

PersistentVolume

PV는 데이터 저장소를 추상화시킨 리소스이다. 쿠버네티스 클러스터 관리자가 데이터 저장소를 사용하기 위해 미리 마련한 저장 자원을 나타낸다.

PV에는 구체적인 저장소 정보가 담겨있는데, 예를 들면:

  • AWS 플랫폼: EBS(Elastic Block Storage) 정보
  • GCP 플랫폼: PersistentDisk 정보
  • 로컬 호스트: 로컬 호스트의 볼륨 path 정보

이처럼 다양한 환경에서 다양한 저장소 타입을 제공하기 위해 쿠버네티스에서는 PersistentVolume(PV)라는 추상화된 리소스를 사용하고 각 환경에 따라 그에 맞는 타입을 선택한다.

hostPath PV

Chapter 5에서 본 호스트 서버의 볼륨을 연결하는 에제를 PersistentVolume으로 표현하면 다음과 같다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-volume
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /tmp
  • storageClassName: 저장소 타입의 이름을 정의한다. PersistentVolumeClaim(PVC)에서 특정 저장소 타입을 지정하기 위해 사용한다.
  • capacity: 데이터 저장소의 크기를 결정한다.
  • accessModes: 접근 모드를 설정한다. ReadWriteOnce는 동시에 1개의 Pod만 해당 볼륨에 접근할 수 있다는 것을 의미한다. NFS volume같은 경우 ReadWriteMany로 여러 Pod에서 동시 접근이 가능하다.
  • hostPath: 호스트 서버에서 연결될 path를 나타낸다.(절대경로)

shell1

NFS PV

Network File System도 PV로 사용할 수 있다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-nfs
spec:
  capacity:
    storage: 20Gi 
  accessModes:
    - ReadWriteMany  # 여러 Pod가 동시에 접근 가능하도록 설정
  persistentVolumeReclaimPolicy: Retain  # PVC 삭제 시 PV 유지
  storageClassName: "nfs-storage"
  mountOptions:
    - nfsvers=3
  nfs:
    server: 172.30.1.29
    path: "/share/MD0_DATA/some_path"
  • storageClassName: nfs-storage라는 이름을 사용한다.
  • capacity: 데이터 저장소의 크기
  • accessModes: NFS 의 경우 ReadWriteMany로 설정할 수 있다.
  • mountOptions: NFS 서버와 마운트 하기 위한 옵션을 설정 가능하다. (필자는 구형 NAS를 사용중이라 nfsv3과 연동하기 위해 위와 같이 설정했다.)
  • nfs: 마운트할 NFS 서버 정보를 입력한다.

awsElasticBlockStorePV

AWS EBS를 사용하는 예제는 다음과 같다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: aws-ebs
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  awsElasticBlockStore:
    volumeID: vol-1234567890abcdef0
    fsType: ext4
  • accessModes: 접근 모드를 설정
  • awsElasticBlockStore: AWS EBS 자원 정보를 입력한다.

그 외 다른 PersistentVolume

  • azureDisk: 애저에서 제공하는 저장소
  • emptyDir: Pod와 생명주기를 같이하는 임시 저장소다. 주로 같은 Pod내 컨테이너들끼리 filesystem을 통한 정보를 주고받을 때 사용한다.
  • downward API: 일반적인 볼륨과는 다르게 쿠버네티스 리소스 메타 정보를 마운트하여 마치 파일처럼 읽을 수 있게 제공한다.
  • configMap: Chap5 에서 살펴봤듯이, ConfigMap 리소스를 마치 PV 리소스처럼 마운트하여 사용할 수 있다.

PersistentVolumeClaim

PersistentVolumeClaim(PVC)는 저장소 사용자가 PV를 요청하는 리소스이다. 클러스터 관리자가 PersistentVolume을 통해 데이터 저장소를 준비하면 쿠버네티스 사용자(또는 개발자)가 PVC 요청을 통해 해당 리소스를 선점하다. 이름에서 알 수 있듯이 PersistentVolume의 사용을 요청(Claim)하는 역할을 담당한다.

pvc

PVC 아키텍처 (출처: https://medium.com/devops-mojo)

작동 순서는 다음과 같다.

  1. StorageClass 정의
  • 관리자가 StorageClass를 먼저 정의한다.
  • 이는 “어떤 방식으로 동적으로 볼륨을 생성할 것인가”에 대한 규칙을 담는다. 예: AWS EBS, NFS, Azure Disk 등.
  • provisioner, parameters, reclaimPolicy 등을 지정한다.
  1. 사용자가 PVC(PersistentVolumeClaim) 생성
  • 사용자는 어떤 StorageClass를 쓸 것인지 명시하고, 원하는 용량(resources.requests.storage)과 접근 모드(accessModes)를 명시한 PVC를 생성한다.
  • 이때 아직 PV는 명시적으로 존재하지 않아도 된다. 동적 프로비저닝이 가능한 경우 자동으로 생성된다.
  1. PV Controller가 동작
  • PVC가 생성되면, kube-controller-manager 내의 PV Controller가 이를 감지하고,
  • 해당 StorageClass의 정의에 따라 자동으로 PV(PersistentVolume) 를 생성한다.
  1. PV와 PVC 바인딩
  • 생성된 PV는 PVC와 조건(용량, 접근 모드 등)이 일치할 경우 자동으로 바인딩된다.
  • 이때 PVC는 특정 PV를 점유(Claim)한 상태가 된다.
  1. Pod에서 PVC 사용
  • 사용자는 Pod 정의에서 volume 항목에 PVC 이름을 명시해 마운트한다.
  • 결국 Pod → PVC → PV → 실제 스토리지로 연결된다.
  1. 실제 Storage와 연결
  • PV의 spec에 따라 AWS EBS, GCE PD, Azure Disk 등 실제 스토리지에 연결된다.
  • 쿠버네티스 노드 상에서 마운트가 이루어진다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata: 
  name: my-pvc
spec:
  storageClassName: manual
  accessModes:
    -ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  • storageClassName: 선점할 PV의 저장소 클래스를 지정한다.
  • accessModes: 선점할 PV의 접근모드를 설정한다.
  • resources: 요청할 저장소 크기를 지정한다.

PVC 리소스를 생성하면 요청한 저장소 타입클래스 이름(storageClassName)에 맞는 PV를 연결해준다.

이제 Pod에서

spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - mountPath: /test-volume
      name: vol
  volumes:
  - name: vol
    persistentVolumeClaim:
      claimName: my-pvc

로 마운트 할 수 있다.

StorageClass

StorageClass 소개

StorageClass 리소스는 클러스터 관리자에 의해 사용자들이 선택할 수 있는 스토리지 종류를 열거한 것이다.

사용자는 클러스터 관리자가 제공하는 StorageClass를 이용하여 동적으로 볼륨을 제공 받을 수 있다. 원래 데이터 저장소를 사용하려면 먼저 쿠버네티스 관리자가 데이터 저장소를 미리 준비해야 한다. 만약 가용한 볼륨이 존재하지 않는다면, Pod가 생성되지 않고 Pending 상태로 대기하게 된다.

하지만 StorageClass를 이용하면 볼륨을 생성하기를 기다릴 필요 없이 동적으로 데이터 저장소를 제공받을 수 있다!

kubectl get sc

StorageClass 리소스는 클러스터 레벨의 리소스로, 네임스페이스 지정을 할 필요가 없다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata: 
  name: my-pvc-sc
spec:
  storageClassName: local-path
  accessModes:
    -ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  • storageClassName: 사용자가 직접 정의한 manual StorageClass가 아닌 클러스터 관리자가 제공한 StorageClass인 local-path를 사용한다.

PVC가 생성되면 StorageClass의 정의에 따라 자동으로 PV가 동적으로 생성되며, 조건이 일치하면 곧바로 PVC와 바인딩된다. 이후 Pod에서 해당 PVC를 사용하면 실제 스토리지가 마운트된다.

※ 단, volumeBindingMode: WaitForFirstConsumer로 설정된 StorageClass의 경우엔 Pod 스케줄링이 될 때까지 PV 생성을 기다린다. (그건 지연 바인딩 이라고 따로 다루는 예외 케이스임.)

StorageClass를 사용하는 이유

첫째로, PV를 StorageClass가 대신 특정 디렉터리 위치 아래(var/lib/...)로만 만들어주기 때문에 일반 사용자가 로컬 호스트 서버의 아무 위치나 디렉터리를 사용하지 못하게 막을 수 있다.

둘째로, local-path와 같이 간단하게 PV를 설정하는 경우는 별 차이가 없겠지만 NFS StorageClass와 같이 NFS 서버정보, 마운트 옵션, 마운트 디렉터리 등 PV를 생성하기 위해 복잡한 인프라 정보를 알아야 하는 경우, 사용자가 StorageClass에게 요청만 하면 나머지는 StorageClass가 알아서 PV를 만들어주어 PVC에 연결해 준다.

  1. 일반 사용자가 StorageClass를 이용하여 PVC를 생성
  2. StorageClass procisioner가 사용자의 요청 인지
  3. 사용자의 요청에 따라 PV 생성
  4. 볼륨 생성
  5. PV와 볼륨이 연결됨.

NFS StorageClass 설정

이번에는 네트워크로 연결된 NFS 볼륨을 동적으로 생성하는 StorageClass를 만들어보자.

책에서는 외부 NFS를 사용하지 않고 Helm으로 내부 NFS를 사용했는데, 필자는 NAS가 있으므로 External Procisioner를 사용할 것이다.

먼저, nfs-subdir-external-provisioner를 helm으로 설치하고, 내 NAS의 IP와 공유 디렉토리를 지정한다.

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
  --set nfs.server=192.168.0.10 \
  --set nfs.path=/share/MD0_DATA/k8s_test \
  --set storageClass.name=nfs-storage

그리고 아래와 같이 StorageClass를 만들어준다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "true"   # PVC 삭제 시 디렉터리를 아카이브할지 여부
reclaimPolicy: Delete
volumeBindingMode: Immediate
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  storageClassName: nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

이제 PVC를 생성하면 PV가 자동으로 생성된다.