Search
Duplicate

퍼시스턴트 볼륨의 동적 프로비저닝

Created By
Status
완료
Type
Created
2022/12/13 23:24
Last Edited Time
2022/12/13 23:24
Last Edited By
Date
Week
2
위 주제에 이야기 함에 앞서 볼륨, 컨테이너와 디스크 스토리지 연결에 관해 얘기하고 넘어가고 싶습니다.

볼륨

hostPath

hostPath다음은 hostPath 라는 볼륨 타입인데, hostPath는 노드의 로컬 디스크의 경로를 Pod에서 마운트해서 사용한다. 같은 hostPath에 있는 볼륨은 여러 Pod 사이에서 공유되어 사용된다.또한  Pod가 삭제 되더라도 hostPath에 있는 파일들은 삭제되지 않고 다른 Pod가 같은 hostPath를 마운트하게 되면, 남아 있는 파일을 액세스할 수 있다. 주의할점 중의 하나는 Pod가 재시작되서 다른 노드에서 기동될 경우, 그 노드의 hostPath를 사용하기 때문에, 이전에 다른 노드에서 사용한 hostPath의 파일 내용은 액세스가 불가능하다. hostPath는 노드의 파일 시스템을 접근하는데 유용한데, 예를 들어 노드의 로그 파일을 읽어서 수집하는 로그 에이전트를 Pod로 배포하였을 경우, 이 Pod에서 노드의 파일 시스템을 접근해야 한다. 이러한 경우에 유용하게 사용할 수 있다.

퍼시스턴트 스토리지 사용

1.
파드에서 실행 중인 애플리케이션이 디스크에 데이터를 유지해야 하고
2.
파드가 다른 노드로 재스케줄링된 경우에도 동일한 데이터를 사용
—> 이러한 데이터는 어떤 클러스터 노드에서도 접근이 필요해 NAS 유형에 저장돼야 한다.

NFS 볼륨 사용하기

그외로 NFS 볼륨을 사용하는 방법을 소개한다.클러스터가 여러 대의 서버로 실행되는 경우, 외장 스토리지를 볼륨에 마운트하기 위한 옵션을 사용해야 하는데, NFS 서버와 서버에서 익스포트 경로(대체로 /etc/exports 로 지정)를 지정하면 된다고 한다. 이를 통해 서버의 특정 경로로 외부에서 마운트가 가능하게 한다.
$ cat mongodb-pod-nfs.yaml apiVersion: v1 kind: Pod metadata: name: mongodb-nfs spec: volumes: - name: mongodb-data nfs: server: 1.2.3.4 path: /some/path containers: - image: mongo name: mongodb volumeMounts: - name: mongodb-data mountPath: /data/db ports: - containerPort: 27017 protocol: TCP
YAML
복사

기반 스토리지 기술과 파드 분리

파드 개발자가 인프라 세부사항을 걱정하지 않고도 이식 가능하도록,개발자가 애플리케이션에 일정량의 퍼시스턴트 스토리지를 필요로 하면 쿠버네티스에 요청하고,동일하게 파드 생성시 CPU나 메모리와 같은 다른 리소스도 요청이 가능해야 한다.
애플리케이션을 배포하는 개발자는 기저에 어떤 종류의 스토리지 기술이 사용되는지 알 필요가 없어야 하고, 동일한 방식으로 파드를 실행하기 위해 어떤 유형의 물리 서버가 사용되는지 알 필요가 없어야 한다.
인프라스트럭처 관련 처리는 클러스트 관리자마의 영역이어야 한다.

퍼시스턴트 볼륨과 퍼시스턴트 볼륨 클레임

