-
CSI(Container Storage Interface)란?
-
AWS EBS Controller
-
AWS EBS Controller 설치
-
volumeBindingMode
-
실습: EBS 볼륨 PVC 생성 및 파드 연결
-
볼륨 크기 증설
-
AWS Volume SnapShots Controller
-
AWS Volume SnapShots Controller 설치
-
볼륨 스냅숏 생성 및 복구 테스트
-
AWS EFS Controller
-
EFS CSI Controller 설치
-
정적 프로비저닝(Static Provisioning)
-
동적프로비저닝(Dynamic Provisioning_
-
EKS Persistent Volumes for Instance Store
-
kubestr 성능 모니터링 도구
CSI(Container Storage Interface)란?
CSI(Container Storage Interface)는 쿠버네티스가 다양한 스토리지 시스템과 쉽게 연결될 수 있도록 만든 표준 인터페이스이다. 예전에는 AWS EBS 같은 스토리지 기능이 쿠버네티스 코드 안에 직접 포함되어 있어서, 스토리지 기능을 업데이트하려면 쿠버네티스 전체를 업그레이드해야 했다. 이런 불편함을 해결하기 위해 스토리지 관련 코드를 외부로 분리한 것이 바로 CSI이며, 이를 통해 스토리지 기능을 필요할 때마다 독립적으로 추가하고 업데이트할 수 있게 되었다.
다양한 벤더사의 CSI Driver가 존재하지만, AWS EBS CSI Driver로 예를 들면, csi-controller
파드(Statefulset or Deployment)는 AWS API를 사용하여 실제 EBS Volume을 생성하고, DeamonSet으로 배포된 csi-node
는 AWS API를 사용하여, 쿠버네티스 노드(EC2)에 EBS Volume을 attach 해준다.

AWS EBS Controller
AWS EBS Controller는 AWS 제공하는 블록 스토리지 서비스 EBS(볼륨)을 쿠버네티스 클러스터에서 관리하는 CSI(Container Storage Interface) 드라이버이다.

AWS EBS Controller 설치
- EBS CSI driver는 EKS addon을 통해 설치를 지원한다.
- 쿠버네티스의 리소스인
ebs-csi-controller
가 AWS의 리소스인 EBS를 제어할 수 있도록 IRSA도 설정한다. - 각 볼륨은 한 번에 하나의 파드에만 마운트될 수 있다(accessMode=ReadWriteOnce).
# 아래는 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
# ISRA 설정 : AWS관리형 정책 AmazonEBSCSIDriverPolicy 사용
eksctl create iamserviceaccount \
--name ebs-csi-controller-sa \
--namespace kube-system \
--cluster ${CLUSTER_NAME} \
--attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
--approve \
--role-only \
--role-name AmazonEKS_EBS_CSI_DriverRole
# 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
# ISRA 확인
eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}
NAMESPACE NAME ROLE ARN
kube-system ebs-csi-controller-sa arn:aws:iam::911283464785:role/AmazonEKS_EBS_CSI_DriverRole
ebs-csi-controller 파드는 6개의 컨테이너로 구성이 되어있다.
# 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
(admin:N/A) [root@operator-host ~]# kubectl get pod -n kube-system -l app=ebs-csi-controller
NAME READY STATUS RESTARTS AGE
ebs-csi-controller-7f8f8cb84-6gfp2 6/6 Running 0 88s
ebs-csi-controller-7f8f8cb84-rfrw4 6/6 Running 0 88s
(admin:N/A) [root@operator-host ~]# kubectl get csidrivers
NAME ATTACHREQUIRED PODINFOONMOUNT STORAGECAPACITY TOKENREQUESTS REQUIRESREPUBLISH MODES AGE
ebs.csi.aws.com true false false <unset> false Persistent 3h38m
gp3
스토리지 클래스를 생성해준다. 기본적으로 gp2
스토리지 클래스가 존재하지만, CSI driver를 통한 GP3 사용 방식이 비용 효율성과 성능이(IOPS) 더 좋으므로 gp3 스토리지 클래스 사용을 추천한다.
# gp3 스토리지 클래스 생성
kubectl get sc
cat <<EOF | kubectl apply -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
#iops: "5000"
#throughput: "250"
allowAutoIOPSPerGBIncrease: 'true'
encrypted: 'true'
fsType: xfs # 기본값이 ext4
EOF
kubectl get sc
gp2 kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 4h59m
gp3 (default) ebs.csi.aws.com Delete WaitForFirstConsumer true 3h33m
kubectl describe sc gp3 | grep Parameters
Parameters: allowAutoIOPSPerGBIncrease=true,encrypted=true,fsType=xfs,type=gp3
volumeBindingMode
volumeBindingMode
볼륨 바인딩과 동적 프로비저닝의 시작 시기를 제어하는 필드로 아래 두 가지 방식이 존재(기본값은 Immediate 모드)
Immediate
모드는 PVC 생성 즉시 볼륨을 바인딩하지만, 파드의 스케줄링 요구사항을 고려하지 않아 파드가 스케줄 되지 못할 수 있다.WaitForFirstConsumer
모드는 파드가 생성될 때까지 볼륨 바인딩을 지연시키고, 파드의 스케줄링 요구사항(리소스, 노드 셀렉터, 어피니티 등)을 고려하여 적절한 볼륨을 생성하므로 더 안전하고 효율적인 볼륨 배치가 가능하다.
AWS EBS는 WaitForFirstConsumer 모드를 필수적으로 사용해야 한다. 이는 EBS 볼륨이 특정 가용영역(AZ)에 종속되어 있기 때문이다. 만약 Immediate 모드를 사용하면 PVC 생성 시점에 EBS 볼륨이 임의의 가용영역에 생성되어, 나중에 파드가 다른 가용영역의 노드에 스케줄링될 경우 볼륨을 마운트 할 수 없는 문제가 발생한다. 따라서 파드의 스케줄링 위치를 먼저 확인한 후 해당 가용영역에 볼륨을 생성하는 WaitForFirstConsumer 모드를 사용해야 한다..
실습: EBS 볼륨 PVC 생성 및 파드 연결
# PVC 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOF
kubectl get pvc,pv
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
EOF
# krew 플러그인
kubectl df-pv
# PVC, 파드 확인
kubectl get pvc,pv,pod
kubectl get VolumeAttachment
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
## 파드 내에서 볼륨 정보 확인
(admin:N/A) [root@operator-host ~]# kubectl exec -it app -- sh -c 'df -hT --type=overlay'
kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 120G 5.2G 115G 5% /
EBS 볼륨의 AZ 종속성 때문에 PV가 생성될 때 해당 EBS 볼륨이 존재하는 가용영역을 노드 어피니티로 지정하여, 파드가 반드시 같은 가용영역의 노드에만 스케줄링되도록 제한하는 것을 확인할 수 있다.
kubectl get pv -o yaml
...
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2b
...
볼륨 크기 증설
PVC의 spec.resources.requests.storage
필드를 수정하여 손쉽게 볼륨을 늘릴 수 있다. 하지만, 한번 증가된 크기는 다시 줄일 수 없으므로 초기 크기 설정이 중요하다. 또한 allowVolumeExpansion
이 true
로 설정되어 있어야 기능을 사용할 수 있다.
kubectl get pvc ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
볼륨 상태를 보면 optimizing 상태임을 확인할 수 있다. 볼륨 용량 수정이 반영되기 까지는 수치 반영이 조금 느릴 수 있다.


