[Kubernetes] 쿠버네티스 볼륨(Volume) - PV & PVC
안녕하세요, 달콤한달팽이입니다.🐌🙂
지난 시간에는 hostPath에 대하여 알아보았습니다.
오늘은 PV와 PVC라는 볼륨에 대하여 실습해보도록 하겠습니다.
이번 내용은 조금 길고, 부가적인 내용이 많으므로 차근차근 읽어보시길 바랍니다!
PV & PVC란?
PV 와 PVC
PV와 PVC는 EKS 오브젝트와 별개의 생명주기를 통해 오브젝트 삭제로 인한 데이터 손실을 방지하는 영구 스토리지 제공방법을 의미합니다.
PV는 실제 스토리지 볼륨 그 자체를, PVC는 Pod가 PV를 얻기 위해 수행하는 요청을 의미합니다.
(PVC의 존재 이유는 기본적으로 PV는 직접 Pod에 마운트 할 수 없기 때문입니다!)
때문에 PV는 AWS EFS, EBS, FSx, NFS 등의 스토리지 볼륨을 의미하며,
사용방식에 따라 정적 프로비저닝(Static Provisioning)과 동적 프로비저닝(Dynamic Provisioning)으로 구분됩니다.
정적 프로비저닝(Static Provisioning)과 동적 프로비저닝(Dynamic Provisioning)
정적 프로비저닝이란, PV를 미리 만들어둔 후 PVC와 연결하는 방식입니다.
AWS 공식 홈페이지는 정적 프로비저닝 방식을 위와 같이 설명해 두었습니다.
정적 프로비저닝에 대한 작동 방식을 간단하게 정리하여 기입하면 아래와 같습니다.
1. PV 생성
2. Pod에서 PVC로의 PV 요청
3. PVC에서 요청받은 조건에 부합하는 PV 확인
4. 사전에 생성해둔 PV를 Pod에 전달 및 바인딩
동적 프로비저닝이란, 요청이 있을 때 PV를 동적으로 생성하는 방식입니다.
AWS 공식 홈페이지는 정적 프로비저닝 방식을 위와 같이 설명해 두었습니다.
동적 프로비저닝에 대한 작동 방식을 간단하게 정리하여 기입하면 아래와 같습니다.
1. Pod의 PV 요청
2. PVC에서 요청받은 조건에 부합하는 PV 확인
3. PV에서 볼륨 생성을 위해 스토리지 클래스로 스토리지 정보 요청
4. 스토리지 정보를 받아 PV를 동적으로 생성
5. 생성된 PV를 PVC로 전달 및 Pod에 바인딩
그렇다면 여기서 나오는 스토리지 클래스는 무엇일까요??
스토리지 클래스(Storage Class)?
스토리지 클래스란, 동적 프로비저닝을 관리하는 방법을 정의하는 리소스입니다.
스토리지 클래스에는 PV의 특성, 세부 설정, 바인딩 시점 등을 정의할 수 있습니다.
때문에 스토리지 클래스 오브젝트의 이름은 매우매우 중요합니다!
(스토리지 클래스와 PVC의 이름이 달라서 바인딩 안되는 경우도 많기 때문이죠..)
CSI(Container Storage Interface) Driver
다양한 종류의 스토리지에 종속받지 않고 PV와 PVC를 사용하기 위해선 표준 인터페이스가 필요합니다.
이를 위해 쿠버네티스에서는 스토리지 사용을 위해 필요한 표준 인터페이스를 의미하는 CSI Driver를 지원하고 있으며
앞으로의 실습에서 가장 우선적으로 선행되어야하는 작업이기도 합니다.
이를 통해 쿠버네티스에서는 해당 스토리지에 대한 제어 권한을 가지게 됩니다.
** 주의: 표준 인터페이스이기 때문에 사용에 필요한 스토리지에 따른 CSI Driver를 설치가 필요합니다.
(EFS를 사용할 경우 EFS CSI Driver를, EBS를 사용할 경우 EBS CSI Driver를 설치해야해요!)
PV와 PVC의 생명주기
PV와 PVC는 다음과 같은 생명주기를 가지고 있습니다.
1. Provisioning : PV를 만드는 단계를 의미하며, 정적과 동적 프로비저닝 방식이 있습니다.
2. Binding : Provisioning에서 만든 PV를 PVC와 연결하는 단계입니다. ( PV와 PVC는 1:1 매핑됩니다.)
3. Using : Pod에 PVC를 연결하여 볼륨을 사용하는 단계입니다.
4. Recaliming : 사용이 끝난 PVC를 삭제하고 PV를 초기화하여 반환하는 단계입니다.
PV & PVC 실습 (with AWS EFS)
본격적인 PV와 PVC 실습에 앞서,
이번 실습에서는 AZ 제약이 없는 EFS를 사용하여 구성할 예정이며 아래의 방식을 통해 EFS CSI Driver 구성이 선행되어 있어야합니다.
EFS CSI Driver 설치
1) OIDC 설정
OIDC란, 쿠버네티스에서 AWS 리소스에 접근할 권한을 부여받는 것을 의미합니다.
(자세한 내용은 추후에 다뤄보도록 하겠습니다.)
때문에 아래의 명령어를 사용해 클러스터에 OIDC를 생성해주세요!
$ eksctl utils associate-iam-oidc-provider --cluster test-eks --approve --region ap-northeast-2
2) EFS CSI Driver Policy 생성
쿠버네티스에서 EFS의 AP를 호출할 수 있도록 Policy도 생성해주도록 합시다.
$ curl -o iam-policy-example.json https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/v1.3.2/docs/iam-policy-example.json
$ aws iam create-policy \
--policy-name efs-csi-driver-policy \
--policy-document file://iam-policy-example.json
$ eksctl create iamserviceaccount \
--name efs-csi-controller-sa \
--namespace kube-system \
--cluster test-eks \
--attach-policy-arn arn:aws:iam::{계정 ID}:policy/efs-csi-driver-policy \
--approve \
--override-existing-serviceaccounts \
--region ap-northeast-2
3) EFS CSI Driver 설치(Helm 방식)
이제 Helm을 통해 EFS CSI Driver를 설치하고, 이를 EKS의 Service Account에 연결해주세요.
$ helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
$ helm repo update
$ helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
--namespace kube-system \
--set image.repository=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/aws-efs-csi-driver \
--set controller.serviceAccount.create=false \
--set controller.serviceAccount.name=efs-csi-controller-sa
4) EFS CSI Driver 설치 확인
위의 모든 과정이 정상적으로 수행되었다면, 마지막으로 EFS CSI Driver가 정상적으로 설치되었는지 확인해주세요!
$ kubectl get pods -n kube-system | grep efs-csi
위 과정이 모두 정상적으로 수행되었다면 이제 본격적으로 PV, PVC 실습을 진행해보도록 하겠습니다.
(EFS가 주요 주제는 아니므로 EFS 생성 과정은 건너뛰었습니다.)
정적 프로비저닝(Static Provisioning)
1. PV, PVC 생성
아래를 참고하여 PV와 PVC를 생성해주세요
PV는 스토리지에 대한 정보(EFS)와 사용량(5Gi), 엑세스 모드(ReadWriteMany) 등이 정의되어 있습니다.
[pv.yaml]
apiVersion: v1
kind: PersistentVolume
metadata:
name: efs-static-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: efs-static
csi:
driver: efs.csi.aws.com
volumeHandle: {EFS ID}
PVC에는 PV와 연결을 위한 정보가 기입되어 있습니다.
[pvc.yaml]
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-static-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: efs-static
2. Pod 생성
Pod는 컨테이너에 대한 정보와, 앞서 생성한 스토리지를 어디에 마운트 할 것인지, 어떤 PVC를 사용할 것인지 기입합니다.
[efs-static-pod.yaml]
apiVersion: v1
kind: Pod
metadata:
name: efs-static-pod
spec:
containers:
- name: efs-container
image: nginx
volumeMounts:
- mountPath: "/mnt/efs"
name: efs-volume
volumes:
- name: efs-volume
persistentVolumeClaim:
claimName: efs-static-pvc
3. 결과 확인
우선 PV와 PVC가 잘 연결되어있는지 확인해주도록 합시다.
$ kubectl get pv,pvc
컨테이너 내부에서도 잘 마운트 되어있는지 확인해봅시다.
$ kubectl exec -it efs-static-pod -- sh -c "df -hT"
컨테이너 내부에서 생성한 파일이 잘 공유되었는지도 확인해볼까요?
$ df -hT
아주 정상적으로 잘 연결되었음을 확인할 수 있습니다.
이로써 정적 프로비저닝(Static Provisioning) 실습은 성공입니다!
이제 pv, pvc, pod를 삭제하고 동적 프로비저닝(Dynamic Provisioning)을 실습해보도록 합시다.
동적 프로비저닝(Dynamic Provisioning)
1. 스토리지 클래스 정의
아래를 참고하여 스토리지 클래스를 생성해주세요.
스토리지 클래스에서는 이름과 EFS의 ID에 주의하여 기입해주세요!
[storageclass.yaml]
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: {EFS ID}
directoryPerms: "700"
2. PVC 정의
다음으로는 아래를 참고하여 PVC를 생성해주세요.
여기서는 storageClassName의 값이 스토리지 클래스 이름과 동일하도록 설정해주세요!
[pvc.yaml]
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
storageClassName: efs-sc
3. Deployment 정의
마지막으로는 아래를 참고하여 Deployment를 생성해주세요.
여기선 claimName의 값이 PVC의 이름과 동일하도록 설정해주세요!
[efs-dynamic-dp.yaml]
apiVersion: apps/v1
kind: Deployment
metadata:
name: efs-app
spec:
replicas: 3
selector:
matchLabels:
app: efs-app
template:
metadata:
labels:
app: efs-app
spec:
containers:
- name: efs-container
image: nginx
volumeMounts:
- mountPath: /mnt/efs
name: efs-storage
volumes:
- name: efs-storage
persistentVolumeClaim:
claimName: efs-pvc
4. 결과 확인
이제 위의 파일들을 모두 배포 한 후, 결과를 확인해보도록 합시다!
우선 PV와 PVC가 정상적으로 배포되었는지 확인해주세요.
STATUS가 Bound 상태라면 성공입니다!
$ kubectl get pv,pvc
이제 PVC와 연결된 Pod이 잘 배포되었는지 확인해주세요.
Deployment에 명시한 것과 같이 3개의 pod이 떴으면 성공입니다!
$ kubectl get pod -A
이제 각각의 Pod에 EFS가 잘 마운트 되었는지 확인해볼까요?
exec 명령어를 통해 각 Pod에 연결된 스토리지를 확인하고, EFS가 포함되어 있는지 확인해주세요!
(아래 명령어에서 Pod의 이름은 다를 수 있으니 자신의 Pod에 맞는 이름으로 변경해주세요.)
$ kubectl exec -it efs-app-56c97d8f7d-6mttg -- sh -c "df -hT"
$ kubectl exec -it efs-app-56c97d8f7d-9ktpl -- sh -c "df -hT"
아주 정상적으로 잘 연결되었음을 확인할 수 있습니다.
이로써 동적 프로비저닝(Dynamic Provisioning) 실습도 성공했습니다!
지금까지 PV, PVC를 사용하여 동적/정적 프로비저닝 방식에 대하여 알아보았습니다!
(... 그런데 이제 EFS를 곁들인..)
긴 글 읽어주셔서 감사합니다!