인프라 세부사항을 처리하지 않고 애플리케이션이 스토리지를 요청하는 데에 사용하는 2가지 리소스이다.
1.
클러스터 관리자(비-개발자)가 기반 스토리지를 설정한다(NFS 익스포트와 같은 네트워크 스토리지 유형 설정)
2.
클러스터 관리자가 쿠버네티스 API 서버로 퍼시스턴트 볼륨 리소스를 생성하여 쿠버네티스에 등록한다.(PV 디스크립터를 API 서버에 게시해 PV를 생성)클러스터 관리자는 생성된 퍼시스턴트 볼륨의 크기와 지원가능한 접근 모드를 지정한다.
3.
클러스터 사용자가 퍼시스턴트 볼륨 클레임 매니페스트를 작성해 최소 크기와 필요 접근 모드를 명시한다.
4.
클러스터 사용자가 퍼시스턴트 볼륨 클레임 매니페스트를 API 서버에 게시한다.
5.
쿠버네티스가 적절한 퍼시스턴트 볼륨을 찾아, 클레임에 볼륨을 바인딩한다.
6.
바인딩된 퍼시스턴트 볼륨을 파드 내부 볼륨 중 하나로 사용한다.
7.
볼륨 클레임의 바인딩을 삭제해 릴리스될 때까지 다른 사용자의 해당 볼륨 사용을 금한다.
$ cat mongodb-pv-hostpath.yaml apiVersion: v1 kind: PersistentVolume metadata: name: mongodb-pv spec: capacity: storage: 1Gi // 퍼시스턴트볼륨 사이즈 accessModes: - ReadWriteOnce // 단일 클라이언트의 읽기, 쓰기용 - ReadOnlyMany // 여러 클라이언트의 읽기 전용 persistentVolumeReclaimPolicy: Retain // 클레임 해제 후 퍼시스턴트 볼륨 유지 (삭제 X) hostPath: path: /tmp/mongodb // 이전에 생성한 몽고디비 볼륨 기반 $ kubectl create -f mongodb-pv-hostpath.yaml persistentvolume/mongodb-pv created $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mongodb-pv 1Gi RWO,ROX Retain Available 17s
Plain Text
복사
퍼시스턴트 볼륨은 특정 네임스페이스에 속하지 않는다. 노드와 같은 클러스터 수준 리소스이다.Available 상태는 아직 클레임이 생성되어 바인딩되지 않았음을 의미한다.
볼륨 클레임을 생성하면, 그 조건에 맞는 볼륨을 바로 바인딩한다. (클레임이 있는 네임스페이스의 파드에서만 사용 가능)(볼륨 자체는 클러스터 수준 리소스이므로 특정 네임스페이스 내 생성이 불가하다)
$ cat mongodb-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mongodb-pvc spec: resources: requests: storage: 1Gi accessModes: - ReadWriteOnce storageClassName: "" // 이 부분은 동적 프로비저닝에서 배운다 $ kubectl create -f mongodb-pvc.yaml persistentvolumeclaim/mongodb-pvc created $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mongodb-pv 1Gi RWO,ROX Retain Bound default/mongodb-pvc 3m44s $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mongodb-pvc Bound mongodb-pv 1Gi RWO,ROX 13s
SQL
복사
접근모드는 다음과 같이 동시 사용 가능한 워커 노드 수를 중심으로 구분된다.
RWO, ReadWriteOnce : 단일 노드가 읽기, 쓰기용으로 마운트
ROX, ReadOnlyMany : 다수 노드가 읽기용으로 마운트
RWX, ReadWriteMany : 다수 노드가 읽기, 쓰기용으로 마운트
이제 해당 볼륨을 사용하는 파드를 만들어 데이터를 확인해보자.파드 볼륨에서 퍼시스턴트 볼륨 클레임을 참조하도록 설정한다.
$ cat mongodb-pod-pvc.yaml apiVersion: v1 kind: Pod metadata: name: mongodb spec: containers: - image: mongo name: mongodb volumeMounts: - name: mongodb-data mountPath: /data/db ports: - containerPort: 27017 protocol: TCP volumes: - name: mongodb-data persistentVolumeClaim: claimName: mongodb-pvc // 클레임 참조 $ kubectl create -f mongodb-pod-pvc.yaml pod/mongodb created $ kubectl exec -it mongodb -- mongo MongoDB shell version v5.0.6 ... > use mystore switched to db mystore > db.foo.find() { "_id" : ObjectId("620d25f99f07914ea504c1af"), "name" : "foo" } > exit bye
Plain Text
복사

퍼시스턴트 볼륨 클레임과 퍼시스턴트 볼륨의 장점

애플리케이션 개발자에게 인프라 스토리지를 가져오는 간접적인 방식을 사용하도록 돕는다.볼륨 클레임과 볼륨을 생성하는 추가 절차가 필요한 대신 스토리지 기술을 알지 않아도 된다.또한, 다른 쿠버네티스 클러스터에서도 사용 가능하다. 접근모드와 스토리지 용량만 정해주면 요청이 가능하다.