AWS Volume SnapShots Controller
AWS EBS 볼륨의 스냅샷을 관리하는 CSI 컨트롤러이다. 쿠버네티스의 VolumeSnapshot 리소스를 통해 EBS 볼륨의 백업을 자동화하고 관리할 수 있게 해 준다. 볼륨 스냅숏은 볼륨의 특정 시점의 복사본이다. 운영자의 실수로 볼륨을 지우거나, 문제가 생겼을 때 스냅숏을 통해 특정 시점의 볼륨으로 손쉽게 복원이 가능하다. AWS EKS 환경에서는 EBS 볼륨의 스냅숏을 의믜한다.
AWS Volume SnapShots Controller 설치
먼저, Volumesnapshots을 사용하려면 별도의 CRD를 설치해주어야 한다.
# Install Snapshot CRDs
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl get crd | grep snapshot
volumesnapshotclasses.snapshot.storage.k8s.io 2025-02-22T16:42:07Z
volumesnapshotcontents.snapshot.storage.k8s.io 2025-02-22T16:42:09Z
volumesnapshots.snapshot.storage.k8s.io 2025-02-22T16:42:06Z
# Install Common Snapshot Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
kubectl get deploy -n kube-system snapshot-controller
kubectl get pod -n kube-system
# Install Snapshotclass
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml
kubectl get vsclass # 혹은 volumesnapshotclasses
kubectl describe vsclass
볼륨 스냅숏 생성 및 복구 테스트
5초 간격으로 date로그를 출력하는 파드를 생성한다.
# PVC 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOF
kubectl get pvc,pv
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
EOF
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
Sat Feb 22 20:32:10 UTC 2025
Sat Feb 22 20:32:15 UTC 2025
Sat Feb 22 20:32:20 UTC 2025
Sat Feb 22 20:32:25 UTC 2025
스냅샷 생성 및 확인
# VolumeSnapshot 확인
kubectl get volumesnapshot
kubectl get volumesnapshot ebs-volume-snapshot -o jsonpath={.status.boundVolumeSnapshotContentName} ; echo
kubectl describe volumesnapshot.snapshot.storage.k8s.io ebs-volume-snapshot
kubectl get volumesnapshotcontents
# VolumeSnapshot ID 확인
kubectl get volumesnapshotcontents -o jsonpath='{.items[*].status.snapshotHandle}' ; echo
# AWS EBS 스냅샷 확인
aws ec2 describe-snapshots --owner-ids self | jq
aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[]' --output table

강제로 장애 재현 삭제 : 파드와 pvc를 생성하고 이를 재연하기 위해 두 리소스를 모두 지운다.
kubectl delete pod app && kubectl delete pvc ebs-claim
스냅숏으로 복원
datasource로 pvc 재생성
kubectl get pvc,pv
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-snapshot-restored-claim
spec:
storageClassName: gp3
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
dataSource:
name: ebs-volume-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
EOF
# 확인
kubectl get pvc,pv
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-snapshot-restored-claim
EOF
파드 재생성 이후 파일을 다시 확인해 보면, 파드 삭제 전까지의 저장 기록이 남아 있다. 이후기록도 잘 저장되고 있다

