스터디/AEWS

[AEWS] 3주차 EKS에서 CSI Driver를 통한 AWS 스토리지 사용하기 (EBS, EFS)

안녕유지 2025. 2. 23. 05:11
Cloudnet AWES 3주차 스터디를 진행하며 정리한 글입니다.

 

지난 시간에는 쿠버네티스에서 사용하는 스토리지에 대해 알아보았습니다.

이번 시간에는 AWS EKS에서 다양한 종류의 CSI Driver를 활용하여 AWS의 스토리지를 사용하는 방법에 대해 알아보겠습니다.

 

우선 AWS 환경에 대표적인 스토리지를 알아보겠습니다.

 

AWS 스토리지

AWS EBS (AWS Elastic Block Storage)

  • AWS 블록 스토리지로, EC2에서 디스크처럼 연결하여 사용하는 블록 스토리지입니다.
  • EC2 특정 노드에 종속 되어서 여러 노드에서 공유가 불가능하고, ReadWriteOnce(RWO) 모드만 지원하여 하나의 Pod에서만 접근이 가능합니다. AZ가 제한되어 하나의 AZ 내에서만 접근 가능합니다 
  • 높은 IOPS와 낮은 지연 시간 제공하여 성능이 좋습니다.
  • 스냅샷을 통해 백업이 가능합니다.

 

 

AWS EFS (AWS File System)

  • NFS 기반 AWS 확장 가능한 네트워크 파일 시스템입니다.
  • 다중 AZ를 지원하여 여러 노드에서 동시에 마운트 가능합니다.
  • 사전 할당 필요 없이 자동 확장이 가능합니다.
  • 파일 단위 공유가 가능합니다.
  • EBS보다 높은 지연 시간으로 비교적 낮은 성능을 가집니다.

 

 

AWS S3 (Simple Storage Service)

  • AWS 오브젝트 스토리지입니다. (파일 단위 저장, 계층적 디렉토리 X)
  • 전 세계에서 접근 가능하고, 무제한 확장 가능합니다.
  • EBS, EFS보다 저렴합니다.
  • REST API 기반 접근을 합니다. (POSIX 파일 시스템 아님)

 

CSI Driver

지난 포스팅에서 CSI란 쿠버네티스를 비롯한 컨테이너 오케이스트레이션 도구와 다양한 스토리지 백엔드를 연결하는 표준 인터페이스라고 소개했었는데요. 

 

이러한 CSI 표준을 구현한 스토리지 프로바이더 (Storage Provider) 플러그인이 CSI Driver입니다.

CSI Driver는 스토리지 종류 별로 AWS EBS, AWS EFS, Ceph, NFS 등 다양한 스토리지를 지원하고, 쿠버네티스에서 스토리지 프로비저닝, 볼륨 연결, 마운트, 스냅샷 등의 기능을 제공합니다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com  # <- CSI Driver
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3

 

CSI Driver의 동작 과정

  1. 사용자가 PersistentVolumeClaim (PVC) 를 생성
  2. PVC가 StorageClass와 연결되어 있으면, Kubernetes가 CSI Driver에게 볼륨을 생성하라고 요청
  3. CSI Controller가 스토리지 백엔드(EBS, EFS 등)에 볼륨을 생성

CSI Driver의 주요 컴포넌트

  • CSI Controller Plugin : 볼륨 생성, 삭제, 노드에 Attach, Detach
  • CSI Node Plugin : 볼륨을 해당 노드에 Mount, Unmount

 

 

AWS EBS Controller

EKS에서 EBS를 스토리지로 사용하기 위해서는 EBS CSI Driver 설치가 필요합니다.

 

AWS EBS Controller의 역할

1. 동적 볼륨 생성 

  • PVC를 요청하면 자동으로 EBS 볼륨 생성
  • StorageClass에 정의된 volumeBindingMode를 기반으로 볼륨 프로비저닝

2. 볼륨 연결 및 해제

  • EBS 볼륨을 특정 노드 (워커 노드 EC2)에 연결, 분리 

3. 스냅샷 생성 및 복원

  • EBS 볼륨의 스냅샷 생성하고 새로운 볼륨으로 복구 가능

 

AWS EBS CSI Driver 구성 요소

AWS EBS CSI Driver는 크게 2개 구성요소가 있습니다.

 

