3주차에서는 EKS Storage를 알아보고, EFS CSI를 통해 정적, 동적 프로비저닝을 구현하는 방식에 관련된 내용들을 학습하고 정리한 내용들을 담았습니다.
여러 환경에서 스토리지 관리는 매우 중요하며, K8s 환경에서도 마찬가지 입니다.
애플리케이션을 구동할 Pod에는 Container를 비롯한 여러 정보들이 담겨 있습니다.
이때, 영구 스토리지가 Pod에 연결되어 있지 않다면 어떨 까요?
Pod는 수명 주기에 따라 종료되며, Pod 내의 정보들은 소멸될 것입니다.
❗Pod 자체는 무엇이다? 상태가 없는 Stateless입니다. 즉, 아래 이미지 중 오른쪽 Pod와 같이 임시 볼륨을 사용한다로 이해할 수 있습니다! - Temporary filesystem, Ephemeral Volume

그렇다면, 임시 볼륨에 대해서 조금만 더 알아보도록 하겠습니다.
쿠버네티스는 각 목적에 맞는 몇 가지의 임시 볼륨을 지원합니다.
emptyDir: 파드가 시작될 때 빈 상태로 시작되며, 저장소는 로컬의 kubelet 베이스 디렉터리(보통 루트 디스크) 또는 램에서 제공됩니다.
configMap, downwardAPI, secret: 각 종류의 쿠버네티스 데이터를 파드에 주입합니다.
CSI 임시 볼륨: 앞의 볼륨 종류와 비슷하지만, 특히 이 기능을 지원하는 특수한 CSI 드라이버에 의해 제공됩니다.
일반(generic) 임시 볼륨: 퍼시스턴트 볼륨도 지원하는 모든 스토리지 드라이버에 의해 제공될 수 있습니다.
다시 K8s storage의 적용 배경으로 돌아와서, Pod에 일시적인 스토리지가 아닌, 영구 스토리지를 적용하려면 어떻게 해야할까요?
데이터 손실을 방지하고 Kubernetes에서 상태 저장 애플리케이션을 실행하려면 세 가지 간단한 스토리지 요구 사항을 준수해야 합니다.
❗즉, Pod와 스토리지는 분리되어야 하는 것입니다!
PV는 Kubernetes Pods의 수명 주기와 별개로 자체의 수명 주기를 가집니다.
PV는 기본적으로 두 가지로 구성됩니다.
PV는 추상적인 구성 요소이며, 실제 물리적 스토리지는 어딘 가에서 가져와야 합니다.
PV는 EKS에서 주로 사용되는 csi(EBS, EFS, FSx)부터 iscsi, local, nfs등을 지원합니다.
❗Kubernetes는 연결되는 스토리지 자체에는 신경을 쓰지 않습니다. PV구성 요소를 실제 저장소에 대한 인터페이스로 제공할 뿐입니다.
접근 모드는 PV가 생성될 때 설정되며 Kubernetes가 볼륨을 마운트하는 방법을 알려줍니다.
PVC에서 접근 모드는 사용자가 어떤 방식으로 PV에 접근할 수 있는지를 정의합니다.
예를 들어, ReadWriteOnce, ReadOnlyMany, ReadWriteMany와 같은 접근 모드를 설정할 수 있습니다.
백엔드 기술, 접근 모드를 정의하여 PV를 생성하였습니다.
❓이때, Pod는 생성된 PV에 어떻게 스토리지 사용 요청을 보낼 수 있게 되는 것일까요?
: PersistentVolumeClaim(PVC)를 통해 가능합니다.
기본적으로 PV 객체를 직접 Pod에 마운트할 수 없습니다. 이것은 명시적으로 요청되어야 하며, PVC를 통해 가능합니다.
여기서 중요한 점은 PVC와 PV는 1:1의 관계가 있습니다(PV는 하나의 PVC에만 연결될 수 있음).

아래는 EBS 볼륨을 통한 PV 생성과 PVC를 통한 연결 yaml 파일 예 입니다.
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로 설정이 가능하기 때문입니다
<추가 참고>
예시: 관리자가 하나 이상의 PV를 생성하고 애플리케이션 개발자는 PVC를 생성하여 스토리지 사용을 요청합니다.
위와 같이 PV 및 PVC를 수동으로 만들어야하므로 정적입니다. 대규모 환경에서는 관리의 복잡성을 증대됩니다.

여러 많은 CSI 드라이버 중, EFS CSI 드라이버를 활용하는 방식을 정적/동적 프로비저닝 각각 알아보도록 하겠습니다.
가장 먼저, EFS를 활용하기 위해서는 EFS 생성이 필요합니다.

아래와 같이 "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를 생성합니다.
$ 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에 바인딩될 수 있기 때문입니다.
❗해당 상황과 같을 때, 동적 프로비저닝은 빛을 발휘합니다!
EFS의 경우, 동적 프로비저닝은 내부적으로 각각의 PV에 대한 액세스 포인트를 생성합니다.
즉, Amazon EFS 파일 시스템을 수동으로 생성하고 이를 Storage Class 파라미터에 대한 입력으로 제공해야 합니다.
기본적으로 동적 프로비저닝을 통해 생성된 각 액세스 포인트는 EFS의 다른 디렉터리에 파일을 쓰고, 각 액세스 포인트는 다른 POSIX uid/gid를 사용하여 EFS에 파일을 쓰게 됩니다.
이를 통해 여러 애플리케이션이 동일한 EFS 볼륨을 영구 스토리지로 사용하면서, 동시에 애플리케이션 간의 격리를 제공할 수 있게 됩니다.
#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"를 기재합니다.
#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에서 해당 액세스 포인트를 볼 수 있습니다.

Dynamic Provisioning 대한 요약