AWS EFS Controller
AWS EFS(Elastic File System)를 쿠버네티스 클러스터에 연결하고 관리하는 CSI 드라이버이다. EBS와 달리 EFS는 여러 가용영역에 걸쳐 여러 파드가 동시에 읽고 쓸 수 있는 네트워크 공유 파일시스템을 제공한다.

EFS CSI Controller 설치
- EFS CSI driver는 EKS addon을 통해 설치를 지원한다.
- 쿠버네티스의 리소스인
ebs-csi-controller
가 AWS의 리소스인 EFS를 제어할 수 있도록 IRSA도 설정한다.
# EFS 정보 확인
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
# 아래는 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
# 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}
# 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
# 확인
eksctl get addon --cluster ${CLUSTER_NAME}
kubectl get pod -n kube-system -l "app.kubernetes.io/name=aws-efs-csi-driver,app.kubernetes.io/instance=aws-efs-csi-driver"
kubectl get pod -n kube-system -l app=efs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo
kubectl get csidrivers efs.csi.aws.com -o yaml
AWS → EFS → 파일 시스템 : 네트워크 확인

정적 프로비저닝(Static Provisioning)
이미 생성된 EFS 파일시스템을 파드에 연결하는 방식이며, 하나의 EFS 공간을 여러 파드가 공유한다.
# 실습 코드 clone
git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git /root/efs-csi
cd /root/efs-csi/examples/kubernetes/multiple_pods/specs && tree
# EFS 스토리지클래스 생성 및 확인
cat storageclass.yaml
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
# PV 생성 및 확인 : volumeHandle을 자신의 EFS 파일시스템ID로 변경
EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml
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-05699d3c12ef609e2
kubectl apply -f pv.yaml
kubectl get pv; kubectl describe pv
cat pod1.yaml pod2.yaml
kubectl apply -f pod1.yaml,pod2.yaml
kubectl get pods
kubectl exec -ti app1 -- sh -c "df -hT -t nfs4"
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
# 공유 저장소 저장 동작 확인
/mnt/myefs
├── memo.txt
├── out1.txt
└── out2.txt
동적프로비저닝(Dynamic Provisioning_
PVC 생성 시 자동으로 EFS 액세스 포인트를 생성하는 방식이며, 액세스 포인트를 통해 각 파드의 권한과 루트 디렉터리를 격리도 가능하다.
# EFS 스토리지클래스 생성 및 확인
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/storageclass.yaml
cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap # The type of volume to be provisioned by Amazon EFS. Currently, only access point based provisioning is supported (efs-ap).
fileSystemId: fs-92107410 # The file system under which the access point is created.
directoryPerms: "700" # The directory permissions of the root directory created by the access point.
gidRangeStart: "1000" # optional, The starting range of the Posix group ID to be applied onto the root directory of the access point. The default value is 50000.
gidRangeEnd: "2000" # optional, The ending range of the Posix group ID. The default value is 7000000.
basePath: "/dynamic_provisioning" # optional, The path on the file system under which the access point root directory is created. If the path isn't provided, the access points root directory is created under the root of the file system.
subPathPattern: "${.PVC.namespace}/${.PVC.name}" # optional, A pattern that describes the subPath under which an access point should be created. So if the pattern were ${.PVC.namespace}/${PVC.name}, the PVC namespace is foo and the PVC name is pvc-123-456, and the basePath is /dynamic_provisioner the access point would be created at /dynamic_provisioner/foo/pvc-123-456
ensureUniqueDirectory: "true" # optional # A boolean that ensures that, if set, a UUID is appended to the final element of any dynamically provisioned path, as in the above example. This can be turned off but this requires you as the administrator to ensure that your storage classes are set up correctly. Otherwise, it's possible that 2 pods could end up writing to the same directory by accident. Please think very carefully before setting this to false!
reuseAccessPoint: "false" # optional
sed -i "s/fs-92107410/$EfsFsId/g" storageclass.yaml
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
# PVC/파드 생성 및 확인
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/pod.yaml
cat pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
---
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; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
kubectl apply -f pod.yaml
kubectl get pvc,pv,pod
# PVC/PV 생성 로그 확인
kubectl krew install stern
kubectl stern -n kube-system -l app=efs-csi-controller -c csi-provisioner
혹은
kubectl logs -n kube-system -l app=efs-csi-controller -c csi-provisioner -f
# 파드 정보 확인
kubectl exec -it efs-app -- 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
# 공유 저장소 저장 동작 확인
tree /mnt/myefs # 운영서버 EC2 에서 확인
kubectl exec efs-app -- bash -c "cat /data/out"
kubectl exec efs-app -- bash -c "ls -l /data/out"
kubectl exec efs-app -- bash -c "stat /data/"
# 공유 저장소 저장 동작 확인
/mnt/myefs
├── dynamic_provisioning
│ └── default
│ └── efs-claim-7ce7df00-4632-4d97-b1f0-44049a861c5b
│ └── out
├── memo.txt
├── out1.txt
└── out2.txt
aws 콘솔에서도 액세스 포인트를 확인할 수 있다.

EKS Persistent Volumes for Instance Store
AWS Instance Store는 EC2 인스턴스에 물리적으로(인스턴스의 로컬) 연결된 임시 스토리지이다.
인스턴스 실행 시 생성되고 종료 시 삭제되는 특성을 가지고 있어, 캐시나 임시 데이터 저장에 적합하며 높은 I/O 성능을 제공한다.
하지만 데이터가 영구적으로 보존되지 않기 때문에, 중요한 데이터는 EBS와 같은 영구 스토리지에 저장해야 한다.

Instance Store 노드 그룹 추가
- c5.large 타입
- 참고로 인스턴스 스토어는 aws 콘솔에 볼륨에서 조회되지는 않는다.
cat << EOF > myng2.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: myeks
region: ap-northeast-2
version: "1.31"
managedNodeGroups:
- amiFamily: AmazonLinux2
desiredCapacity: 1
instanceType: c5d.large
labels:
alpha.eksctl.io/cluster-name: myeks
alpha.eksctl.io/nodegroup-name: ng2
disk: instancestore
maxPodsPerNode: 110
maxSize: 1
minSize: 1
name: ng2
ssh:
allow: true
publicKeyName: $SSHKEYNAME
subnets:
- $PubSubnet1
- $PubSubnet2
- $PubSubnet3
tags:
alpha.eksctl.io/nodegroup-name: ng2
alpha.eksctl.io/nodegroup-type: managed
volumeIOPS: 3000
volumeSize: 30
volumeThroughput: 125
volumeType: gp3
preBootstrapCommands:
- |
# Install Tools
yum install nvme-cli links tree jq tcpdump sysstat -y
# Filesystem & Mount
mkfs -t xfs /dev/nvme1n1
mkdir /data
mount /dev/nvme1n1 /data
# Get disk UUID
uuid=\$(blkid -o value -s UUID mount /dev/nvme1n1 /data)
# Mount the disk during a reboot
echo /dev/nvme1n1 /data xfs defaults,noatime 0 2 >> /etc/fstab
EOF
# 신규 노드 그룹 생성
eksctl create nodegroup -f myng2.yaml
# 확인
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get node -l disk=instancestore
# ng2 노드 그룹 *ng2-remoteAccess* 포함된 보안그룹 ID
aws ec2 describe-security-groups --filters "Name=group-name,Values=*ng2-remoteAccess*" | jq
export NG2SGID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=*ng2-remoteAccess*" --query 'SecurityGroups[*].GroupId' --output text)
aws ec2 authorize-security-group-ingress --group-id $NG2SGID --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32
aws ec2 authorize-security-group-ingress --group-id $NG2SGID --protocol '-1' --cidr 172.20.1.100/32
# 워커 노드 SSH 접속
N4=<각자 자신의 워커 노드4번 공인 IP 지정>
N4=3.37.44.222
ssh ec2-user@$N4 hostname
인스턴스 스토어 경로로 local-path 스토리지 클래스 생성
#
curl -sL https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml | sed 's/opt/data/g' | kubectl apply -f -
kubectl describe cm -n local-path-storage local-path-config
...
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/data/local-path-provisioner"]
}
]
...
kubectl get sc local-path
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 4h
# [운영서버 EC2] Read 측정
kubestr fio -f fio-read.fio -s local-path --size 10G --nodeselector disk=instancestore
...
read:
IOPS=20309.355469 BW(KiB/s)=81237
iops: min=17392 max=93872 avg=20316.857422
bw(KiB/s): min=69570 max=375488 avg=81268.023438
Disk stats (read/write):
nvme1n1: ios=2432488/9 merge=0/3 ticks=7639891/23 in_queue=7639913, util=99.950768%
- OK
kubestr 성능 모니터링 도구
kubestr은 쿠버네티스 클러스터의 스토리지 스토리지 시스템의 I/O 성능(FIO)을 측정하는 벤치마킹 도구이다.

