AWES 3주차 - EKS Storage: EFS CSI 정적, 동적 프로비저닝 실습

박민준·2025년 2월 22일

3주차에서는 EKS Storage를 알아보고, EFS CSI를 통해 정적, 동적 프로비저닝을 구현하는 방식에 관련된 내용들을 학습하고 정리한 내용들을 담았습니다.

K8s storage의 적용 배경 - Link

여러 환경에서 스토리지 관리는 매우 중요하며, K8s 환경에서도 마찬가지 입니다.

애플리케이션을 구동할 Pod에는 Container를 비롯한 여러 정보들이 담겨 있습니다.
이때, 영구 스토리지가 Pod에 연결되어 있지 않다면 어떨 까요?
Pod는 수명 주기에 따라 종료되며, Pod 내의 정보들은 소멸될 것입니다.

❗Pod 자체는 무엇이다? 상태가 없는 Stateless입니다. 즉, 아래 이미지 중 오른쪽 Pod와 같이 임시 볼륨을 사용한다로 이해할 수 있습니다! - Temporary filesystem, Ephemeral Volume

https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/

그렇다면, 임시 볼륨에 대해서 조금만 더 알아보도록 하겠습니다.

임시 볼륨의 종류

쿠버네티스는 각 목적에 맞는 몇 가지의 임시 볼륨을 지원합니다.

emptyDir: 파드가 시작될 때 빈 상태로 시작되며, 저장소는 로컬의 kubelet 베이스 디렉터리(보통 루트 디스크) 또는 램에서 제공됩니다.
configMap, downwardAPI, secret: 각 종류의 쿠버네티스 데이터를 파드에 주입합니다.
CSI 임시 볼륨: 앞의 볼륨 종류와 비슷하지만, 특히 이 기능을 지원하는 특수한 CSI 드라이버에 의해 제공됩니다.
일반(generic) 임시 볼륨: 퍼시스턴트 볼륨도 지원하는 모든 스토리지 드라이버에 의해 제공될 수 있습니다.

다시 K8s storage의 적용 배경으로 돌아와서, Pod에 일시적인 스토리지가 아닌, 영구 스토리지를 적용하려면 어떻게 해야할까요?

스토리지 요구 사항

데이터 손실을 방지하고 Kubernetes에서 상태 저장 애플리케이션을 실행하려면 세 가지 간단한 스토리지 요구 사항을 준수해야 합니다.

  • 스토리지는 Pod의 수명 주기에 의존해서는 안 됩니다.
  • 스토리지는 K8s 클러스터의 모든 Pod 및 Node에서 사용할 수 있어야 합니다.
  • 스토리지는 충돌이나 애플리케이션 오류에 관계없이 가용성이 높아야 합니다.

❗즉, Pod와 스토리지는 분리되어야 하는 것입니다!

스토리지와 포드 분리: 영구 볼륨(Persistent Volumes)

PV는 Kubernetes Pods의 수명 주기와 별개로 자체의 수명 주기를 가집니다.

PV는 기본적으로 두 가지로 구성됩니다.

  • PersistentVolume이라고 불리우는 백엔드 기술
  • Kubernetes에 볼륨을 마운트하는 방법을 알려주는 접근 모드

백엔드 기술(Backend technology)

PV는 추상적인 구성 요소이며, 실제 물리적 스토리지는 어딘 가에서 가져와야 합니다.

PV는 EKS에서 주로 사용되는 csi(EBS, EFS, FSx)부터 iscsi, local, nfs등을 지원합니다.

❗Kubernetes는 연결되는 스토리지 자체에는 신경을 쓰지 않습니다. PV구성 요소를 실제 저장소에 대한 인터페이스로 제공할 뿐입니다.

접근 모드(Access mode)

접근 모드는 PV가 생성될 때 설정되며 Kubernetes가 볼륨을 마운트하는 방법을 알려줍니다.

PVC에서 접근 모드는 사용자가 어떤 방식으로 PV에 접근할 수 있는지를 정의합니다.
예를 들어, ReadWriteOnce, ReadOnlyMany, ReadWriteMany와 같은 접근 모드를 설정할 수 있습니다.

영구 볼륨 클레임(Persistent volume claims)

백엔드 기술, 접근 모드를 정의하여 PV를 생성하였습니다.

❓이때, Pod는 생성된 PV에 어떻게 스토리지 사용 요청을 보낼 수 있게 되는 것일까요?
: PersistentVolumeClaim(PVC)를 통해 가능합니다.

PV, PVC 간단 정리

  • PV 는 실제 스토리지 볼륨을 나타내며, PVC는 Pod가 실제 스토리지를 얻기 위해 수행하는 스토리지 요청을 나타냅니다.

기본적으로 PV 객체를 직접 Pod에 마운트할 수 없습니다. 이것은 명시적으로 요청되어야 하며, PVC를 통해 가능합니다.
여기서 중요한 점은 PVC와 PV는 1:1의 관계가 있습니다(PV는 하나의 PVC에만 연결될 수 있음).