EBS CSI controller

  • PVC 요청 시 EBS 볼륨 동적 생성/삭제
  • EBS 볼륨을 EC2 노드에 Attach/Detach
  • Kubernetes 컨트롤 플레인 노드에서 작동 / EKS의 경우에는 kube-system 네임스페이스에서 동작

EBS CSI Node

  • EBS 볼륨을 Pod에 마운트 및 언마운트
  • Pod가 생성되면 볼륨을 마운트하고, 삭제되면 언마운트
  • 각 워커 노드에서 실행되며 kubelet과 상호작용

 

AWS API를 호출하면서 AWS 스토리지를 관리하는 CSI-Controller와 kubelet과 상호작용하면서 AWS스토리지를 pod에 마운트하는 CSI-Node가 있습니다. 

이 때, EBS는 블록 스토리지 이미로, 하나의 노드에서만 마운트가 가능하여, 한 번에 하나의 EC2 인스턴스에만 attach가 가능합니다. 따라서 ReadWriteOnce만 지원됩니다. (=즉, 한 개의 Pod만 볼륨을 마운트 할 수 있습니다.)

또한, EBBS는 하나의 가용 영역에 물리적으로 배치 되기 때문에, 동일한 AZ에 있는 인스턴스에만 연결이 가능합니다. 

 

이제 실제로, Pod가 EBS 볼륨을 사용하도록 실습을 진행해보겠습니다.

aws-ebs-csi-driver 전체 버전 정보와 기본 설치 버전(True) 정보 확인합니다.

❯ aws eks describe-addon-versions \
    --addon-name aws-ebs-csi-driver \
    --kubernetes-version 1.31 \
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
    --output text

v1.39.0-eksbuild.1
True
v1.38.1-eksbuild.2
False
v1.38.1-eksbuild.1
False
v1.37.0-eksbuild.2
False
v1.37.0-eksbuild.1
False
v1.36.0-eksbuild.2
False
v1.36.0-eksbuild.1
False
v1.35.0-eksbuild.2
False
v1.35.0-eksbuild.1
False
v1.34.0-eksbuild.1
False
v1.33.0-eksbuild.1
False
v1.32.0-eksbuild.1
False
v1.31.0-eksbuild.1
False
v1.30.0-eksbuild.1
False
v1.29.1-eksbuild.1
False

 

ISRA 설정(AmazonEBSCSIDriverPolicy)을 사용하고 ISRA를 확인합니다.

AmazonEKS_EBS_CSI_DriverRole이라는 IAM 역할을 생성하고, AmazonEBSCSIDriverPolicy 정책을 해당 IAM 역할에 부여합니다. ebs-csi-controller-sa 서비스 계정이 EBS API를 호출할 수 있도록 설정합니다.

 

++ 이때 ISRA란 무엇일까요?

IAM Service Role for Amazone EKS의 약자로, EKS 클러스터에서 특정 서비스가 AWS 리소스에 안전하게 접근할 수 있도록 IAM 역할을 부여하는 방식입니다.

IAM 역할을 쿠버네티스 ServiceAccount와 연결하여 AWS 리소스에 대한 권한을 제어하는 기능입니다.

 

Kubernetes Service Account

- K8s 클러스터 내부에서 Pod와 API 서버가 상화작용하는데 작용

- Pod가 Kubernetes API를 호출할 때 해당 SA권한 사용

 

IAM Service Account (ISRA)

- AWS IAM 역할을 K8s Service Account에 연결하여 AWS 리소스에 안전하게 접근할 수 있도록 하는 기능

- Pod가 AWS API를 안정하게 호출할 수 있도록 역할을 부여하는 방식

❯ eksctl create iamserviceaccount \                                                                              
  --name ebs-csi-controller-sa \ # 서비스 계정 이름 (K8s에서 사용할 이름)
  --namespace kube-system \ 
  --cluster ${CLUSTER_NAME} \
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \ # AWS 관리형 정책 연결
  --approve \ # 즉시 적용
  --role-only \ # IAM Role만 생성하고, K8s 서비스 계정을 따로 있음
  --role-name AmazonEKS_EBS_CSI_DriverRole # IAM 역할 지정
  
❯ eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}                                                         
NAMESPACE	NAME				ROLE ARN
kube-system	aws-load-balancer-controller	arn:aws:iam::941377114730:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-yq8VPD5xPBny
kube-system	ebs-csi-controller-sa		arn:aws:iam::941377114730:role/AmazonEKS_EBS_CSI_DriverRole

