PersistentVolume

개요

퍼시스턴트 볼륨.. 다시 말해 영속 쿠버네티스 볼륨.
영구 볼륨을 제공하는 방법 중 하나로서, 오브젝트로 제공된다.
이렇게 만들어진 오브젝트는 PersisetentVolumeClaim(PVC)이라는 오브젝트를 통해 파드에서 볼륨으로 사용한다.

이 문서에서는 PV와 PVC에 대해서 다룰 것이다.
이 둘 다 각각의 오브젝트이기는 하나, 매우 밀접한 관련이 있어서 같이 쓰는 게 좋다고 판단했다.

사용법

일반적으로는 다음의 흐름을 따른다.

얼핏 보면, 매우 거추장스러운 과정이 많아 보이기도 한다.

개념 정리

먼저 PV는 관리자에 의해 정적으로, 혹은 스토리지 클래스를 통해 동적 프로비저닝쿠버네티스 볼륨이다.
그리고 PVC는 스토리지에 대한 사용자의 요청이다.

그럼 왜 이런 구분이 생겨났을까?

주의

이 내용은 쿠버네티스 아키텍처의 원론적인 내용을 다룬다.
그리고 상당한 뇌피셜을 담고 있다.
아래 [[#PV 라이프사이클]]을 읽으며 개념과 더 친숙해진 이후에 볼 것을 추천한다.

스토리지와 PV의 분리 필요성

PV는 클러스터에서 사용할 수 있는 영속 스토리지 자원의 추상화이다.
클러스터에서 NFS, Amazon EBS, 등 다양한 스토리지를 활용할 수 있다고 생각해보자.
각각의 스토리지마다 인증, 사용량 제한 등 개별적인 설정이 필요할 수 있다.
이것을 사용자들이 사용하기 편하도록, 가상의 볼륨 인터페이스를 통해 공급을 해줄 수 있다면 사용성이 크게 올라갈 것이다.
쿠버네티스에서는 PV를 통해 이러한 추상화를 제공해주는 것이다.

PV와 PVC의 분리 필요성

그럼, PVC는 왜 필요할까?
그냥 PV를 바로 사용자가 요청하는 식으로 중간 단계를 없애도 되는 게 아니었던 것인가?
이 디자인은 운영자와 개발자의 책임 영역 분리를 위한 추상화를 기반으로 하고 있다.

엄밀하게 표현된 그림은 아니지만, 간단하게 누가 어떤 오브젝트와 리소스를 만들고 관리하는지를 담았다. PV에는 다양한 스토리지 관련 세부 설정을 넣을 수 있으며, PVC에는 용량, 접근모드 세팅 등을 요청할 수 있다. 이를 통해 가지고 있는 지식과 기술이 다른 두 관련자는 자신의 책임 범위 내의 설정과 작업을 할 수 있게 된다. 이게 PV와 PVC가 나뉘어 만들어진 핵심 이유라 할 수 있겠다. - 관리자 - 클러스터 리소스를 정의하고 관리할 수 있다. - 사용 가능하게 할 용량과, 성능, 프로비저닝 방법 등을 정의할 수 있다. - 개발자 - 리소스의 세부 스펙(성능, 인증, 인가 등)을 모르는 채로 용량과 접근 모드만을 명시해 사용할 수 있다. - 실제 자원이 뭐가 있고 어떻게 구현됐는지 알 필요가 없다. ## PVC와 파드의 분리 필요성 그러나 새로운 생각이 또 든다. PVC를 왜 바로 파드 스펙에 쓰지 않고 오브젝트로 따로 구분했을까? 이건 내 추측이나, 아래에서 볼 [[#reclaiming]]과 관련이 깊다고 생각한다. 미리 말하자면, PV는 사용되지 않을 때 자원 회수를 할지 말지 정책을 지정할 수 있다.

PVC 오브젝트가 따로 존재하지 않고 파드의 스펙으로 작성하는 방식으로 쿠버네티스가 개발되었다고 생각해보자.
이때, 디플로이먼트의 여러 파드가 한 PV를 사용하도록 설정했다.
그리고 PV가 더 이상 사용되지 않으면 자원을 자동으로 회수하도록 정책을 지정했다.
모든 파드가 어떠한 이유로든 종료되는 일이 발생하면, PV의 자원은 모두 날아갈 것이다.
결국 파드의 라이프사이클과 무관하게 있어야 할 PV가 파드에 영향을 받는 일이 생긴다는 것이다.
이를 막고자 무조건 PV는 사람이 직접 지우게 만들어야 하나?
그러면 운영 효율성이 어마무시하게 떨어지게 될 것이다.

아울러 PV는 네임스페이스에 종속되어있지 않다.
4.RESOURCE/KNOWLEDGE/Kubernetes/시큐리티/멀티 테넌시를 하며, 각 네임스페이스에서 사용할 수 있는 자원을 격리시키고 싶다면 네임스페이스 단위로 접근할 수 있는 오브젝트 단위가 추가적으로 필요하다.

대안

물론, PV를 처음부터 네임스페이스 종속적으로 구현하는 방법도 있었을 것이다.
그러면 여러 PV를 만들 때 관리자가 신경써야 할 요소가 많아질 수 있다.

고수의 의견

내 나름의 결론을 내린 후 쿠버 톡방에 이 질문을 올렸는데, 대선배님께서 의견을 주셨다.
그 답변이 내게도 확 와닿기에 여기에 정리하고자 한다.
파드는 쿠버네티스에서 불변한(immutable) 객체이며, 이 방침을 지키며 설계하는 것이 쿠버네티스를 개발하는 입장에서 주된 고려사항 중 하나일 것이다.
이때 볼륨 확장을 유연하게 하기 위해서, 혹은 이후에 추가될 여지가 있는 다양한 기능들에 대해 파드와 PVC와의 한 단계 추상화를 더 고려한 게 현재의 쿠버네티스이다.
만약 그저 PVC가 파드 스펙에 들어가 있다면, 볼륨 확장을 동적으로 할 때 불변 객체로서의 파드의 의미가 퇴색될 것이다.
이처럼 파드가, 내부의 컨테이너가 사용할 수 있는 자원들을 유연하게 관리할 수 있도록 개발자들은 초기에 디자인하고 구현했을 것이다.

나는 이 답변을 듣고 추가적인 생각이 하나 더 들었다.
동적으로 컨테이너의 메모리 제한을 확장하는 것이 가능한 것으로 아는데(확실친 않다), 그렇다면 이후에는 메모리, cpu 등의 자원에 대한 오브젝트가 추가로 분리될 수도 있지 않을까?

결국 PV-PVC-파드의 관계는 효율성과 확장성을 위한 추상화가 아니었나 생각한다.

계속 말이 나오는 스토리지 클래스는 무엇인가?
pv를 관리자가 pv를 만들 때 수요에 맞게 다양한 pv를 만들 수 있다.
이때 관리자는 실제 스토리지 구현에 대한 부분들을 숨기고 싶을 수 있는데, 이때 이 설정을 묶어서 관리하는 것이 스토리지 클래스다.
근데 이것 덕분에 StorageClass도 가능해지는 것이다!

PV 라이프사이클

그렇다면 클러스터의 자원인 PV와 해당 자원에 대한 요청인 PVC의 라이프사이클을 쭉 따라가보자.

provisioning

자원을 마련하고 제공할 수 있도록 세팅하는 과정, 즉 프로비저닝.
이 과정에는 계속 얘기하듯이 두 가지 방식이 존재한다.

주의할 것은 PV를 만들었다고 해서 정말 볼륨 만들어진 것은 아니라는 것이다.
요청에 대한 인터페이스만 제공하는 것이라 할 수 있다.
이름이 참 헷갈리게 지어졌다고 느낀다.

binding

컨트롤러가 PVC에 엮일 PV를 매칭하고 묶어주는(bind) 단계이다.
위의 그림에서 PVC에 직접적으로 매칭할 볼륨 이름을 지정해주어 원하는 놈이 붙도록 유도했지만, PVC에는 사실 원하는 스펙만 명시해도 된다.
컨트롤러는 명시된 사이즈, 접근 모드, 스토리지 클래스 등을 고려하여 조건에 일치하는 PV를 붙여줄 것이다.

동적 프로비저닝에서는 실질적으로 거의 동시에 일어나는 과정이라 할 수 있겠다.
한번 연결되기만 하면 일단 해당 연결은 완전히 독점되어, 다른 PVC가 뺏어간다던가 하는 일은 발생하지 않는다.
달리 말하자면 PV와 PVC는 1대1 관계라는 것이다.

굳이 PV 스펙에 명시하지 않아도, 바인딩이 완료되면 PV 스펙에 바인딩된 PVC가 명시된다.
참고로 이 .spec.claimRef 필드를 직접 작성해도 된다.
내 PV에 특정 PVC만 연결됐으면 좋겠다! 싶다면 네임스페이스와 이름을 명시해서 다른 PVC는 바인딩되지 못하도록 만들자.

using

파드가 볼륨으로서 PVC를 사용한다.
PVC가 볼륨이기 때문에, 당연히 다양한 파드와 컨테이너가 접근 모드에 따라 이것을 사용할 수 있다.

파드가 삭제되더라도 사용된 PV, PVC는 알아서 삭제되지 않으며, 이를 통해 의도되지 않은 데이터 손실을 방지한다.
또한 파드가 PVC를 사용 중일 때 PVC와 PV가 삭제되지 않는 것도 보장된다.
image.png
일단 바운딩된 PVC에는 kubernetes.io/pvc-protection라는 Finalizer가,
image.png
연결된 PV에는 kubernetes.io/pv-protection가 붙는 것을 볼 수 있다.

reclaiming

파드가 종료되고 나면 비로소 해당 자원을 회수(reclaim)할 수 있다.
이때 PV에 회수 정책을 명시해 자원을 어떻게 할 것인지 규칙을 정할 수 있다.
이 규칙은 PV와 연관된 실제 데이터와 자원을 어떻게 할 것인지에 대한 정책이다.

원래는 Recycle이란 정책도 있었는데 더 이상 사용되지 않는다..

retain

Retain은 자원 회수가 항상 수동으로 이뤄지게 한다.
PVC가 삭제된 이후 PV는 풀린(released) 것으로 여겨지지만 데이터가 남아있어 다른 PVC가 접근할 수 없다.
회수를 직접 할 때는 다음처럼 하면 된다.

일단 pvc를 지워도 pv가 남는 것은 당연하게 느껴질 것이다.
image.png
그러나 이렇게 pv를 지우더라도..
image.png
해당 자원은 실제로 잘만 남아있는 것이 바로 retain이다.
(EKS에서 AWS EBS CSI Driver로 실습한 모습이다.)

이 방법의 장점은, PV를 지우더라도 같은 스펙으로 다시금 생성하면 이전의 볼륨을 그대로 쓸 수 있다는 것이다.
(CSI를 활용한 동적 프로비저닝된 PV였다면 csi.volumeHandle 필드를 잘 보고 작성해주면 될 것이다.)
매우 중요한 데이터가 보관되는 볼륨이라면 이 정책을 활용하는 것이 좋겠다.
혹은 볼륨의 데이터를 유지하면서 워크로드를 업데이트할 때도 활용할 수 있는 방법이다.
이 정책이 필요한 케이스라면 [[#binding]]에서 본 것처럼 아예 특정 PVC만이 PV에 바인딩되도록 하는 것도 좋을 것이다.
아울러 볼륨 스냅샷 등의 기능도 활용해주는 것이 좋을 것으로 생각된다.

delete

PV 제거시 관련 인프라 자원 역시 회수된다.
PV를 명시하지 않는 동적 프로비저닝에서는 스토리지 클래스의 정책에 따르며, 기본은 delete이다.

PV가 제거될 때, 인프라 자원이 먼저 확실하게 제거된 후 PV가 제거되도록 파이널라이저가 사용된다.
CSI 볼륨이라면 external-provisioner.volume.kubernetes.io/finalizer가 붙는다.
내장 볼륨이라면 kubernetes.io/pv-controller가 붙는데, 동적으로 프로비저닝된 경우에만 한정된다.
아무튼 데이터의 확실한 제거가 보장된다는 것이다.

PV의 상태

라이프사이클을 봤으니 PV의 각 상태도 이해할 수 있겠다.

.status.lastPhaseTransitionTime에는 언제 마지막으로 이 단계가 바뀌었는지 나온다.

PV 유형

영구 볼륨에 속하는 모든 볼륨이 PV로 만들어질 수 있다.
엄밀히 말해 완전히 동치되지는 않는다.
가령 persistentVolumeClaim은 애초에 PV를 사용하기 위해 사용하는 볼륨이니 당연히 PV로 만들 수 없다..
여기에서 간단하게 각 볼륨들을 명시만 하고, 조금 주의 깊게 봐야할 녀석들에 대해서만 추가 작성을 하겠다.

fc

fc를 본다.

iscsi

iscsi를 본다.

nfs

nfs 쪽에 써뒀는데, PV로 만들 때 비로소 추가적인 설정들을 할 수 있다.

hostPath

hostPath도 PV 오브젝트로 만들 수는 있다.
그러나 강력하게, 아래의 local을 사용하는 것이 권장된다.

local

노드에 이미 존재하고 있는 공간을 볼륨으로서 제공한다.
hostPath와 다르게 바로 파드의 스펙으로 명시할 수 없고, 무조건 PV로만 만들 수 있다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  volumeMode: Filesystem
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node

특이한 놈이라 먼저 여기에 양식을 써둔다.
local은 .spec.local.path로 노드의 경로를 명시하고, .spec.nodeAffinityAffinity를 명시해준다.
즉 이 볼륨이 어느 노드에 붙을지 미리 지정을 하는 것이다.
이 볼륨을 사용하는 파드는 해당 노드로 스케줄될 것이 보장된다.

그럼 파드에 다르게 쓰면 어떻게 되나?

몇 가지 알아야 할 사항들이 있다.

hostPath와 local의 차이

위에서 보았듯이 많은 차이가 있는 것이 보인다.

나는 이 두 방식이 존재하는 이유가 바로 용도의 차이라고 본다.

csi

저쪽 문서에서는 영구 볼륨에 넣지 않았던 CSI를 통한 볼륨.
단순하게 파드의 볼륨 스펙으로 명시하는 csi 임시 볼륨는 영속 볼륨이 아니기 때문이다.
하지만 PV로서 csi를 쓴다면 의미가 당연히 다르다.

그런데 실상 csi로 직접 PV를 만드는 케이스는 거의 없다고 무방하다.
csi를 활용할 때는 동적 프로비저닝을 하는 게 대부분이기 때문이다.

이걸 직접 쓴다고 생각하면 귀찮은 지점을 생각해볼까.

  1. csi에 해당하는 공간에 직접 자원을 선정한다.
  2. 이후에 해당 자원을 PV로 지정한다.
  3. 이후에 해당 PV를 PVC로 가져온다.
  4. 이후에 해당 PVC를 파드에 마운팅한다.

할말하않이 된다 이 말입니다.

PV 양식 작성법

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

NFS로 PV를 만드는 예시.
PV는 네임스페이스 종속적이지 않다는 점을 참고하자.
용량과 볼륨 모드, 접근 모드, 정책 등을 설정하는 것이 보인다.

nfs 볼륨을 쓰고 싶다면 이렇게 pv를 만들어서 하는 게 좋다.
여기에서는 /sbin/mount.nfs 헬퍼 프로그램을 사용한다.

...
spec:
  csi:
    driver: <프로비저너이름>
    volumeHandle: <해당 자원을 고유하게 인식할 수 있는 식별자>
...

참고로 csi로 pv를 만들 때는 이렇게 해주면 된다.

capacity

capacity.storage에 허용해줄 양을 명시하면 된다.
여기 값보다 작은 용량을 요청하는 PVC는 해당 PV에 매칭될 수 있을 것이다.
storage만 하위 필드로 쓸 수 있으나, 추후에는 IOPS, throughput 등도 넣을 수 있도록 개발될 수 있다고 한다.

volumeMode

어떤 유형의 스토리지를 쓸 것인가에 대한 필드이다.
현재는 Block, Filesystem인데, 블록은.. 진짜 특별한 케이스고 보통은 파일시스템이라 생각하자.
당연히 파일시스템으로 만들어진 볼륨이어야 파드에 붙을 때 디렉토리로 붙는다.
만약 내용물이 없다면 마운팅 직전에 쿠버네티스가 직접 파일시스템을 구성해줄 것이다(그럼 어떤 유형으로?).

블록이면 말 그대로 블록 디바이스를 붙일 수 있다.
PV, PVC 둘 다 volumeMode: Block으로 명시를 해줘야만 바인딩이 된다.

  volumeMode: Block
  fc:
    targetWWNs: ["50060e801049cfd1"]
    lun: 0
    readOnly: false

이런 식으로 조금 특별한 케이스다.

  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: [ "tail -f /dev/null" ]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda

블록 디바이스를 붙일 때는 파드 스펙에서 이렇게 volumeMounts가 아니라 volumeDevices를 써야 한다!

accessModes

사용 가능한 접근 모드를 지정하는 필드이다.
PVC에 대해 지원하고 싶은 방식들을 리스트로 작성하면 된다.
PVC는 나열된 리스트 중 한 방식으로 바인딩이 가능할 것이다.

근데 유의할 게, 이건 사실 이건 PVC를 매칭할 때 사용하는 조건 중 하나에 불과하다..
여기에서 쓰기 허용을 넣더라도 실제 프로비저너는 읽기만을 허용하고 있을 수도 있다.
다만 RWOP 만큼은 한 파드에 붙는 것을 보장해준다고 한다.

storageClassName

StorageClass를 지정할 수도 있다.
이것도 컨트롤러가 PV와 PVC를 매칭하는 조건으로 쓰인다.
이 값을 지정하지 않은 PV는 스토리지 클래스를 사용하지 않는 PVC와만 바인딩 될 수 있다.
이게 주의점이 있는데, 아래의 [[#storageClassName]]에서 다루겠다.

과거에는 어노테이션을 이용했으며, 이 방식이 아직은 사용 가능하지만 곧 없어질 것이라고 한다.

persistentVolumeReclaimPolicy

위에서 말한 [[#reclaiming]], 즉 회수 정책을 지정할 수 있다.
참고로 스토리지 클래스에서 이 필드의 이름은 reclaimPolicy이다.
왜 굳이 이름이 다르게 해서 작성할 때 헷갈리게 만들어뒀는지는 모르겠다.

잠깐 언급했던 Recycle은 해당 볼륨을 rm -rf 해주는 놈인데..
Kubernetes v1.32 - Penelope 기준 [[#nfs]], [[#hostPath]]만 사용할 수 있다.

recycle의 필요성

애초에 이 기능을 활용하는 케이스가 거의 없다 싶은 게 내 생각이다.
pv는 영속성을 바라는 리소스이다.
그래서 아예 자원이 남을지 말지만이 중요한 관건이 된다고 생각한다.
애매하게 실제 내용은 다 지워지고 껍데기 리소스만 남기는 게 무슨 의미가 있나 하는 것이다.

mountOptions

마운팅할 때 이뤄지는 옵션을 미리 지정할 수 있다.
그런데 모든 PV 유형이 지원하는 것도 아니고 양식도 저마다 다양할 것이다.
이 필드는 PV를 만들 당시 문제 여부를 검증하지 않는다.
문제가 있다면 나중에 파드에 마운팅되는 시점에 실패할 것이다.

nodeAffinity

이 볼륨을 사용하고자 하는 파드들의 위치를 Affinity로 제한할 수 있는 필드이다.
[[#local]]을 제외하면 스토리지 자체가 조건에 맞는 노드에 해당 생긴다는 의미는 아니라 그냥 선택적인 필드라고 생각하자.

PVC 양식 작성법

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

거의 모든 스펙이 [[#PV 양식 작성법]]과 똑같다.
구체적으로는, [[#accessModes]], [[#volumeMode]]가 같다.

PVC는 네임스페이스에 종속된다는 것을 유의하자.
이 말은 접근 모드가 뭐든 간에 PVC에는 같은 네임스페이스 오브젝트를 가진 노드에서만 사용이 가능하다는 것을 암시한다.

resources

할당 자원 관리에서 지정하는 방식을 사용하면 된다.
요청보다 작은 [[#capacity]]를 가진 PV는 매칭되지 않을 것이다.

볼륨 선택

어떤 PV와 연결되고 싶은지 정할 때는 다음의 두 가지 방법이 있다.

선택 관련 필드를 명시하지 않으면 컨트롤러가 알아서 매칭될 만한 PV를 찾아서 연결해준다.
당연하지만, PV를 고르려는 필드가 들어가는 PVC는 동적으로 프로비저닝되지 않는다!

storageClassName

이것도 PV랑 같이, 사용하라 스토리지 클래스를 명시하는 필드이다.
""로 필드를 명시하는 것은 클래스를 두지 않겠다는 말과 같긴 한데.. 이게 좀 PV의 케이스와 다르게 동작한다.

DefaultStorageClass Admission Control가 있다면 클래스가 명시되지 않은 PVC는 기본 스토리지 클래스가 스펙으로 자동으로 들어간다!
원래는 스토리지 클래스가 명시 안 된 PV에 바인딩을 하고 싶었는데 실패하는 케이스가 나올 수도 있다는 것...
이런 케이스도 있을 수 있다.
기본 스토리지 클래스가 없는 클러스터에서 먼저 PV를 만들었다.
PVC를 만드는 와중에 클러스터에 기본 스토리지 클래스가 설정이 돼버리면..
역행적 할당(retroactive assignment)라 하여 클러스터는 기존에 스토리지 클래스가 명시되지 않은 PVC에 전부 클래스를 할당해주기 때문에 의도하지 않은 동작을 일으킬 것이다.

정 PVC에 아무 값도 안 들어가게 하고 싶다면, 명시적으로 storageClassName: ""과 같은 식으로 지정하자.
이렇게 하는 게 좋은 경우가 있기는 하다.
관리자는 동적으로 마구 기본 스토리지 클래스를 바꿔댈 수 있기에, 이러한 위험으로부터 자유로울 수 있는 것이다.

dataSource

볼륨 복제, 볼륨 스냅샷과 관련한 필드이다.
처음 볼륨을 마운팅할 때, 빈 볼륨이 아니라 무언가 채워진 채로 마운팅할 수 있게 해준다.
이 기능들은 이를 지원하는 CSI 플러그인을 사용할 때만 사용할 수 있다.

  dataSource:
    name: new-snapshot-test
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io

볼륨 스냅샷은 이렇게 한다.
대충 봐도 스냅샷 떠둔 볼륨을 그대로 쓰는 것이다.

  dataSource:
    name: existing-src-pvc-name
    kind: PersistentVolumeClaim

볼륨 복제는 이렇게 한다.

dataSource는 이렇게 PVC나, VolumeSnapshot만을 받을 수 있다.

dataSourceRef

AnyVolumedataSource 피처 게이트가 활성화돼있을 때 사용할 수 있는 필드이다.
위의 dataSource와 같이 빈 볼륨을 받을 때 내용을 채울 때 쓰는 것이며, 둘은 양립할 수 없다.
그러나 이 필드가 dataSource와 다른 건, 같은 네임스페이스의 커스텀 오브젝트'도' 집어넣을 수 있다는 것이다!

볼륨 채우기

문서에서는 볼륨을 채워주는 것을 volume populator라고 부른다.
볼륨 생성자? 볼륨 기입자? 어떻게 번역할지 모르겠어서 그냥 넘어간다.

여기에서 말하는 커스텀 오브젝트는 volume populator이다.
이것은 볼륨의 내용물을 채우는 컨트롤러로, CRD로 구현해야 할 것이다.

dataSource와의 비교

이 기능이 활성화된 클러스터라면, 이 필드를 사용하는 것을 강력하게 문서에서 추천한다.
이 필드가 좋은 이유는 다음과 같다.

이러면서 다른 오브젝트도 쓸 수 있으니 사옹만 할 수 있다면 안 좋은 이유가 없다는 것이다.
어차피 같은 기능을 하는 필드인데 둘 다 남겨둔 이유는 그저 과거와의 호환성 때문이다.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-ns1-pvc
  namespace: default
spec:
  from:
  - group: ""
    kind: PersistentVolumeClaim
    namespace: ns1
  to:
  - group: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: new-snapshot-demo

이런 식으로 세팅하면 ns1의 PVC가 default의 볼륨 스냅샷을 사용할 수 있게 될 것이다.
말 그대로 해석해보자면 ns1의 PVC에서 default의 볼륨 스냅샷으로 참조하는 것을 허용하는 오브젝트라 할 수 있겠다.

볼륨 요청 확장

이건 PVC를 익히는데 필수적인 건 아니라 생각해서 뒤쪽으로 내용을 뺐다.

이런 상황을 생각해보자.
20메가만 요청하는 PVC를 만들어서 바인딩이 완료됐다.
근데 갑자기 50메가가 필요한 것 같다!
함부로 PVC를 지웠다 다시 만드는 것은 PV를 뺏기는 상황을 야기할 수 있다(물론 PV에 claimRef를 걸면 되긴 하다).
또한 이를 사용 중인 어플리케이션이 있다면 더더욱 불가능하다.
동적으로 50메가로 확장할 수는 없을까?

이를 위해 동적으로 볼륨 크기를 확장할 수 있도록 CSI에 정의되어 있다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: example-vol-default
provisioner: vendor-name.example/magicstorage
allowVolumeExpansion: true

스토리지 클래스에 allowVolumeExpansion: true 필드가 설정돼야 한다.
이렇게 돼있다면, 그저 PVC의 스펙에서 사이즈를 키우는 것만으로 편하게 용량을 확장할 수 있다.
연결된 PV도 알아서 확장이 지원된 것이 반영될 것이다.

주의점

동적 확장 기술은 당연히 많은 주의점을 수반한다.

당연하지만.. 이걸 지원하지 않는 CSI 드라이버가 있을 수도 있으니 잘 확인해보자.
또한 파일시스템으로 제공되는 스토리지라면 xfsext3,ext4 파일시스템에 대해서만 가능하다.

또한 이미 볼륨이 파일시스템을 가지고 있다면, 파드에서 쓰기가 허용됐을 때만 가능할 것이다.
파드에서 사용중인 요청도 리사이징이 가능하다.

직접 PV의 사이즈를 바꾸는 작업은 지양하는 게 좋다.
왜냐하면 볼륨 확장 작업은 컨트롤 플레인에서 PV와 PVC의 상태를 추적하면서 발생하는 것이기 때문이다.
그냥 PV를 직접 수정해버리면 컨트롤 플레인은 제대로 상황을 추적할 수 없고 실질적인 자원의 리사이징이 발생하지 않을 수 있다.

이거 구체적으로 어케 된다는 거임

볼륨을 확장하는데, 가용한 자원보다 큰 사이즈를 요청해버려서 계속 요청이 실패할 수도 있다.
그러면 관리자가 조치를 취할 때까지 PVC 확장은 계속 재시도될 것이다.
그럼 이 확장을 재조정을 해야 할 텐데, 여기에 두어 가지 전략을 생각할 수 있다.

이거 실험해보자.

관련 문서

이름 noteType created
StatefulSet knowledge 2024-12-26
PersistentVolume knowledge 2025-01-11

참고