EKS Storage
💡Stateless 한 Pod 기본적으로 pod는 상태를 유지하지 않는다. 즉, pod 정지 및 삭제 시 pod 안에 있는 데이터가 모두 삭제가 된다. 따라서 데이터 보존이 필요하다면 Storage를 pod에 mount하여 따로 백업을 해야 한다. 외부 데이터베이스, Cloud Storage, PV(Persistent Volume), PVC(Persistent Volume Claim), 외부 파일 시스템 마운트 등의 방법이 있다.
Volume 및 Storage 특징
Volume Type

- emptyDir:
- 임시적인 스토리지를 제공합니다. Pod이 생성되고 삭제될 때까지만 데이터가 유지됩니다.
- 여러 컨테이너 간 데이터를 공유할 때 유용합니다.
- 클러스터 노드의 임시 디렉토리에 저장됩니다.
- hostPath:
- 호스트 노드의 파일 시스템 경로를 Pod에 마운트합니다.
- 호스트 노드 간에 데이터를 공유할 때 사용됩니다.
- 데이터 보존 및 복구에 유용하지만, 클러스터화된 환경에서는 권장되지 않습니다.
- PV/PVC (Persistent Volume/Persistent Volume Claim):
- 영구적인 스토리지를 제공하며, 클러스터 외부에 있는 외부 스토리지를 사용할 수 있습니다.
- 다양한 스토리지 솔루션을 통합할 수 있으며, 동적 프로비저닝과 스토리지 클래스를 통해 관리됩니다.
- 볼륨의 생명주기와 볼륨에 대한 요구사항을 정의하는 데 사용됩니다.
다양한 스토리지 솔루션

- Kubernetes 자체 제공 (hostPath, local):
- 간편하게 사용할 수 있으며, 클러스터의 노드에 직접 액세스할 수 있습니다.
- 단점으로는 클러스터 노드 간에 데이터 공유가 어렵고, 데이터 보존이 제한적일 수 있습니다.
- 온프렘 솔루션 (예: Ceph 등):
- 자체 데이터 센터에 있는 스토리지 솔루션을 사용할 수 있습니다.
- 클러스터화된 환경에서 안정적이고 확장 가능한 스토리지를 제공할 수 있습니다.
- NFS (Network File System):
- 네트워크를 통해 파일 시스템을 공유하는 서비스입니다.
- 여러 노드 간에 데이터를 공유하고, 확장성과 유연성을 제공합니다.
- 클라우드 스토리지 (예: AWS EBS 등):
- 클라우드 제공업체가 제공하는 스토리지 서비스를 사용할 수 있습니다.
- 클라우드 벤더의 관리 및 유지보수가 용이하며, 확장성과 가용성을 제공합니다.
동적 프로비저닝 & 볼륨 상태, ReclaimPolicy:

- 동적 프로비저닝(Dynamic Provisioning):
- PV/PVC를 사용하여 Pod에 필요한 스토리지를 동적으로 프로비저닝하는 기능입니다.
- 요청된 볼륨 사양에 따라 스토리지 클래스에 의해 자동으로 볼륨이 프로비저닝됩니다.
- 볼륨 상태 및 ReclaimPolicy:
- PV는 다양한 상태를 가질 수 있으며, 해당 PV의 ReclaimPolicy에 따라 삭제된 후에도 데이터가 보존되거나 삭제됩니다.
- ReclaimPolicy는 PV가 삭제되었을 때의 동작을 정의합니다. 예를 들어,
Delete
옵션을 사용하면 PV에 연결된 스토리지를 삭제할 수 있습니다.
실습 환경