❯ kubectl get sa -A | grep ebs

 

Amazon EBS CSI driver addon 배포합니다.

❯ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)

❯ eksctl create addon --name aws-ebs-csi-driver --cluster ${CLUSTER_NAME} --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EBS_CSI_DriverRole --force

❯ kubectl get sa -n kube-system ebs-csi-controller-sa -o yaml | head -5
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
  annotations:
  
❯ eksctl get addon --cluster ${CLUSTER_NAME}
NAME			VERSION			STATUS	ISSUES	IAMROLE										UPDATE AVAILABLE	CONFIGURATION VALUES		POD IDENTITY ASSOCIATION ROLES
aws-ebs-csi-driver	v1.39.0-eksbuild.1	ACTIVE	0	arn:aws:iam::941377114730:role/AmazonEKS_EBS_CSI_DriverRole
coredns			v1.11.4-eksbuild.2	ACTIVE	0
kube-proxy		v1.31.3-eksbuild.2	ACTIVE	0
metrics-server		v0.7.2-eksbuild.2	ACTIVE	0
vpc-cni			v1.19.2-eksbuild.5	ACTIVE	0	arn:aws:iam::941377114730:role/eksctl-myeks-addon-vpc-cni-Role1-cSbYmEhHAYbN	enableNetworkPolicy: "true"

❯ kubectl get deploy,ds -l=app.kubernetes.io/name=aws-ebs-csi-driver -n kube-system                      
NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ebs-csi-controller   2/2     2            2           5m50s

NAME                                  DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR              AGE
daemonset.apps/ebs-csi-node           3         3         3       3            3           kubernetes.io/os=linux     5m50s
daemonset.apps/ebs-csi-node-windows   0         0         0       0            0           kubernetes.io/os=windows   5m51s

❯ kubectl get pod -n kube-system -l app.kubernetes.io/component=csi-driver                             
NAME                                 READY   STATUS    RESTARTS   AGE
ebs-csi-controller-7f8f8cb84-9v5mq   6/6     Running   0          5m39s
ebs-csi-controller-7f8f8cb84-hnkzb   6/6     Running   0          5m39s
ebs-csi-node-5zjzp                   3/3     Running   0          5m39s
ebs-csi-node-bnxtr                   3/3     Running   0          5m39s
ebs-csi-node-llzph                   3/3     Running   0          5m39s

# ebs-csi-controller에 컨테이너 6개가 배포됩니다.
❯ kubectl get pod -n kube-system -l app=ebs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo
ebs-plugin csi-provisioner csi-attacher csi-snapshotter csi-resizer liveness-probe

# csinode 확인
❯ kubectl api-resources | grep -i csi                                                                    
csidrivers                                       storage.k8s.io/v1                 false        CSIDriver
csinodes                                         storage.k8s.io/v1                 false        CSINode
csistoragecapacities                             storage.k8s.io/v1                 true         CSIStorageCapacity

❯ kubectl get csinodes                                                                                   
NAME                                               DRIVERS   AGE
ip-192-168-1-115.ap-northeast-2.compute.internal   1         11h
ip-192-168-2-36.ap-northeast-2.compute.internal    1         11h
ip-192-168-3-152.ap-northeast-2.compute.internal   1         11h

❯ kubectl describe csinodes                                                                              
Name:               ip-192-168-1-115.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd,kubernetes.io/portworx-v...
CreationTimestamp:  Sat, 22 Feb 2025 13:57:49 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-02bd989c032cbc389
      Allocatables:
        Count:        25
      Topology Keys:  [kubernetes.io/os topology.ebs.csi.aws.com/zone topology.kubernetes.io/zone]
      
❯ kubectl get csidrivers                                                                                 
NAME              ATTACHREQUIRED   PODINFOONMOUNT   STORAGECAPACITY   TOKENREQUESTS   REQUIRESREPUBLISH   MODES        AGE
ebs.csi.aws.com   true             false            false             <unset>         false               Persistent   9m9s
efs.csi.aws.com   false            false            false             <unset>         false               Persistent   12      

❯ kubectl describe csidrivers ebs.csi.aws.com                                                            
Name:         ebs.csi.aws.com
Namespace:
Labels:       app.kubernetes.io/component=csi-driver
              app.kubernetes.io/managed-by=EKS
              app.kubernetes.io/name=aws-ebs-csi-driver
              app.kubernetes.io/version=1.39.0