kubestr 설치
# [운영서버 EC2] kubestr 툴 다운로드 - Link
wget https://github.com/kastenhq/kubestr/releases/download/v0.4.48/kubestr_0.4.48_Linux_amd64.tar.gz
tar xvfz kubestr_0.4.48_Linux_amd64.tar.gz && mv kubestr /usr/local/bin/ && chmod +x /usr/local/bin/kubestr
# 스토리지클래스 점검
kubestr -h
kubestr
성능측정
# 랜덤 읽기 성능 테스트 수행 : 3분 정도 소요
# libaio 엔진과 다이렉트 I/O를 사용하여 고성능 스토리지의 랜덤 읽기 성능을 측정.
# OS 캐시를 사용하지 않고 직접 디스크 I/O 수행 (direct 플래그)
cat << EOF > fio-read.fio
[global]
ioengine=libaio
direct=1
bs=4k
runtime=120
time_based=1
iodepth=16
numjobs=4
group_reporting
size=1g
rw=randread
[read]
EOF
kubestr fio -f fio-read.fio -s local-path --size 10G # size 미 지정시 기본 100G로 노드 Disk full 발생하니 유의
결과
EBS 볼륨 측정

속도 측정 3000 iops throughtput(처리량)
Instance Store 볼륨 측정
Instance Store 볼륨의 IOPS의 평균값이 20308이 나오는 것을 볼 수 있습니다 일반 EBS의 기본 IOPS의 값이 3000인데, 무려 6-7배가량 빠른 것을 볼 수 있다.