(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get sc #여기서 sc는 storage class 의 줄임
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 98m
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get sc gp2 -o yaml | yh
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"name":"gp2"},"parameters":{"fsType":"ext4","type":"gp2"},"provisioner":"kubernetes.io/aws-ebs","volumeBindingMode":"WaitForFirstConsumer"}
storageclass.kubernetes.io/is-default-class: "true"
creationTimestamp: "2024-03-23T08:43:26Z"
name: gp2
resourceVersion: "272"
uid: 8f3a25fa-8961-4ce0-976d-e335b408fa74
parameters:
fsType: ext4
type: gp2
provisioner: kubernetes.io/aws-ebs
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
- StorageClass 이름:
gp2
- 프로비저너(Provisioner):
kubernetes.io/aws-ebs
- 이 스토리지 클래스는 AWS의 Elastic Block Store (EBS)를 프로비저닝하는 데 사용됩니다.
- 리클레임 정책(Reclaim Policy):
Delete
- 이 스토리지 클래스를 사용하여 프로비저닝된 볼륨이 삭제될 때, 해당 볼륨의 데이터가 삭제됩니다.
- 볼륨 바인딩 모드(Volume Binding Mode):
WaitForFirstConsumer
- 이 스토리지 클래스를 사용하여 프로비저닝된 볼륨은 첫 번째 사용자가 바인딩할 때까지 대기합니다.
- 파라미터(Parameters):
fsType: ext4
: 프로비저닝된 볼륨의 파일 시스템 유형은 ext4입니다.type: gp2
: EBS 스토리지 유형은 gp2입니다.
- 생성 시간(Creation Timestamp): 2024년 3월 23일에 생성되었습니다.
- 자원 버전(Resource Version): 현재 스토리지 클래스의 자원 버전은 “272”입니다.
- 고유 식별자(UID): 스토리지 클래스의 고유 식별자는
8f3a25fa-8961-4ce0-976d-e335b408fa74
입니다. - 주석(Annotations):
kubectl.kubernetes.io/last-applied-configuration
: 마지막 적용된 구성 정보입니다.storageclass.kubernetes.io/is-default-class
: 이 스토리지 클래스가 기본 클래스로 설정되어 있음을 나타냅니다. (true)
❓StorageClass gp2 gp2는 General Purpose SSD(Solid State Drive)의 약어로, 일반적인 워크로드에 적합한 SSD 스토리지 유형입니다. gp2 볼륨은 안정적인 성능과 비용 효율성을 제공하며, 주로 일반적인 애플리케이션 및 데이터베이스 용도로 사용됩니다. Kubernetes에서 AWS의 gp2 스토리지를 사용할 때, 해당 스토리지를 프로비저닝하기 위해 AWS EBS (Elastic Block Store) CSI 드라이버를 사용할 수 있습니다. 여기서 CSI드라이버란 컨테이너와 외부 스토리지 시스템 간의 통합을 위한 표준 인터페이스 입니다. 표준화, 확장성, 유연성, 간편한 업그레이드 및 유지보수의 이유로 사용합니다.
K8S host Path VS
Local Path Provisioner
hostPath:
- 특징:
- 호스트 머신의 파일 시스템 경로를 직접 마운트하여 Pod에 볼륨을 제공합니다.
- 클러스터 노드의 로컬 파일 시스템에 직접 액세스할 수 있습니다.
- 주로 개발 및 테스트 환경에서 사용되며, 간단한 구성이 가능합니다.
- 장점:
- 쉽게 구성할 수 있고, 별도의 스토리지 시스템이 필요하지 않습니다.
- 빠른 성능을 제공할 수 있습니다.
- 단일 노드 환경에서 효과적으로 사용될 수 있습니다.
- 단점:
- 호스트 노드에 의존하기 때문에 이식성이 낮고, 여러 노드 간에 볼륨을 공유할 수 없습니다.
- 클러스터 노드 간에 Pod를 이동시키거나 복제할 때 데이터의 유실이 발생할 수 있습니다.
- 스케일링 및 고가용성을 위한 클러스터 구성에는 적합하지 않습니다.
Local Path Provisioner (StorageClass 제공):
- 특징:
- Kubernetes에서 제공하는 StorageClass를 사용하여 로컬 디스크를 동적으로 프로비저닝합니다.
- 각 노드에 있는 로컬 디스크를 사용하여 볼륨을 프로비저닝하며, Pod에 마운트됩니다.
- 로컬 디스크를 동적으로 할당하므로 별도의 스토리지 시스템이 필요하지 않습니다.
- 장점:
- 로컬 디스크를 동적으로 할당하여 스토리지를 프로비저닝하므로 호스트 경로에 대한 의존성이 없습니다.
- 클러스터의 여러 노드에 볼륨을 공급할 수 있으며, Pod의 이동 및 복제에 대해 더 안전합니다.
- 로컬 디스크를 사용하여 빠른 성능을 제공할 수 있습니다.
- 단점:
- 로컬 디스크를 사용하므로 확장성이 제한될 수 있습니다. 클러스터에 새로운 노드를 추가할 때 스토리지 용량이 부족할 수 있습니다.
- 로컬 디스크의 유실에 대비한 데이터 백업 및 복구 전략이 필요합니다.
- 클러스터 전체에서 일관된 스토리지 관리가 어려울 수 있습니다.
- hostPath는 간단하고 빠르게 설정할 수 있으며, 단일 노드 환경에서 적합합니다. 하지만 이식성이 낮고, 고가용성 및 스케일링에 적합하지 않습니다.
- Local Path Provisioner는 로컬 디스크를 동적으로 프로비저닝하여 클러스터의 여러 노드에 볼륨을 공급할 수 있으며, 이동성 및 안정성 측면에서 더 우수합니다. 하지만 로컬 디스크에 대한 데이터 관리와 확장성에 대한 고려가 필요합니다.
1. Kubernetes Local Path 실습 환경 구성
apiVersion: v1
kind: Namespace
metadata:
name: local-path-storage
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: local-path-provisioner-role
namespace: local-path-storage
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: local-path-provisioner-role
rules:
- apiGroups: [""]
resources: ["nodes", "persistentvolumeclaims", "configmaps", "pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "patch", "update", "delete"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: local-path-provisioner-bind
namespace: local-path-storage
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: local-path-provisioner-role
subjects:
- kind: ServiceAccount
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: local-path-provisioner-bind
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: local-path-provisioner-role
subjects:
- kind: ServiceAccount
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: local-path-provisioner
namespace: local-path-storage
spec:
replicas: 1
selector:
matchLabels:
app: local-path-provisioner
template:
metadata:
labels:
app: local-path-provisioner
spec:
serviceAccountName: local-path-provisioner-service-account
containers:
- name: local-path-provisioner
image: rancher/local-path-provisioner:master-head
imagePullPolicy: IfNotPresent
command:
- local-path-provisioner
- --debug
- start
- --config
- /etc/config/config.json
volumeMounts:
- name: config-volume
mountPath: /etc/config/
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumes:
- name: config-volume
configMap:
name: local-path-config
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
---
kind: ConfigMap
apiVersion: v1
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |-
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
]
}
setup: |-
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"
teardown: |-
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"
helperPod.yaml: |-
apiVersion: v1
kind: Pod
metadata:
name: helper-pod
spec:
priorityClassName: system-node-critical
tolerations:
- key: node.kubernetes.io/disk-pressure
operator: Exists
effect: NoSchedule
containers:
- name: helper-pod
image: busybox
imagePullPolicy: IfNotPresent
- Namespace: local-path-storage:
- 이 부분은 Kubernetes에서 사용되는 네임스페이스를 정의합니다. 다른 리소스들이 이 네임스페이스 안에서 생성됩니다.
- ServiceAccount: local-path-provisioner-service-account:
- Kubernetes에서 사용되는 서비스 계정을 정의합니다. 이 서비스 계정은 볼륨 프로비저닝을 위한 권한을 가지게 됩니다.
- Role: local-path-provisioner-role:
- Kubernetes에서 사용되는 역할(Role)을 정의합니다. 이 역할은 특정 작업에 대한 권한을 부여합니다.
- ClusterRole: local-path-provisioner-role:
- Kubernetes 클러스터 전체에서 사용되는 역할을 정의합니다. 역시 특정 작업에 대한 권한을 부여합니다.
- RoleBinding: local-path-provisioner-bind:
- 서비스 계정과 역할을 연결하여 해당 서비스 계정에 역할의 권한을 부여합니다.
- ClusterRoleBinding: local-path-provisioner-bind:
- 클러스터 역할과 서비스 계정을 연결하여 해당 서비스 계정에 클러스터 역할의 권한을 부여합니다.
- Deployment: local-path-provisioner:
- 애플리케이션을 배포하기 위한 Kubernetes의 Deployment를 정의합니다. 이 경우 로컬 디스크를 사용하여 볼륨을 프로비저닝하는 로컬 경로 프로비저너를 배포합니다.
- Deployment는 하나의 Pod을 관리하며, 이 Pod은 local-path-provisioner 이미지를 사용합니다.
- Pod 내부에는
local-path-provisioner
컨테이너가 정의되어 있으며, 이 컨테이너는/etc/config/
디렉토리에 마운트된 설정 파일(config.json
)을 사용하여 로컬 경로 프로비저너를 실행합니다.
- StorageClass: local-path:
- 볼륨을 프로비저닝하는데 사용되는 스토리지 클래스를 정의합니다. 이 스토리지 클래스는 local-path-provisioner를 사용하여 로컬 디스크 공간을 프로비저닝합니다.
- ConfigMap: local-path-config:
- 설정 정보를 포함하는 ConfigMap을 정의합니다. 이 ConfigMap에는 로컬 경로 프로비저너의 설정 파일(
config.json
)과 로컬 경로 프로비저너의 설치 및 제거에 필요한 스크립트가 포함되어 있습니다.
- 설정 정보를 포함하는 ConfigMap을 정의합니다. 이 ConfigMap에는 로컬 경로 프로비저너의 설정 파일(
2. pvc pod 생성
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: localpath-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: "local-path"
3. pod 생성 및 삭제 진행하면서 데이터 유지되는지 확인

EKS – EBS Controller
Amazon EBS CSI driver as an Amazon EKS add-on
설치
(pak8266@myeks:default) [root@myeks-bastion ~]# aws eks describe-addon-versions \
> --addon-name aws-ebs-csi-driver \
> --kubernetes-version 1.28 \
> --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
> --output text
v1.28.0-eksbuild.1
True
v1.27.0-eksbuild.1
False
(pak8266@myeks:default) [root@myeks-bastion ~]# 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
2024-03-24 00:15:53 [ℹ] 1 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller) will be excluded
2024-03-24 00:15:53 [ℹ] 1 iamserviceaccount (kube-system/ebs-csi-controller-sa) was included (based on the include/exclude rules)
2024-03-24 00:15:53 [!] serviceaccounts in Kubernetes will not be created or modified, since the option --role-only is used
2024-03-24 00:15:53 [ℹ] 1 task: { create IAM role for serviceaccount "kube-system/ebs-csi-controller-sa" }
2024-03-24 00:15:53 [ℹ] building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2024-03-24 00:15:53 [ℹ] deploying stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2024-03-24 00:15:53 [ℹ] waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2024-03-24 00:16:23 [ℹ] waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
(pak8266@myeks:default) [root@myeks-bastion ~]# eksctl get iamserviceaccount --cluster myeks
NAMESPACE NAME ROLE ARN
kube-system aws-load-balancer-controller arn:aws:iam::159088646233:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-WAOH1sNH2E1M
kube-system ebs-csi-controller-sa arn:aws:iam::159088646233:role/AmazonEKS_EBS_CSI_DriverRole
(pak8266@myeks:default) [root@myeks-bastion ~]# 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
2024-03-24 00:19:27 [ℹ] Kubernetes version "1.28" in use by cluster "myeks"
2024-03-24 00:19:27 [ℹ] using provided ServiceAccountRoleARN "arn:aws:iam::159088646233:role/AmazonEKS_EBS_CSI_DriverRole"
2024-03-24 00:19:27 [ℹ] creating addon
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get sa -n kube-system ebs-csi-controller-sa -o yaml | head -5
apiVersion: v1
automountServiceAccountToken: true
kind: ServiceAccount
metadata:
annotations:
(pak8266@myeks:default) [root@myeks-bastion ~]# eksctl get addon --cluster ${CLUSTER_NAME}
2024-03-24 00:19:54 [ℹ] Kubernetes version "1.28" in use by cluster "myeks"
2024-03-24 00:19:54 [ℹ] getting all addons
2024-03-24 00:19:55 [ℹ] to see issues for an addon run `eksctl get addon --name <addon-name> --cluster <cluster-name>`
NAME VERSION STATUS ISSUES IAMROLE UPDATE AVAILABLE CONFIGURATION VALUES
aws-ebs-csi-driver v1.28.0-eksbuild.1 CREATING 0 arn:aws:iam::159088646233:role/AmazonEKS_EBS_CSI_DriverRole
coredns v1.10.1-eksbuild.7 ACTIVE 0
kube-proxy v1.28.6-eksbuild.2 ACTIVE 0
vpc-cni v1.17.1-eksbuild.1 ACTIVE 0 arn:aws:iam::159088646233:role/eksctl-myeks-addon-vpc-cni-Role1-3zKeRHu5zLI9enableNetworkPolicy: "true"
(pak8266@myeks:default) [root@myeks-bastion ~]# 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 30s
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 30s
daemonset.apps/ebs-csi-node-windows 0 0 0 0 0 kubernetes.io/os=windows 30s
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get pod -n kube-system -l 'app in (ebs-csi-controller,ebs-csi-node)'
NAME READY STATUS RESTARTS AGE
ebs-csi-controller-765cf7cf9-55br7 6/6 Running 0 30s
ebs-csi-controller-765cf7cf9-6tst9 6/6 Running 0 30s
ebs-csi-node-6wk49 3/3 Running 0 31s
ebs-csi-node-lnz9d 3/3 Running 0 31s
ebs-csi-node-wwwpz 3/3 Running 0 31s
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get pod -n kube-system -l app.kubernetes.io/component=csi-driver
NAME READY STATUS RESTARTS AGE
ebs-csi-controller-765cf7cf9-55br7 6/6 Running 0 31s
ebs-csi-controller-765cf7cf9-6tst9 6/6 Running 0 31s
ebs-csi-node-6wk49 3/3 Running 0 32s
ebs-csi-node-lnz9d 3/3 Running 0 32s
ebs-csi-node-wwwpz 3/3 Running 0 32s
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get csinodes
NAME DRIVERS AGE
ip-192-168-1-29.ap-northeast-2.compute.internal 1 6h30m
ip-192-168-2-97.ap-northeast-2.compute.internal 1 6h30m
ip-192-168-3-162.ap-northeast-2.compute.internal 1 6h30m
(pak8266@myeks:default) [root@myeks-bastion ~]#
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 6h39m
local-path rancher.io/local-path Delete WaitForFirstConsumer false 154m
(pak8266@myeks:default) [root@myeks-bastion ~]# cat <<EOT > gp3-sc.yaml
> kind: StorageClass
> apiVersion: storage.k8s.io/v1
> metadata:
> name: gp3
> allowVolumeExpansion: true
> provisioner: ebs.csi.aws.com
> volumeBindingMode: WaitForFirstConsumer
> parameters:
> type: gp3
> #iops: "5000"
> #throughput: "250"
> allowAutoIOPSPerGBIncrease: 'true'
> encrypted: 'true'
> fsType: xfs # 기본값이 ext4
>
EOT
(pak8266@myeks:default) [root@myeks-bastion ~]# cat gp3-sc.yaml | yh
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: gp3
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
allowAutoIOPSPerGBIncrease: 'true'
encrypted: 'true'
fsType: xfs # 기본값이 ext4
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl apply -f gp3-sc.yaml
storageclass.storage.k8s.io/gp3 created
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 6h40m
gp3 ebs.csi.aws.com Delete WaitForFirstConsumer true 6s
local-path rancher.io/local-path Delete WaitForFirstConsumer false 155m
(pak8266@myeks:default) [root@myeks-bastion ~]# kubectl describe sc gp3 | grep Parameters
Parameters: allowAutoIOPSPerGBIncrease=true,encrypted=true,fsType=xfs,type=gp3
aws eks describe-addon-versions
- AWS EBS CSI 드라이버의 버전 및 호환성을 확인
- 해당 명령어는 Kubernetes 버전 1.28에서의 AWS EBS CSI 드라이버의 호환성을 확인
eksctl create iamserviceaccount
- EKS(IAM Service Account, ISRA)를 생성
- 여기서는
ebs-csi-controller-sa
라는 이름의 IAM Service Account를 kube-system 네임스페이스에 생성하고, - 해당 서비스 계정에 AmazonEKS_EBS_CSI_DriverRole IAM 역할을 연결.
eksctl create addon
- eksctl을 사용하여 AWS EBS CSI 드라이버를 EKS 클러스터에 추가
- 이 명령은 EKS 클러스터에 AWS EBS CSI 드라이버를 설치하고 활성화
kubectl get sa
- kube-system 네임스페이스에서
ebs-csi-controller-sa
라는 서비스 계정을 조회
- kube-system 네임스페이스에서
kubectl get deploy,ds
- kube-system 네임스페이스에서 AWS EBS CSI 드라이버와 관련된 Deployment 및 DaemonSet을 조회
kubectl get pod
- kube-system 네임스페이스에서 AWS EBS CSI 드라이버와 관련된 Pod을 조회
kubectl get csinodes
- CSI 드라이버를 사용하는 노드를 조회
kubectl get sc
- 현재 사용 가능한 스토리지 클래스를 조회
cat <<EOT > gp3-sc.yaml
- gp3 스토리지 클래스를 정의하는 YAML 작성
kubectl apply -f gp3-sc.yaml
- gp3 스토리지 클래스를 Kubernetes 클러스터에 배포
- gp3 스토리지 클래스가 생성되고 사용 가능
💡gp2 와 gp3 차이점 gp3는 IOPS 및 처리량을 조정할 수 있는 기능을 제공합니다. 이는 워크로드의 요구 사항에 따라 스토리지의 성능을 조정할 수 있어 더 유연한 운영을 가능하게 합니다. 반면에 gp2는 성능 조정 기능이 없으며, 고정된 성능을 제공합니다.
Test
# 워커노드의 EBS 볼륨 확인 : tag(키/값) 필터링 - 링크
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --output table
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[*].Attachments" | jq
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[*].{ID:VolumeId,Tag:Tags}" | jq
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[].[VolumeId, VolumeType, Attachments[].[InstanceId, State][]][]" | jq
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq
# 워커노드에서 파드에 추가한 EBS 볼륨 확인
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --output table
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[*].{ID:VolumeId,Tag:Tags}" | jq
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq
# 워커노드에서 파드에 추가한 EBS 볼륨 모니터링
while true; do aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" --output text; date; sleep 1; done
# PVC 생성
cat <<EOT > awsebs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
EOT
kubectl apply -f awsebs-pvc.yaml
kubectl get pvc,pv
# 파드 생성
cat <<EOT > awsebs-pod.yaml
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
EOT
kubectl apply -f awsebs-pod.yaml
# PVC, 파드 확인
kubectl get pvc,pv,pod
kubectl get VolumeAttachment
# 추가된 EBS 볼륨 상세 정보 확인
aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq
# PV 상세 확인 : nodeAffinity 내용의 의미는?
kubectl get pv -o yaml | yh
...
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2b
...
kubectl get node --label-columns=topology.ebs.csi.aws.com/zone,topology.kubernetes.io/zone
kubectl describe node | more
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
# 아래 명령어는 확인까지 다소 시간이 소요됨
kubectl df-pv
## 파드 내에서 볼륨 정보 확인
kubectl exec -it app -- sh -c 'df -hT --type=overlay'
kubectl exec -it app -- sh -c 'df -hT --type=xfs'