Annotations:  <none>
API Version:  storage.k8s.io/v1
Kind:         CSIDriver
Metadata:
  Creation Timestamp:  2025-02-22T16:43:58Z
  Resource Version:    144190
  UID:                 b167e9ee-470a-4b1a-86fc-a04633ee8ea1
Spec:
  Attach Required:     true
  Fs Group Policy:     ReadWriteOnceWithFSType
  Pod Info On Mount:   false
  Requires Republish:  false
  Se Linux Mount:      false
  Storage Capacity:    false
  Volume Lifecycle Modes:
    Persistent
Events:  <none>

 

EBS CSI Driver를 추가하였으므로, 이제 EKS에 EBS 볼륨을 동적으로 생성하고 관리할 수 있습니다.

 

예제로 PVC/PV를 사용하여 워드프레스 배포해보는 실습을 진행해보겠습니다.

https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/

 

Example: Deploying WordPress and MySQL with Persistent Volumes

This tutorial shows you how to deploy a WordPress site and a MySQL database using Minikube. Both applications use PersistentVolumes and PersistentVolumeClaims to store data. A PersistentVolume (PV) is a piece of storage in the cluster that has been manuall

kubernetes.io

 

# 사용하고 싶은 비밀번호 추가
❯ cat <<EOF >./kustomization.yaml                                                                                
secretGenerator:
- name: mysql-pass
  literals:
  - password=hellouz818
EOF

❯ curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml                               
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   162  100   162    0     0    560      0 --:--:-- --:--:-- --:--:--   591
100  1442  100  1442    0     0   2035      0 --:--:-- --:--:-- --:--:-- 15673

❯ curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml                               
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   162  100   162    0     0    759      0 --:--:-- --:--:-- --:--:--   778
100  1341  100  1341    0     0   1778      0 --:--:-- --:--:-- --:--:--     0

# 다만 EBS를 볼륨으로 쓰고자 PVC에 storageclass 추가
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: gp2
  resources:
    requests:
      storage: 20Gi

❯ kubectl apply -k ./                                                                                11s ○ myeks 03:03:54
secret/mysql-pass-k7bm2d8695 created
service/wordpress created
service/wordpress-mysql created
persistentvolumeclaim/mysql-pv-claim created
persistentvolumeclaim/wp-pv-claim created
deployment.apps/wordpress created
deployment.apps/wordpress-mysql created

 

하지만 워드프레스 접속은 Ingress(ALB, group 활용) - Service(ClusterIP)로 설정하는 경우에는 추가적인 설정이 더 필요합니다.

우선, Service 타입은 ClusterIP로 변경하고 ALB를 Ingress로 사용하기 위해 AWS Load Balancer Controller를 배포해야합니다.

❯ helm repo add eks https://aws.github.io/eks-charts
❯ helm repo update

❯ helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=myeks \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller
NAME: aws-load-balancer-controller
LAST DEPLOYED: Sun Feb 23 03:15:34 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
AWS Load Balancer controller installed!             


# 배포한 설정
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
    tier: frontend
  type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: gp2
  resources:
    requests:
      storage: 20Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
      - image: wordpress:6.2.1-apache
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: WORDPRESS_DB_USER
          value: wordpress
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wp-pv-claim
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wordpress-ingress
  namespace: default
  annotations:
    alb.ingress.kubernetes.io/certificate-arn: arn:~~
    alb.ingress.kubernetes.io/group.name: study
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":443}]'
    alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/ssl-redirect: "443"
    alb.ingress.kubernetes.io/success-codes: 200-399
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
  - host: wordpress.hellouz818.com # 사전에 구입한 도메인
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wordpress
            port:
              number: 80

 

 

 

++ 참고) EBS 볼륨 확인 명령어

실습하였을 때 EBS를 볼륨으로 사용하고 있는 것을 볼 수 있습니다.