아래는 EBS 볼륨을 통한 PV 생성과 PVC를 통한 연결 yaml 파일 예 입니다.

  • AWS EBS는 ReadWriteOnce 접근 모드만을 지원합니다, 즉 하나의 Pod에서만 읽고 쓸 수 있는 방식입니다.

PersistentVolume (PV) 정의 (AWS EBS 사용)

apiVersion: v1
kind: PersistentVolume
metadata:
  name: ebs-pv
spec:
  capacity:
    storage: 10Gi  # 스토리지 용량
  volumeMode: Block  # EBS는 블록 스토리지이므로 Block 모드로 설정
  accessModes:
    - ReadWriteOnce  # 하나의 Pod에서만 읽고 쓸 수 있는 모드
  persistentVolumeReclaimPolicy: Retain  # PV를 삭제할 때 정책
  storageClassName: ebs-sc  # 스토리지 클래스
  awsElasticBlockStore:
    volumeID: vol-xxxxxxxx  # EBS 볼륨 ID
    fsType: ext4  # 파일 시스템 유형

PersistentVolumeClaim (PVC) 정의 (EBS 볼륨 요청)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-pvc
spec:
  accessModes:
    - ReadWriteOnce  # PVC에서 요구하는 접근 모드
  resources:
    requests:
      storage: 5Gi  # PVC에서 요청하는 스토리지 용량
  storageClassName: ebs-sc  # PV와 연결된 storage class

< PV >
awsElasticBlockStore 섹션을 통해 AWS EBS 볼륨을 지정합니다. volumeID에는 실제 EBS 볼륨의 ID를 입력해야 합니다.
accessModes는 ReadWriteOnce로 설정되어 하나의 Pod만 EBS에 접근할 수 있습니다.
< PVC >
PVC에서 **accessModes**ReadWriteOnce로 설정하여, 이 PVC가 연결될 PV가 동일한 접근 모드를 가져야 합니다.
**resources.requests.storage**에는 PVC가 요청하는 스토리지 용량을 설정하며, 이 용량은 PV에서 제공하는 용량과 일치해야 합니다.
**storageClassName**을 통해 EBS와 연결된 스토리지 클래스를 지정합니다.

❓그렇다면, 하나의 스토리지에 여러 Pod가 접근해야 한다면 어떻게 해야할까요?
: EFS를 사용하셔야 합니다. EFS는 접근 모드를 ReadWriteMany로 설정이 가능하기 때문입니다

<추가 참고>

  • 파드가 생성될 때 자동으로 볼륨을 마운트하여 파드에 연결하는 기능을 동적 프로비저닝(Dynamic Provisioning) 이라고 합니다.
  • PV의 사용이 끝났을 때 해당 볼륨은 어떻게 초기화할 것인지 별도로 설정할 수 있는데, 쿠버네티스는 이를 Reclaim Policy 라고 부릅니다.
  • Reclaim Policy 에는 크게 Retain(보존), Delete(삭제, 즉 EBS 볼륨도 삭제됨), Recycle 방식이 있습니다.
  • Recycle 방식은 현재로는 사용되지 않으며, 동적 프로비저닝 방식을 권장한다고 합니다.

CSI(Container Storage Interface) 드라이버

  • CSI 드라이버는 Kubernetes에서 위에서 설명된, 다양한 스토리지 솔루션을 쉽게 사용할 수 있도록 설계되었으며, 프로비저닝 방식은 정적, 동적으로 나뉩니다.

정적 프로비저닝(Static provisioning)

예시: 관리자가 하나 이상의 PV를 생성하고 애플리케이션 개발자는 PVC를 생성하여 스토리지 사용을 요청합니다.
위와 같이 PV 및 PVC를 수동으로 만들어야하므로 정적입니다. 대규모 환경에서는 관리의 복잡성을 증대됩니다.

동적 프로비저닝(Dynamic provisioning)

  • 동적 프로비저닝을 사용하면 PV객체를 생성할 필요가 없으며, PVC를 생성할 때 K8s 내부에서 자동으로 생성됩니다.
  • Storage Class라는 객체를 사용하여 동적 프로비저닝을 수행합니다.

AWS EFS Controller - Link

여러 많은 CSI 드라이버 중, EFS CSI 드라이버를 활용하는 방식을 정적/동적 프로비저닝 각각 알아보도록 하겠습니다.

Amazon EFS를 사용한 정적 프로비저닝

가장 먼저, EFS를 활용하기 위해서는 EFS 생성이 필요합니다.

  • EFS 명을 정의하고, PV 생성에 사용될 "FileSystemId"를 기억합니다.

아래와 같이 "FileSystemId"를 포함한 PV manifast 파일을 작성합니다.

❓EFS의 용량(capacity)를 5Gi로 명시해뒀기에, 5Gi까지만 사용이 가능할까요?
❗EFS는 탄력/확장이 가능한 filesystem이기에 사용량에 따라 자동으로 확장/축소가 됩니다.
즉, 5Gi로 기재하였지만, 이는 파일을 위한 요소일 뿐, 크게 의미가 있는 사항은 아닙니다.

#pv.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: efs-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: efs.csi.aws.com
    volumeHandle: fs-073d77123471b2917