'AWS > EKS' 카테고리의 다른 글
EKS 서버리스 서비스 간략히 알아보기(Fargate & EKS AutoMode) (0) | 2025.03.23 |
---|---|
EKS Karpenter 기본 사용(Node AutoScaling) (1) | 2025.03.05 |
EKS 기본 네트워크(AWS VPC CNI + AWS LoadBalancer Controller) (0) | 2025.02.16 |
EKS 설치 및 클러스터 엔드포인트(EKS Cluster Endpoint) (2) | 2025.02.09 |
EKS IaC(Terraform) - AEWS 8주차 (1) | 2024.04.28 |
CSI(Container Storage Interface)란?
CSI(Container Storage Interface)는 쿠버네티스가 다양한 스토리지 시스템과 쉽게 연결될 수 있도록 만든 표준 인터페이스이다. 예전에는 AWS EBS 같은 스토리지 기능이 쿠버네티스 코드 안에 직접 포함되어 있어서, 스토리지 기능을 업데이트하려면 쿠버네티스 전체를 업그레이드해야 했다. 이런 불편함을 해결하기 위해 스토리지 관련 코드를 외부로 분리한 것이 바로 CSI이며, 이를 통해 스토리지 기능을 필요할 때마다 독립적으로 추가하고 업데이트할 수 있게 되었다.
다양한 벤더사의 CSI Driver가 존재하지만, AWS EBS CSI Driver로 예를 들면, csi-controller
파드(Statefulset or Deployment)는 AWS API를 사용하여 실제 EBS Volume을 생성하고, DeamonSet으로 배포된 csi-node
는 AWS API를 사용하여, 쿠버네티스 노드(EC2)에 EBS Volume을 attach 해준다.

AWS EBS Controller
AWS EBS Controller는 AWS 제공하는 블록 스토리지 서비스 EBS(볼륨)을 쿠버네티스 클러스터에서 관리하는 CSI(Container Storage Interface) 드라이버이다.

AWS EBS Controller 설치
- EBS CSI driver는 EKS addon을 통해 설치를 지원한다.
- 쿠버네티스의 리소스인
ebs-csi-controller
가 AWS의 리소스인 EBS를 제어할 수 있도록 IRSA도 설정한다. - 각 볼륨은 한 번에 하나의 파드에만 마운트될 수 있다(accessMode=ReadWriteOnce).
# 아래는 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
# ISRA 설정 : AWS관리형 정책 AmazonEBSCSIDriverPolicy 사용
eksctl create iamserviceaccount \
--name ebs-csi-controller-sa \
--namespace kube-system \
--cluster ${CLUSTER_NAME} \
--attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
--approve \
--role-only \
--role-name AmazonEKS_EBS_CSI_DriverRole
# 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
# ISRA 확인
eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}
NAMESPACE NAME ROLE ARN
kube-system ebs-csi-controller-sa arn:aws:iam::911283464785:role/AmazonEKS_EBS_CSI_DriverRole
ebs-csi-controller 파드는 6개의 컨테이너로 구성이 되어있다.
# 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
(admin:N/A) [root@operator-host ~]# kubectl get pod -n kube-system -l app=ebs-csi-controller
NAME READY STATUS RESTARTS AGE
ebs-csi-controller-7f8f8cb84-6gfp2 6/6 Running 0 88s
ebs-csi-controller-7f8f8cb84-rfrw4 6/6 Running 0 88s
(admin:N/A) [root@operator-host ~]# kubectl get csidrivers
NAME ATTACHREQUIRED PODINFOONMOUNT STORAGECAPACITY TOKENREQUESTS REQUIRESREPUBLISH MODES AGE
ebs.csi.aws.com true false false <unset> false Persistent 3h38m
gp3
스토리지 클래스를 생성해준다. 기본적으로 gp2
스토리지 클래스가 존재하지만, CSI driver를 통한 GP3 사용 방식이 비용 효율성과 성능이(IOPS) 더 좋으므로 gp3 스토리지 클래스 사용을 추천한다.
# gp3 스토리지 클래스 생성
kubectl get sc
cat <<EOF | kubectl apply -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: gp3
annotations:
storageclass.kubernetes.io/is-default-class: "true"
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
#iops: "5000"
#throughput: "250"
allowAutoIOPSPerGBIncrease: 'true'
encrypted: 'true'
fsType: xfs # 기본값이 ext4
EOF
kubectl get sc
gp2 kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 4h59m
gp3 (default) ebs.csi.aws.com Delete WaitForFirstConsumer true 3h33m
kubectl describe sc gp3 | grep Parameters
Parameters: allowAutoIOPSPerGBIncrease=true,encrypted=true,fsType=xfs,type=gp3
volumeBindingMode
volumeBindingMode
볼륨 바인딩과 동적 프로비저닝의 시작 시기를 제어하는 필드로 아래 두 가지 방식이 존재(기본값은 Immediate 모드)
Immediate
모드는 PVC 생성 즉시 볼륨을 바인딩하지만, 파드의 스케줄링 요구사항을 고려하지 않아 파드가 스케줄 되지 못할 수 있다.WaitForFirstConsumer
모드는 파드가 생성될 때까지 볼륨 바인딩을 지연시키고, 파드의 스케줄링 요구사항(리소스, 노드 셀렉터, 어피니티 등)을 고려하여 적절한 볼륨을 생성하므로 더 안전하고 효율적인 볼륨 배치가 가능하다.
AWS EBS는 WaitForFirstConsumer 모드를 필수적으로 사용해야 한다. 이는 EBS 볼륨이 특정 가용영역(AZ)에 종속되어 있기 때문이다. 만약 Immediate 모드를 사용하면 PVC 생성 시점에 EBS 볼륨이 임의의 가용영역에 생성되어, 나중에 파드가 다른 가용영역의 노드에 스케줄링될 경우 볼륨을 마운트 할 수 없는 문제가 발생한다. 따라서 파드의 스케줄링 위치를 먼저 확인한 후 해당 가용영역에 볼륨을 생성하는 WaitForFirstConsumer 모드를 사용해야 한다..
실습: EBS 볼륨 PVC 생성 및 파드 연결
# PVC 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOF
kubectl get pvc,pv
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
EOF
# krew 플러그인
kubectl df-pv
# PVC, 파드 확인
kubectl get pvc,pv,pod
kubectl get VolumeAttachment
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
## 파드 내에서 볼륨 정보 확인
(admin:N/A) [root@operator-host ~]# kubectl exec -it app -- sh -c 'df -hT --type=overlay'
kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 120G 5.2G 115G 5% /
EBS 볼륨의 AZ 종속성 때문에 PV가 생성될 때 해당 EBS 볼륨이 존재하는 가용영역을 노드 어피니티로 지정하여, 파드가 반드시 같은 가용영역의 노드에만 스케줄링되도록 제한하는 것을 확인할 수 있다.
kubectl get pv -o yaml
...
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2b
...
볼륨 크기 증설
PVC의 spec.resources.requests.storage
필드를 수정하여 손쉽게 볼륨을 늘릴 수 있다. 하지만, 한번 증가된 크기는 다시 줄일 수 없으므로 초기 크기 설정이 중요하다. 또한 allowVolumeExpansion
이 true
로 설정되어 있어야 기능을 사용할 수 있다.
kubectl get pvc ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
볼륨 상태를 보면 optimizing 상태임을 확인할 수 있다. 볼륨 용량 수정이 반영되기 까지는 수치 반영이 조금 느릴 수 있다.