# 워커 노드 EBS 볼륨 확인
❯ aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --output table
--------------------------------------------------------------
|                       DescribeVolumes                      |
+------------------------------------------------------------+
||                          Volumes                         ||
|+---------------------+------------------------------------+|
||  AvailabilityZone   |  ap-northeast-2a                   ||
||  CreateTime         |  2025-02-22T04:57:06.422000+00:00  ||
||  Encrypted          |  False                             ||
||  Iops               |  3000                              ||
||  MultiAttachEnabled |  False                             ||
||  Size               |  120                               ||
||  SnapshotId         |  snap-09234f0993e1a70d3            ||
||  State              |  in-use                            ||
||  Throughput         |  125                               ||
||  VolumeId           |  vol-06d41d88a511ce918             ||
||  VolumeType         |  gp3                               ||
|+---------------------+------------------------------------+|
|||                       Attachments                      |||
||+------------------------+-------------------------------+||
|||  AttachTime            |  2025-02-22T04:57:06+00:00    |||
|||  DeleteOnTermination   |  True                         |||
|||  Device                |  /dev/xvda                    |||
|||  InstanceId            |  i-02bd989c032cbc389          |||
|||  State                 |  attached                     |||
|||  VolumeId              |  vol-06d41d88a511ce918        |||
||+------------------------+-------------------------------+||
|||                        Operator                        |||
||+------------------------------+-------------------------+||
|||  Managed                     |  False                  |||
||+------------------------------+-------------------------+||
|||                          Tags                          |||
||+------------------------------------+-------------------+||
|||                 Key                |       Value       |||
||+------------------------------------+-------------------+||
|||  alpha.eksctl.io/nodegroup-type    |  managed          |||
|||  Name                              |  myeks-ng1-Node   |||
|||  alpha.eksctl.io/nodegroup-name    |  ng1              |||
|||  eks:nodegroup-name                |  ng1              |||
|||  eks:cluster-name                  |  myeks            |||
--------------------------------------------------------------

# 워커 노드에서 파드에 추가한 EBS 볼륨 확인 (워드프레스 실습 전)
❯ aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --output table
-----------------
|DescribeVolumes|
+---------------+


# 워커 노드에서 파드에 추가한 EBS 볼륨 확인 (워드프레스 실습 후)
-----------------------------------------------------------------------------------------------------------
|                                             DescribeVolumes                                             |
+---------------------------------------------------------------------------------------------------------+
||                                                Volumes                                                ||
|+--------------------------------------+----------------------------------------------------------------+|
||  AvailabilityZone                    |  ap-northeast-2c                                               ||
||  CreateTime                          |  2025-02-22T18:06:53.448000+00:00                              ||
||  Encrypted                           |  False                                                         ||
||  Iops                                |  100                                                           ||
||  MultiAttachEnabled                  |  False                                                         ||
||  Size                                |  20                                                            ||
||  SnapshotId                          |                                                                ||
||  State                               |  in-use                                                        ||
||  VolumeId                            |  vol-0172e8f746fc14ca9                                         ||
||  VolumeType                          |  gp2                                                           ||
|+--------------------------------------+----------------------------------------------------------------+|
|||                                             Attachments                                             |||
||+--------------------------------------------+--------------------------------------------------------+||
|||  AttachTime                                |  2025-02-22T18:06:57+00:00                             |||
|||  DeleteOnTermination                       |  False                                                 |||
|||  Device                                    |  /dev/xvdaa                                            |||
|||  InstanceId                                |  i-05491bd445d37f0f3                                   |||
|||  State                                     |  attached                                              |||
|||  VolumeId                                  |  vol-0172e8f746fc14ca9                                 |||
||+--------------------------------------------+--------------------------------------------------------+||
|||                                              Operator                                               |||
||+-------------------------------------------------------+---------------------------------------------+||
|||  Managed                                              |  False                                      |||
||+-------------------------------------------------------+---------------------------------------------+||
|||                                                Tags                                                 |||
||+------------------------------------------+----------------------------------------------------------+||
|||                    Key                   |                          Value                           |||
||+------------------------------------------+----------------------------------------------------------+||
|||  CSIVolumeName                           |  pvc-f81bce58-7c94-463f-a2e9-e5d32d09c3e5                |||
|||  KubernetesCluster                       |  myeks                                                   |||
|||  Name                                    |  myeks-dynamic-pvc-f81bce58-7c94-463f-a2e9-e5d32d09c3e5  |||
|||  ebs.csi.aws.com/cluster                 |  true                                                    |||
|||  kubernetes.io/cluster/myeks             |  owned                                                   |||
|||  kubernetes.io/created-for/pvc/namespace |  default                                                 |||
|||  kubernetes.io/created-for/pv/name       |  pvc-f81bce58-7c94-463f-a2e9-e5d32d09c3e5                |||
|||  kubernetes.io/created-for/pvc/name      |  mysql-pv-claim                                          |||
||+------------------------------------------+----------------------------------------------------------+||

 

 

 