EKS Persistent Volumes for Instance Store & Add NodeGroup
💡Node가 EC2로 되어있고 EC2도 일시적이며 인스턴스가 종료되면 데이터가 소멸되므로 Persistent Volumes for Instance Store를 사용하여 이러한 인스턴스 스토어에 영구적인 스토리지를 사용하여 데이터를 영구적으로 유지하는 방법이다.
# 인스턴스 스토어 볼륨이 있는 c5 모든 타입의 스토리지 크기
aws ec2 describe-instance-types \
--filters "Name=instance-type,Values=c5*" "Name=instance-storage-supported,Values=true" \
--query "InstanceTypes[].[InstanceType, InstanceStorageInfo.TotalSizeInGB]" \
--output table
--------------------------
| DescribeInstanceTypes |
+---------------+--------+
| c5d.large | 50 |
| c5d.12xlarge | 1800 |
...
# 신규 노드 그룹 생성
eksctl create nodegroup --help
eksctl create nodegroup -c $CLUSTER_NAME -r $AWS_DEFAULT_REGION --subnet-ids "$PubSubnet1","$PubSubnet2","$PubSubnet3" --ssh-access \
-n ng2 -t c5d.large -N 1 -m 1 -M 1 --node-volume-size=30 --node-labels disk=nvme --max-pods-per-node 100 --dry-run > myng2.yaml
cat <<EOT > nvme.yaml
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
EOT
sed -i -n -e '/volumeType/r nvme.yaml' -e '1,$p' myng2.yaml
eksctl create nodegroup -f myng2.yaml
# 노드 보안그룹 ID 확인
NG2SGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng2* --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NG2SGID --protocol '-1' --cidr 192.168.1.100/32
# 워커 노드 SSH 접속
N4=<각자 자신의 워커 노드4번 Private IP 지정>
N4=192.168.3.160
ssh ec2-user@$N4 hostname
# 확인
ssh ec2-user@$N4 sudo nvme list
ssh ec2-user@$N4 sudo lsblk -e 7 -d
ssh ec2-user@$N4 sudo df -hT -t xfs
ssh ec2-user@$N4 sudo tree /data
ssh ec2-user@$N4 sudo cat /etc/fstab
# 기존 삭제
#curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
cd
kubectl delete -f local-path-storage.yaml
#
sed -i 's/opt/data/g' local-path-storage.yaml
kubectl apply -f local-path-storage.yaml
# 모니터링
watch 'kubectl get pod -owide;echo;kubectl get pv,pvc'
ssh ec2-user@$N4 iostat -xmdz 1 -p nvme1n1
# 측정 : Read
#curl -s -O https://raw.githubusercontent.com/wikibook/kubepractice/main/ch10/fio-read.fio
kubestr fio -f fio-read.fio -s local-path --size 10G --nodeselector disk=nvme