AWS Volume SnapShots Controller
AWS EBS 볼륨의 스냅샷을 관리하는 CSI 컨트롤러이다. 쿠버네티스의 VolumeSnapshot 리소스를 통해 EBS 볼륨의 백업을 자동화하고 관리할 수 있게 해 준다. 볼륨 스냅숏은 볼륨의 특정 시점의 복사본이다. 운영자의 실수로 볼륨을 지우거나, 문제가 생겼을 때 스냅숏을 통해 특정 시점의 볼륨으로 손쉽게 복원이 가능하다. AWS EKS 환경에서는 EBS 볼륨의 스냅숏을 의믜한다.
AWS Volume SnapShots Controller 설치
먼저, Volumesnapshots을 사용하려면 별도의 CRD를 설치해주어야 한다.
# Install Snapshot CRDs
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl get crd | grep snapshot
volumesnapshotclasses.snapshot.storage.k8s.io 2025-02-22T16:42:07Z
volumesnapshotcontents.snapshot.storage.k8s.io 2025-02-22T16:42:09Z
volumesnapshots.snapshot.storage.k8s.io 2025-02-22T16:42:06Z
# Install Common Snapshot Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
kubectl get deploy -n kube-system snapshot-controller
kubectl get pod -n kube-system
# Install Snapshotclass
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml
kubectl get vsclass # 혹은 volumesnapshotclasses
kubectl describe vsclass
볼륨 스냅숏 생성 및 복구 테스트
5초 간격으로 date로그를 출력하는 파드를 생성한다.
# PVC 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOF
kubectl get pvc,pv
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
EOF
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
Sat Feb 22 20:32:10 UTC 2025
Sat Feb 22 20:32:15 UTC 2025
Sat Feb 22 20:32:20 UTC 2025
Sat Feb 22 20:32:25 UTC 2025
스냅샷 생성 및 확인
# VolumeSnapshot 확인
kubectl get volumesnapshot
kubectl get volumesnapshot ebs-volume-snapshot -o jsonpath={.status.boundVolumeSnapshotContentName} ; echo
kubectl describe volumesnapshot.snapshot.storage.k8s.io ebs-volume-snapshot
kubectl get volumesnapshotcontents
# VolumeSnapshot ID 확인
kubectl get volumesnapshotcontents -o jsonpath='{.items[*].status.snapshotHandle}' ; echo
# AWS EBS 스냅샷 확인
aws ec2 describe-snapshots --owner-ids self | jq
aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[]' --output table

강제로 장애 재현 삭제 : 파드와 pvc를 생성하고 이를 재연하기 위해 두 리소스를 모두 지운다.
kubectl delete pod app && kubectl delete pvc ebs-claim
스냅숏으로 복원
datasource로 pvc 재생성
kubectl get pvc,pv
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-snapshot-restored-claim
spec:
storageClassName: gp3
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
dataSource:
name: ebs-volume-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
EOF
# 확인
kubectl get pvc,pv
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
terminationGracePeriodSeconds: 3
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-snapshot-restored-claim
EOF
파드 재생성 이후 파일을 다시 확인해 보면, 파드 삭제 전까지의 저장 기록이 남아 있다. 이후기록도 잘 저장되고 있다

AWS EFS Controller
AWS EFS(Elastic File System)를 쿠버네티스 클러스터에 연결하고 관리하는 CSI 드라이버이다. EBS와 달리 EFS는 여러 가용영역에 걸쳐 여러 파드가 동시에 읽고 쓸 수 있는 네트워크 공유 파일시스템을 제공한다.