AWS EFS Controller

EKS에서 EFS를 스토리지로 사용하기 위해서는 EFS CSI Driver 설치가 필요합니다.

AWS CSI driver의 동작 원리는 EBS CSI driver와 유사합니다.

 

aws-efs-csi-driver 전체 버전 정보와 기본 설치 버전(True) 정보 확인합니다.

ISRA 설정(AmazonEFSCSIDriverPolicy)을 사용하고 ISRA를 확인합니다.

AmazonEKS_EFS_CSI_DriverRole이라는 IAM 역할을 생성하고, AmazonEFSCSIDriverPolicy 정책을 해당 IAM 역할에 부여합니다. efs-csi-controller-sa 서비스 계정이 EFS API를 호출할 수 있도록 설정합니다.

# EFS 정보 확인 
❯ aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
fs-067eb7f3b3ee1b039

# 아래는 aws-efs-csi-driver 전체 버전 정보와 기본 설치 버전(True) 정보 확인
❯ aws eks describe-addon-versions \
    --addon-name aws-efs-csi-driver \
    --kubernetes-version 1.31 \
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
    --output text
v2.1.4-eksbuild.1
True
v2.1.3-eksbuild.1
False
v2.1.2-eksbuild.1
False
v2.1.1-eksbuild.1
False
v2.1.0-eksbuild.1
False
v2.0.9-eksbuild.1
False
v2.0.8-eksbuild.1
False
v2.0.7-eksbuild.1
False
v2.0.6-eksbuild.2
False
v2.0.6-eksbuild.1
False
v2.0.5-eksbuild.1
False
v2.0.4-eksbuild.1
False
v2.0.3-eksbuild.1
False
v2.0.2-eksbuild.1
False
v2.0.1-eksbuild.1
False
v2.0.0-eksbuild.1
False
v1.7.7-eksbuild.1
False
v1.7.6-eksbuild.2
False
v1.7.6-eksbuild.1

# ISRA 설정 : 고객관리형 정책 AmazonEKS_EFS_CSI_Driver_Policy 사용
❯ eksctl create iamserviceaccount \                                                                              
  --name efs-csi-controller-sa \
  --namespace kube-system \
  --cluster ${CLUSTER_NAME} \
  --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEFSCSIDriverPolicy \
  --approve \
  --role-only \
  --role-name AmazonEKS_EFS_CSI_DriverRole
  
# ISRA 확인  
❯ eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}                                                    
NAMESPACE	NAME				ROLE ARN
kube-system	aws-load-balancer-controller	arn:aws:iam::941377114730:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-yq8VPD5xPBny
kube-system	ebs-csi-controller-sa		arn:aws:iam::941377114730:role/AmazonEKS_EBS_CSI_DriverRole
kube-system	efs-csi-controller-sa		arn:aws:iam::941377114730:role/AmazonEKS_EFS_CSI_DriverRole

 

Amazon EFS CSI driver addon 배포합니다.

 

# Amazon EFS CSI driver addon 배포(설치)
❯ export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
❯ eksctl create addon --name aws-efs-csi-driver --cluster ${CLUSTER_NAME} --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EFS_CSI_DriverRole --force

❯ kubectl get sa -n kube-system efs-csi-controller-sa -o yaml | head -5                           

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::941377114730:role/AmazonEKS_EFS_CSI_DriverRole
    
❯ eksctl get addon --cluster ${CLUSTER_NAME}
NAME			VERSION			STATUS	ISSUES	IAMROLE										UPDATE AVAILABLE	CONFIGURATION VALUES		POD IDENTITY ASSOCIATION ROLES
aws-ebs-csi-driver	v1.39.0-eksbuild.1	ACTIVE	0	arn:aws:iam::941377114730:role/AmazonEKS_EBS_CSI_DriverRole
aws-efs-csi-driver	v2.1.4-eksbuild.1	ACTIVE	0	arn:aws:iam::941377114730:role/AmazonEKS_EFS_CSI_DriverRole
coredns			v1.11.4-eksbuild.2	ACTIVE	0
kube-proxy		v1.31.3-eksbuild.2	ACTIVE	0
metrics-server		v0.7.2-eksbuild.2	ACTIVE	0
vpc-cni			v1.19.2-eksbuild.5	ACTIVE	0	arn:aws:iam::941377114730:role/eksctl-myeks-addon-vpc-cni-Role1-cSbYmEhHAYbN	enableNetworkPolicy: "true"