퍼시스턴트 볼륨 재사용 실험

$ kubectl delete pod mongodb pod "mongodb" deleted $ kubectl delete pvc mongodb-pvc persistentvolumeclaim "mongodb-pvc" deleted $ kubectl get pvc No resources found in default namespace. $ kubectl create -f mongodb-pvc.yaml persistentvolumeclaim/mongodb-pvc created $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mongodb-pvc Pending 3s $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mongodb-pv 1Gi RWO,ROX Retain Released default/mongodb-pvc 14m
Plain Text
복사
클레임과 파드를 삭제한 뒤에 클레임을 재생성하더라도, 기존 퍼시스턴트 볼륨이 해당 클레임과 다시 바인딩되지 않고, Released 상태가 된다. 이미 볼륨을 사용해 데이터를 가지고 있기 때문에, 클러스터 관리자가 기존 볼륨을 완전히 비우지 않으면 새로운 클레임에 바인딩할 수 없다는 것이다.

퍼시스턴트 볼륨 다시 클레임하기

1.
수동으로 클레임하는 방식퍼시스턴트 볼륨이 클레임 해제 시에도 콘텐츠를 유지하게 하는 방법은,persistentVolumeClaimPolicy를 Retain으로 설정하는 방식이다.이럴 경우, 수동으로 재사용하기 위해서는 기존 퍼시스턴트 볼륨 리소스를 삭제 후 재생성해야 한다.
2.
자동으로 클레임하는 방식
a.
Recycle다른 리클레임 정책 중 하나로, 볼륨의 콘텐츠를 삭제하고 리클레임되도록 사용가능하게 만드는 정책이다.여러번 재사용이 가능한 퍼시스턴트 볼륨이 된다.
b.
Delete다른 리클레임 정책 중 하나로 기반 스토리지를 삭제한다.
정책 옵션의 경우 각 상황별로 지원 가능 여부가 상이함에 유의하자.

동적 프로비저닝

클러스터 관리자 입장에서 실제 스토리지를 미리 프로비저닝 해두어야 했는데,퍼시스턴트의 동적 프로비저닝을 활용하여 자동으로 수행하면 클러스터 관리자의 업무 부담도 감소한다.
직접 볼륨을 생성하지 않고, 퍼시스턴트 볼륨 프로비저너를 배포해,사용자가 선택 가능한 볼륨 타입을 하나 이상의 스토리지클래스 오브젝트로 정의하면,사용자가 퍼시스턴트볼륨클레임에서 스토리지클래스를 참조하여,프로비저너가 이를 프로비저닝할 시에 처리한다.
즉, 사용자의 클레임 요청 시마다 자동으로 볼륨을 생성하여, 수급이 원활해진다.
또한, 스토리지 클래스는 클레임 이름으로 스토리지 클래스를 참조하기 때문에, 이름만 동일하면 다른 클러스터 간 이식이 가능하다는 장점이 있다.
$ cat storageclass-fast-hostpath.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast provisioner: k8s.io/minikube-hostpath // 프로비저닝 용 볼륨 플러그인 parameters: // 프로비저너 전달 파라미터 type: pd-ssd
Plain Text
복사
스토리지클래스 리소스가 볼륨 클레임의 요청 시 어떤 프로비저너가 사용될지를 지정한다.
$ kubectl create -f storageclass-fast-hostpath.yaml storageclass.storage.k8s.io/fast created $ cat mongodb-pvc-dp.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mongodb-pvc spec: storageClassName: fast resources: requests: storage: 100Mi accessModes: - ReadWriteOnce
Plain Text
복사
앞서 생성한 스토리지 클래스를 참고하도록 클레임을 작성한다.
$ kubectl create -f mongodb-pvc-dp.yaml persistentvolumeclaim/mongodb-pvc created $ kubectl get pvc mongodb-pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mongodb-pvc Bound pvc-04ef71a7-78a8-4545-9687-8e0ad439a52f 100Mi RWO fast 12s $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mongodb-pv 1Gi RWO,ROX Retain Released default/mongodb-pvc 27m pvc-04ef71a7-78a8-4545-9687-8e0ad439a52f 100Mi RWO Delete Bound default/mongodb-pvc fast 26s
SQL
복사
그뒤 클레임을 생성하면, 스토리지클래스의 정보가 들어간 것을 볼 수 있고, 그에 맞게 퍼시스턴트 볼륨도 생성되었다.

