# 3. PV와 PVC

쿠버네티스에서도 도커와 같이 호스트에 위치한 디렉터리를 각 포드와 공유함으로써 데이터를 보존하는 것이 가능
그렇지만 쿠버네티스와 같이 다중 서버로 구성된 환경에서는 적합하지 않음

→ 그럴 때 사용하는 것이 PV(Persistent Volume)

# PV

  • 워커 노드들이 네트워크상에서 스토리지를 마운트해 영속적으로 데이터를 저장할 수 있는 볼륨
  • 포드에 장애가 생겨 다른 노드로 옮겨가더라도 해동 노드에서 PV에 네트워크로 연결해 데이터를 계속 사용 가능

# NFS

  • 여러 서버 혹은 Pod에서 동시에 접근 가능한 파일 시스템을 NFS(Network File System)이라 함

# NFS를 네트워크 볼륨으로 사용하기

# 호스트(Raspberry Pi)에 직접 NFS 서버 설치 후 연결

apiVersion: v1
kind: Pod
metadata:
  name: nfs-pod
spec:
  containers:
    - name: nfs-mount-container
      image: busybox
      args: ["tail", "-f", "/dev/null"]
      volumeMounts:
        - name: nfs-volume
          mountPath: /mnt
  volumes:
    - name: nfs-volume
      nfs:
        path: "/export/nfs"
        server: 192.168.0.7
  • AWS나 GCP에서 제공하는 NFS 사용해도 됨
dgh0001@rpi4b:~/study$ kubectl exec -it nfs-pod -- sh
/ # df -h
Filesystem                 Size      Used Available Use% Mounted on
192.168.0.7:/export/nfs  219.6G     28.2G    182.5G  13% /mnt

# PV와 PVC를 이용한 볼륨 관리

  • 위 방식으로 NFS를 바로 명시해서 사용하면 서로 밀접하게 연관되어 있어 서로 분리할 수 없어짐
  • 이를 해결하기 위해 PV와 PVC 사용
  1. 인프라 관리자가 네트워크 볼륨 서버로 PV 리소스를 미리 생성
  2. 사용자(개발자)가 포드를 정의하는 YAML 파일 등에 "영속적인 데이터 저장을 위한 외부 볼륨이 필요함"을 명시 후 해당 PVC 생성
  3. 쿠버네티스가 기존에 생성해뒀던 PV의 속성과 사용자가 요청한 PVC의 요구 사항이 일치하면 두 리소스를 매칭시켜 바인딩함
  • 즉, 사용자는 디플로이먼트의 YAML 파일에서 볼륨의 상세한 스펙을 정의하지 않아도 됨
# Before
volumes:
- name: nfs-volume
nfs:
    path: /
    server: {NFS_SERVER_IP}

# After
volumes:
- name: nfs-volume
  persistentVolumeClaim:
    claimName: mtpvc

# PV와 PVC 사용하기

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  nfs:
    path: "/export/nfs"
    server: 192.168.0.7
  mountOptions:
    - nfsvers=4

# PVC 생성 후 Pod 생성 시 PVC적용

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: ""
  accessModes: # 조건 1
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi # 조건 2

---
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-mount-container
      image: busybox
      args: ["tail", "-f", "/dev/null"]
      volumeMounts:
        - name: my-volume
          mountPath: /mnt
  volumes:
    - name: my-volume
      persistentVolumeClaim:
        claimName: my-pvc

# 실행 결과

dgh0001@rpi4b:~/study$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS
persistentvolume/nfs-pv                                     10Gi       RWX            Retain           Bound

NAME                              STATUS    VOLUME                                     CAPACITY   ACCESS MODES
persistentvolumeclaim/my-pvc      Bound     nfs-pv                                     10Gi       RWX
  • PV와 PVC의 상태(STATUS)가 Bound로 설정되어 있으면 정상적으로 마운트 된 상태

PV와 PVC의 차이 - 네임스페이스

  • PV는 네임스페이스에 속하지 않는 클러스터 단위의 오브젝트
  • PVC는 네임스페이스에 속하는 클러스터 단위의 오브젝트

# PV를 선택하기 위한 조건들

# accessModes

accessModes 이름 설명
ReadWriteOnce 1:1 마운트만 가능, 읽기 쓰기 가능
ReadOnlyMany 1:N 마운트 가능, 읽기 전용
ReadWriteMany 1:N 마운트 가능, 읽기 쓰기 가능
  • 사용하는 NFS 종류에 따라 다름(ex. EBS는 1:1 관계의 마운트만 가능)
  • 그 외에도 볼륨의 크기, 스토리지 클래스, 라벨 셀렉터 등이 있음

# PV의 라이프사이클과 Reclaim Policy

dgh0001@rpi4b:~/study$ kubectl delete -f nfs-pod.yaml
persistentvolumeclaim "my-pvc" deleted from default namespace

dgh0001@rpi4b:~/study$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS
nfs-pv                                     10Gi       RWX            Retain           Released
  • Available → Bound → Released
  • Released는 해당 PV의 사용이 끝났지만, 실제 데이터들이 안에 Retain이라는 Reclaim Policy로 보존되어 있기 때문에 다시 사용 불가능함

# Retain Policy

  • PVC 삭제 시 PV의 데이터를 어떻게 처리할 것인지 별도로 정의 가능
  • 보통은 데이터를 영구적으로 저장하기 위해 Retain 옵션 사용
  • Delete 사용 시 자동으로 PV까지 삭제
  • Recycle 사용 시 PV의 데이터를 모두 삭제한 후 다시 Available 상태로 만들어줌 (Deprecated 예정)

# 다이나믹 프로비저닝과 스토리지 클래스

  • PV를 사용하려면 미리 외부 스토리지를 준비해야 햇음
  • 쿠버네티스는 다이나믹 프로비저닝을 통해 PVC와 일치하는 PV가 존재하지 않으면 자동으로 PV와 외부 스토리지를 함께 프로비저닝 함
  • 위 그림과 같이 만족하는 PV가 없으면 스토리지 클래스에 정의된 속성에 따라 외부 스토리지 생성
  • 다이나믹 프로비저닝 기능을 사용하기 위해서는 미리 해당 기능이 지원되는 스토리지 프로비저너가 활성화되어 있어야 함
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: test
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.168.0.7
  share: "/export/nfs"
reclaimPolicy: Delete

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  storageClassName: test
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

# 결과

dgh0001@rpi4b:~/study$ kubectl apply -f pvc-test.yaml
persistentvolumeclaim/test-pvc created

dgh0001@rpi4b:~/study$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
persistentvolume/pvc-c228acb2-7053-44d4-a499-8b469d5d96c1   5Gi        RWX            Delete           Bound    default/test-pvc   test           <unset>                          11s

NAME                             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/test-pvc   Bound    pvc-c228acb2-7053-44d4-a499-8b469d5d96c1   5Gi        RWX            test           <unset>                 14s

# 특정 스토리지 클래스 기본값으로 사용

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: test
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: nfs.csi.k8s.io
parameters:
  server: 192.168.0.7
  share: "/export/nfs"
reclaimPolicy: Delete
  • 어노테이션 추가로 사용할 디폴트 스토리지 클래스 설정 가능

# 결과

dgh0001@rpi4b:~/study$ kubectl get sc
NAME             PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
test (default)   nfs.csi.k8s.io   Delete          Immediate           false                  73s
  • 별도로 스토리지 클래스 명시 안하면 자동으로 기본 설정된 스토리지 클래스를 통해 다이나믹 프로비저닝 수행