❯ kubectl get pod -n kube-system -l "app.kubernetes.io/name=aws-efs-csi-driver,app.kubernetes.io/instance=aws-efs-csi-driver"
NAME                                  READY   STATUS    RESTARTS   AGE
efs-csi-controller-64fc4bc65d-2ksl9   3/3     Running   0          2m50s
efs-csi-controller-64fc4bc65d-sllvp   3/3     Running   0          2m50s
efs-csi-node-2bh7z                    3/3     Running   0          2m50s
efs-csi-node-8cbv8                    3/3     Running   0          2m50s
efs-csi-node-8qld7                    3/3     Running   0          2m50s

❯ kubectl get pod -n kube-system -l app=efs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo
efs-plugin csi-provisioner liveness-probe

 

AWS → EFS → 파일 시스템 : 네트워크 확인

 

EFS 파일 시스템을 파드가 사용할 수 있도록 설정하겠습니다.

[root@operator-host ~]# git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git /root/efs-csi
Cloning into '/root/efs-csi'...
remote: Enumerating objects: 30760, done.
remote: Counting objects: 100% (5451/5451), done.
remote: Compressing objects: 100% (1512/1512), done.
remote: Total 30760 (delta 4230), reused 4145 (delta 3902), pack-reused 25309 (from 3)
Receiving objects: 100% (30760/30760), 28.56 MiB | 11.23 MiB/s, done.
Resolving deltas: 100% (16621/16621), done.

[root@operator-host ~]# cd /root/efs-csi/examples/kubernetes/multiple_pods/specs && tree
.
├── claim.yaml
├── pod1.yaml
├── pod2.yaml
├── pv.yaml
└── storageclass.yaml

0 directories, 5 files

# aws configure 이후 
# EFS 스토리지클래스 생성 및 확인
(admin:N/A) [root@operator-host specs]# cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com

(admin:N/A) [root@operator-host specs]# kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/efs-sc created

(admin:N/A) [root@operator-host specs]# kubectl get sc efs-sc
NAME     PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
efs-sc   efs.csi.aws.com   Delete          Immediate           false           10s

# PV 생성 및 확인 : volumeHandle을 자신의 EFS 파일시스템ID로 변경
(admin:N/A) [root@operator-host specs]# EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)

(admin:N/A) [root@operator-host specs]# ls
claim.yaml  pod1.yaml  pod2.yaml  pv.yaml  storageclass.yaml

(admin:N/A) [root@operator-host specs]# sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml

(admin:N/A) [root@operator-host specs]# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: efs-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: efs-sc
  csi:
    driver: efs.csi.aws.com
    volumeHandle: fs-067eb7f3b3ee1b039
    
    
(admin:N/A) [root@operator-host specs]# kubectl apply -f pv.yaml
persistentvolume/efs-pv created

(admin:N/A) [root@operator-host specs]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                    STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
efs-pv                                     5Gi        RWX            Retain           Available                            efs-sc         <unset>                          12s
pvc-a80f7150-3023-48b0-af32-02b7e365aafe   20Gi       RWO            Delete           Bound       default/wp-pv-claim      gp2            <unset>                          107m
pvc-f81bce58-7c94-463f-a2e9-e5d32d09c3e5   20Gi       RWO            Delete           Bound       default/mysql-pv-claim   gp2            <unset>                          107m

# PVC 생성 및 확인
(admin:N/A) [root@operator-host specs]# cat claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi
      
(admin:N/A) [root@operator-host specs]# kubectl apply -f claim.yaml
persistentvolumeclaim/efs-claim created

(admin:N/A) [root@operator-host specs]# kubectl get pvc
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
efs-claim        Bound    efs-pv                                     5Gi        RWX            efs-sc         <unset>                 15s
mysql-pv-claim   Bound    pvc-f81bce58-7c94-463f-a2e9-e5d32d09c3e5   20Gi       RWO            gp2            <unset>                 108m
wp-pv-claim      Bound    pvc-a80f7150-3023-48b0-af32-02b7e365aafe   20Gi       RWO            gp2            <unset>                 108m

 

실제 EFS를 볼륨으로 사용하는 Pod를 생성해보도록 하겠습니다.