EFS CSI Controller 설치
- EFS CSI driver는 EKS addon을 통해 설치를 지원한다.
- 쿠버네티스의 리소스인
ebs-csi-controller
가 AWS의 리소스인 EFS를 제어할 수 있도록 IRSA도 설정한다.
# EFS 정보 확인
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
# 아래는 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
# 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}
# 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
# 확인
eksctl get addon --cluster ${CLUSTER_NAME}
kubectl get pod -n kube-system -l "app.kubernetes.io/name=aws-efs-csi-driver,app.kubernetes.io/instance=aws-efs-csi-driver"
kubectl get pod -n kube-system -l app=efs-csi-controller -o jsonpath='{.items[0].spec.containers[*].name}' ; echo
kubectl get csidrivers efs.csi.aws.com -o yaml
AWS → EFS → 파일 시스템 : 네트워크 확인

정적 프로비저닝(Static Provisioning)
이미 생성된 EFS 파일시스템을 파드에 연결하는 방식이며, 하나의 EFS 공간을 여러 파드가 공유한다.
# 실습 코드 clone
git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git /root/efs-csi
cd /root/efs-csi/examples/kubernetes/multiple_pods/specs && tree
# EFS 스토리지클래스 생성 및 확인
cat storageclass.yaml
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
# PV 생성 및 확인 : volumeHandle을 자신의 EFS 파일시스템ID로 변경
EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml
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-05699d3c12ef609e2
kubectl apply -f pv.yaml
kubectl get pv; kubectl describe pv
cat pod1.yaml pod2.yaml
kubectl apply -f pod1.yaml,pod2.yaml
kubectl get pods
kubectl exec -ti app1 -- sh -c "df -hT -t nfs4"
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
# 공유 저장소 저장 동작 확인
/mnt/myefs
├── memo.txt
├── out1.txt
└── out2.txt
동적프로비저닝(Dynamic Provisioning_
PVC 생성 시 자동으로 EFS 액세스 포인트를 생성하는 방식이며, 액세스 포인트를 통해 각 파드의 권한과 루트 디렉터리를 격리도 가능하다.
# EFS 스토리지클래스 생성 및 확인
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/storageclass.yaml
cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap # The type of volume to be provisioned by Amazon EFS. Currently, only access point based provisioning is supported (efs-ap).
fileSystemId: fs-92107410 # The file system under which the access point is created.
directoryPerms: "700" # The directory permissions of the root directory created by the access point.
gidRangeStart: "1000" # optional, The starting range of the Posix group ID to be applied onto the root directory of the access point. The default value is 50000.
gidRangeEnd: "2000" # optional, The ending range of the Posix group ID. The default value is 7000000.
basePath: "/dynamic_provisioning" # optional, The path on the file system under which the access point root directory is created. If the path isn't provided, the access points root directory is created under the root of the file system.
subPathPattern: "${.PVC.namespace}/${.PVC.name}" # optional, A pattern that describes the subPath under which an access point should be created. So if the pattern were ${.PVC.namespace}/${PVC.name}, the PVC namespace is foo and the PVC name is pvc-123-456, and the basePath is /dynamic_provisioner the access point would be created at /dynamic_provisioner/foo/pvc-123-456
ensureUniqueDirectory: "true" # optional # A boolean that ensures that, if set, a UUID is appended to the final element of any dynamically provisioned path, as in the above example. This can be turned off but this requires you as the administrator to ensure that your storage classes are set up correctly. Otherwise, it's possible that 2 pods could end up writing to the same directory by accident. Please think very carefully before setting this to false!
reuseAccessPoint: "false" # optional
sed -i "s/fs-92107410/$EfsFsId/g" storageclass.yaml
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
# PVC/파드 생성 및 확인
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/pod.yaml
cat pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
---
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; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
kubectl apply -f pod.yaml
kubectl get pvc,pv,pod
# PVC/PV 생성 로그 확인
kubectl krew install stern
kubectl stern -n kube-system -l app=efs-csi-controller -c csi-provisioner
혹은
kubectl logs -n kube-system -l app=efs-csi-controller -c csi-provisioner -f
# 파드 정보 확인
kubectl exec -it efs-app -- 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
# 공유 저장소 저장 동작 확인
tree /mnt/myefs # 운영서버 EC2 에서 확인
kubectl exec efs-app -- bash -c "cat /data/out"
kubectl exec efs-app -- bash -c "ls -l /data/out"
kubectl exec efs-app -- bash -c "stat /data/"
# 공유 저장소 저장 동작 확인
/mnt/myefs
├── dynamic_provisioning
│ └── default
│ └── efs-claim-7ce7df00-4632-4d97-b1f0-44049a861c5b
│ └── out
├── memo.txt
├── out1.txt
└── out2.txt
aws 콘솔에서도 액세스 포인트를 확인할 수 있다.

EKS Persistent Volumes for Instance Store
AWS Instance Store는 EC2 인스턴스에 물리적으로(인스턴스의 로컬) 연결된 임시 스토리지이다.
인스턴스 실행 시 생성되고 종료 시 삭제되는 특성을 가지고 있어, 캐시나 임시 데이터 저장에 적합하며 높은 I/O 성능을 제공한다.
하지만 데이터가 영구적으로 보존되지 않기 때문에, 중요한 데이터는 EBS와 같은 영구 스토리지에 저장해야 한다.

Instance Store 노드 그룹 추가
- c5.large 타입
- 참고로 인스턴스 스토어는 aws 콘솔에 볼륨에서 조회되지는 않는다.
cat << EOF > myng2.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: myeks
region: ap-northeast-2
version: "1.31"
managedNodeGroups:
- amiFamily: AmazonLinux2
desiredCapacity: 1
instanceType: c5d.large
labels:
alpha.eksctl.io/cluster-name: myeks
alpha.eksctl.io/nodegroup-name: ng2
disk: instancestore
maxPodsPerNode: 110
maxSize: 1
minSize: 1
name: ng2
ssh:
allow: true
publicKeyName: $SSHKEYNAME
subnets:
- $PubSubnet1
- $PubSubnet2
- $PubSubnet3
tags:
alpha.eksctl.io/nodegroup-name: ng2
alpha.eksctl.io/nodegroup-type: managed
volumeIOPS: 3000
volumeSize: 30
volumeThroughput: 125
volumeType: gp3
preBootstrapCommands:
- |
# Install Tools
yum install nvme-cli links tree jq tcpdump sysstat -y
# Filesystem & Mount
mkfs -t xfs /dev/nvme1n1
mkdir /data
mount /dev/nvme1n1 /data
# Get disk UUID
uuid=\$(blkid -o value -s UUID mount /dev/nvme1n1 /data)
# Mount the disk during a reboot
echo /dev/nvme1n1 /data xfs defaults,noatime 0 2 >> /etc/fstab
EOF
# 신규 노드 그룹 생성
eksctl create nodegroup -f myng2.yaml
# 확인
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get node -l disk=instancestore
# ng2 노드 그룹 *ng2-remoteAccess* 포함된 보안그룹 ID
aws ec2 describe-security-groups --filters "Name=group-name,Values=*ng2-remoteAccess*" | jq
export NG2SGID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=*ng2-remoteAccess*" --query 'SecurityGroups[*].GroupId' --output text)
aws ec2 authorize-security-group-ingress --group-id $NG2SGID --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32
aws ec2 authorize-security-group-ingress --group-id $NG2SGID --protocol '-1' --cidr 172.20.1.100/32
# 워커 노드 SSH 접속
N4=<각자 자신의 워커 노드4번 공인 IP 지정>
N4=3.37.44.222
ssh ec2-user@$N4 hostname
인스턴스 스토어 경로로 local-path 스토리지 클래스 생성
#
curl -sL https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml | sed 's/opt/data/g' | kubectl apply -f -
kubectl describe cm -n local-path-storage local-path-config
...
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/data/local-path-provisioner"]
}
]
...
kubectl get sc local-path
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 4h
# [운영서버 EC2] Read 측정
kubestr fio -f fio-read.fio -s local-path --size 10G --nodeselector disk=instancestore
...
read:
IOPS=20309.355469 BW(KiB/s)=81237
iops: min=17392 max=93872 avg=20316.857422
bw(KiB/s): min=69570 max=375488 avg=81268.023438
Disk stats (read/write):
nvme1n1: ios=2432488/9 merge=0/3 ticks=7639891/23 in_queue=7639913, util=99.950768%
- OK
kubestr 성능 모니터링 도구
kubestr은 쿠버네티스 클러스터의 스토리지 스토리지 시스템의 I/O 성능(FIO)을 측정하는 벤치마킹 도구이다.