apply를 통해 PV를 생성합니다.

  • Status가 Available이며 Claim 비어있듯이, PVC에 바인딩이 되지 않은 상태입니다.
$ kubectl apply -f pv.yaml
persistentvolume/efs-pv created

$ kubectl get pv efs-pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
efs-pv   5Gi        RWO            Retain           Available                                   45s

PV와 바인딩을 하기 위한 PVC manifast 파일을 작성 및 생성합니다.

#pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ""
  resources:
    requests:
      storage: 5Gi
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/efs-claim created

PV 및 PVC 상태를 확인한 결과, Status가 Bound로 변경되었으며, Claim또한 efs-claim로 변경되었습니다.

$ kubectl get pv efs-pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
efs-pv   5Gi        RWO            Retain           Bound    default/efs-claim                           15m

$ kubectl get pvc efs-claim
NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
efs-claim   Bound    efs-pv   5Gi        RWO                           103s

Static Provisioning에 대한 요약

만약, 현 상황에서 또 다른 PVC를 생성한다면 어떻게 될까요?
PV는 하나만 생성했으며, 이미 바인딩되어 사용 중이기 때문에 실패합니다.
이전에 언급했듯이, 하나의 PV는 하나의 PVC에 바인딩될 수 있기 때문입니다.

❗해당 상황과 같을 때, 동적 프로비저닝은 빛을 발휘합니다!

Amazon EFS를 사용한 동적 프로비저닝

EFS의 경우, 동적 프로비저닝은 내부적으로 각각의 PV에 대한 액세스 포인트를 생성합니다.
즉, Amazon EFS 파일 시스템을 수동으로 생성하고 이를 Storage Class 파라미터에 대한 입력으로 제공해야 합니다.

기본적으로 동적 프로비저닝을 통해 생성된 각 액세스 포인트는 EFS의 다른 디렉터리에 파일을 쓰고, 각 액세스 포인트는 다른 POSIX uid/gid를 사용하여 EFS에 파일을 쓰게 됩니다.
이를 통해 여러 애플리케이션이 동일한 EFS 볼륨을 영구 스토리지로 사용하면서, 동시에 애플리케이션 간의 격리를 제공할 수 있게 됩니다.

  • 샘플 app(efs-app)을 생성하여 추가적으로 동적 프로비저닝에 대해서 알아보겠습니다.
#pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: efs-app
spec:
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 2; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim

kubectl을 사용하여 Amazon EFS 파일 시스템에 데이터가 기록되었는지 확인할 수 있습니다.

$ kubectl exec -ti efs-app -- tail -f /data/out.txt
Fri Feb 21 10:33:05 UTC 2025
Fri Feb 21 10:33:07 UTC 2025

StorageClass를 생성하고, "FileSystemId"를 기재합니다.

  • 새로운 EFS 볼륨을 생성하여 진행하거나, 기존의 PV, PVC 리소스들은 삭제가 필요합니다.
#sc.yaml
----
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  fileSystemId: fs-073d123fcb71b2917
  directoryPerms: "700"

Storage Class를 생성하고 확인합니다.

$ kubectl apply -f sc.yaml
storageclass.storage.k8s.io/efs-sc created

$ kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
efs-sc          efs.csi.aws.com         Delete          Immediate              false                  4s
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  2d5h

아래와 같이 애플리케이션을 배포하기 전에 PV를 생성할 필요가 없습니다.
따라서, PVC와 Pod를 계속해서 만들 수 있습니다.

#pvc_pod_1.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim-1
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: efs-app-1
spec:
  containers:
    - name: app
      image: centos
      command: ["/bin/sh"]
      args: ["-c", "while true; do echo $(date -u) >> /data/out; sleep 5; done"]
      volumeMounts:
        - name: persistent-storage
          mountPath: /data
  volumes:
    - name: persistent-storage
      persistentVolumeClaim:
        claimName: efs-claim-1

PVC 와 Pod를 배포해 보겠습니다.
efs-app-1 Pod가 정상적으로 실행중이며, PV는 EFS CSI 드라이버에 의해 자동으로 생성되었습니다!

$ kubectl apply -f pvc_pod_1.yaml
persistentvolumeclaim/efs-claim-1 created
pod/efs-app-1 created

$ kubectl get pvc
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
efs-claim-1   Bound    pvc-82da05b5-ff85-40a5-8135-50428480fd22   5Gi        RWX            efs-sc         89s

$ kubectl get pv | grep efs-sc
pvc-7994fdce-5711-4346-aefb-85e10155ec7c   5Gi        RWX            Delete

$ kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
efs-app-1   1/1     Running   0          80s

$ kubectl exec -ti efs-app-1 -- tail -f /data/out
Fri Feb 21 11:20:04 UTC 2025
Fri Feb 21 11:20:04 UTC 2025

Console에서 해당 액세스 포인트를 볼 수 있습니다.

  • efs-app-2, efs-app-3... 과 같이 계속적으로 Pod, PVC를 생성해도 PV를 1대1 매칭으로 생성할 필요가 없습니다.

Dynamic Provisioning 대한 요약

profile
바교망

0개의 댓글