(admin:N/A) [root@operator-host specs]# cat pod1.yaml pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app1
spec:
  containers:
  - name: app1
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim
apiVersion: v1
kind: Pod
metadata:
  name: app2
spec:
  containers:
  - name: app2
    image: busybox
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out2.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: efs-claim
      
(admin:N/A) [root@operator-host specs]# kubectl apply -f pod1.yaml,pod2.yaml
pod/app1 created
pod/app2 created

(admin:N/A) [root@operator-host specs]# kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
app1                              1/1     Running   0          49s
app2                              1/1     Running   0          49s
wordpress-84d786f98-xmdv8         1/1     Running   0          110m
wordpress-mysql-987f4c89f-rr7pw   1/1     Running   0          110m
(admin:N/A) [root@operator-host specs]# kubectl exec -ti app1 -- sh -c "df -hT -t nfs4"
Filesystem           Type            Size      Used Available Use% Mounted on
127.0.0.1:/          nfs4            8.0E         0      8.0E   0% /data
(admin:N/A) [root@operator-host specs]# kubectl exec -ti app2 -- sh -c "df -hT -t nfs4"
Filesystem           Type            Size      Used Available Use% Mounted on
127.0.0.1:/          nfs4            8.0E         0      8.0E   0% /data

(admin:N/A) [root@operator-host specs]# tree /mnt/myefs
/mnt/myefs [error opening dir]

0 directories, 0 files

(admin:N/A) [root@operator-host specs]# tail -f /mnt/myefs/out1.txt
tail: cannot open ‘/mnt/myefs/out1.txt’ for reading: No such file or directory
tail: no files remaining

(admin:N/A) [root@operator-host specs]# tail -f /mnt/myefs/out2.txt
tail: cannot open ‘/mnt/myefs/out2.txt’ for reading: No such file or directory
tail: no files remaining

(admin:N/A) [root@operator-host specs]# kubectl exec -ti app1 -- tail -f /data/out1.txt
Sat Feb 22 19:57:47 UTC 2025
Sat Feb 22 19:57:52 UTC 2025
Sat Feb 22 19:57:57 UTC 2025
Sat Feb 22 19:58:02 UTC 2025
Sat Feb 22 19:58:07 UTC 2025
Sat Feb 22 19:58:12 UTC 2025
Sat Feb 22 19:58:17 UTC 2025
Sat Feb 22 19:58:22 UTC 2025
Sat Feb 22 19:58:27 UTC 2025
Sat Feb 22 19:58:32 UTC 2025
^Ccommand terminated with exit code 130

(admin:N/A) [root@operator-host specs]# kubectl exec -ti app2 -- tail -f /data/out2.txt
Sat Feb 22 19:57:52 UTC 2025
Sat Feb 22 19:57:57 UTC 2025
Sat Feb 22 19:58:02 UTC 2025
Sat Feb 22 19:58:07 UTC 2025
Sat Feb 22 19:58:12 UTC 2025
Sat Feb 22 19:58:17 UTC 2025
Sat Feb 22 19:58:22 UTC 2025
Sat Feb 22 19:58:27 UTC 2025
Sat Feb 22 19:58:32 UTC 2025
Sat Feb 22 19:58:38 UTC 2025
^Ccommand terminated with exit code 130

 

 

++) PV는 5Gi로 설정했는데 df -hT -t nfs4는 8.0E(엑사바이트)로 보일까요?

EFS는 블록 스토리지가 아니라 네트워크 파일 시스템입니다.

EBS와 같이 스토리지 크기를 미리 정하지 않고, EFS는 자동 확장 되기 때문에 고정된 크기를 가지지 않습니다. 따라서 efs-pv의 5Gi 설정은 쿠버네티스 리소스 관리 목적일 뿐(PVC 매칭을 위한 설정값), 실제 용량 제한이 아닙니다.

 

++) df -hT에서 8.0E가 나오는 이유는 무엇일까요?

AWS는 EFS를 무제한 확장 가능하도록 설계했기 때문입니다. 

NFS 마운트 시 df -hT 명령어가 무한한 공간을 제공한다고 인식했기 때문에 8.0E로 표시되는 것입니다.

따라서 EFS에서 사용량을 직접 확인하려면 aws efs describe-file-systems 또는 du -sh 명령어 사용하여 마운트한 상태에서 디렉터리 사용량을 조회하는 것이 좋습니다.