스토리지 클래스를 지정하지 않은 동적 프로비저닝

$ kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE fast k8s.io/minikube-hostpath Delete Immediate false 6m42s standard (default) k8s.io/minikube-hostpath Delete Immediate false 22d $ kubectl get sc standard -o yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"labels":{"addonmanager.kubernetes.io/mode":"EnsureExists"},"name":"standard"},"provisioner":"k8s.io/minikube-hostpath"} storageclass.kubernetes.io/is-default-class: "true" // 스토리지 클래스를 기본값으로 표시한다. 기본 스토리지 클래스는 명시적으로 지정하지 않을 경우 동적 프로비저닝에 사용됨을 의미한다. creationTimestamp: "2022-01-25T08:15:19Z" labels: addonmanager.kubernetes.io/mode: EnsureExists name: standard resourceVersion: "331" uid: f69492e1-37c3-476b-bf64-1e2dbcf1d60d provisioner: k8s.io/minikube-hostpath reclaimPolicy: Delete volumeBindingMode: Immediate
SQL
복사
정의를 보면, 이 스토리지 클래스를 기본값으로 표시한다.기본 스토리지 클래스는 명시적으로 지정하지 않을 경우 동적 프로비저닝에 사용됨을 의미한다.
$ cat mongodb-pvc-dp-nostorageclass.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mongodb-pvc2 spec: resources: requests: storage: 100Mi accessModes: - ReadWriteOnce $ kubectl create -f mongodb-pvc-dp-nostorageclass.yaml persistentvolumeclaim/mongodb-pvc2 created $ kubectl get pvc mongodb-pvc2 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mongodb-pvc2 Bound pvc-44a26cd7-083d-4c82-b044-f22534108fd6 100Mi RWO standard 16s $ kubectl get pv pvc-44a26cd7-083d-4c82-b044-f22534108fd6 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-44a26cd7-083d-4c82-b044-f22534108fd6 100Mi RWO Delete Bound default/mongodb-pvc2 standard 29s
SQL
복사
클레임에 명세되지 않았더니 기본값 스토리지클래스로 동적 프로비저닝이 이루어졌음이 확인 가능하다.

What is CSI Driver?

StorageClass 설명에는 매우 불편한 점을 찾을 수 있습니다. Kubernetes source code 내부에 존재하는 AWS EBS provisioner는 당연히 Kubernetes release lifecycle을 따라서 배포되므로, provisioner 신규 기능을 사용하기 위해서는 Kubernetes version을 업그레이드해야 하는 제약 사항이 있습니다.
따라서, Kubernetes 개발자는 Kubernetes 내부에 내장된 provisioner를 모두 삭제하고, 별도의 controller Pod을 통해 동적 provisioning을 사용할 수 있도록 만들었습니다. 이것이 바로 CSI (Container Storage Interface) driver입니다. AWS EBS 역시 Amazon EBS CSI driver 를 사용하여 동적으로 provisioning 할 수 있습니다. AWS 공식 문서에 따라 Helm chart나 manifest를 통해 CSI driver를 설치하면 몇 개의 workload와 CSIDriver라는 종류의 object가 설치됩니다.
apiVersion: storage.k8s.io/v1 kind: CSIDriver metadata: name: ebs.csi.aws.com spec: attachRequired: true podInfoOnMount: false
YAML
복사
그러면 이제 ebs.csi.aws.com provisioner를 사용하는 StorageClass를 아래와 같이 추가할 수 있습니다. 이후에는 in-tree StorageClass를 사용했던 것처럼 PVC object를 생성하면 됩니다.
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: gp2-csi provisioner: ebs.csi.aws.com parameters: type: gp2 fsType: ext4
YAML
복사
아래 그림은 일반적인 CSI driver의 구조입니다. AWS EBS CSI driver 역시 아래와 같은 구조를 가지는데, 오른쪽 StatefulSet 또는 Deployment로 배포된 controller Pod이 AWS API를 사용하여 실제 EBS volume을 생성하는 역할을 합니다. 왼쪽 DaemonSet으로 배포된 node Pod은 AWS API를 사용하여 Kubernetes node (EC2 instance)에 EBS volume을 attach 해줍니다.
더 자세한 구조와 동작에 대한 설명은 Container Storage Interface의 Design Document를 참고하시기 바랍니다.

참고