kubestr 설치
# [운영서버 EC2] kubestr 툴 다운로드 - Link
wget https://github.com/kastenhq/kubestr/releases/download/v0.4.48/kubestr_0.4.48_Linux_amd64.tar.gz
tar xvfz kubestr_0.4.48_Linux_amd64.tar.gz && mv kubestr /usr/local/bin/ && chmod +x /usr/local/bin/kubestr
# 스토리지클래스 점검
kubestr -h
kubestr
성능측정
# 랜덤 읽기 성능 테스트 수행 : 3분 정도 소요
# libaio 엔진과 다이렉트 I/O를 사용하여 고성능 스토리지의 랜덤 읽기 성능을 측정.
# OS 캐시를 사용하지 않고 직접 디스크 I/O 수행 (direct 플래그)
cat << EOF > fio-read.fio
[global]
ioengine=libaio
direct=1
bs=4k
runtime=120
time_based=1
iodepth=16
numjobs=4
group_reporting
size=1g
rw=randread
[read]
EOF
kubestr fio -f fio-read.fio -s local-path --size 10G # size 미 지정시 기본 100G로 노드 Disk full 발생하니 유의
결과
EBS 볼륨 측정

속도 측정 3000 iops throughtput(처리량)
Instance Store 볼륨 측정
Instance Store 볼륨의 IOPS의 평균값이 20308이 나오는 것을 볼 수 있습니다 일반 EBS의 기본 IOPS의 값이 3000인데, 무려 6-7배가량 빠른 것을 볼 수 있다.


'AWS > EKS' 카테고리의 다른 글
EKS 서버리스 서비스 간략히 알아보기(Fargate & EKS AutoMode) (0) | 2025.03.23 |
---|---|
EKS Karpenter 기본 사용(Node AutoScaling) (1) | 2025.03.05 |
EKS 기본 네트워크(AWS VPC CNI + AWS LoadBalancer Controller) (0) | 2025.02.16 |
EKS 설치 및 클러스터 엔드포인트(EKS Cluster Endpoint) (2) | 2025.02.09 |
EKS IaC(Terraform) - AEWS 8주차 (1) | 2024.04.28 |