티스토리 뷰
안녕하세요. AWS EKS Workshop Study (=AEWS) 3기 모임에서 스터디한 내용을 정리했습니다. 해당 글에서는 Amzaon EKS Sroage에 대해 자세히 알아보겠습니다.
실습 환경
기본 설정 및 EFS 확인
# yaml 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-3week.yaml
# CloudFormation 스택 배포
# aws cloudformation deploy --template-file myeks-1week.yaml --stack-name mykops --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region <리전>
# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
$ aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text
# 작업용 EC2 SSH 접속
$ ssh -i ~/.ssh/kp-hayley3.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)
$ df -hT --type nfs4
Filesystem Type Size Used Avail Use% Mounted on
fs-0af2a7f5ee43292ef.efs.ap-northeast-2.amazonaws.com:/ nfs4 8.0E 0 8.0E 0% /mnt/myefs
$ mount | grep nfs4
fs-0af2a7f5ee43292ef.efs.ap-northeast-2.amazonaws.com:/ on /mnt/myefs type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,noresvport,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.1.100,local_lock=none,addr=192.168.1.145)
$ echo "EKS Workshop" > /mnt/myefs/memo.txt
AWS LB/ExternalDNS, kube-ops-view 설치
# kube-ops-view : 노드에 설치된 Pod 의 개수를 보기 편하게 해주는 도구
$ helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
$ helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
# AWS LB 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=$CLUSTER_NAME \
--set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
# ExternalDNS
$ MyDomain=<자신의 도메인>
$ MyDomain=wellbeconnected.com
$ MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
$ echo $MyDomain, $MyDnzHostedZoneId
wellbeconnected.com, /hostedzone/Z06204681HPKV2R55I4OR
$ curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
$ MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
deployment.apps/external-dns created
스토리지 이해
- 파드 내부의 데이터는 파드가 정지되면 모두 삭제되기 때문에 파드가 모두 상태가 없는(Stateless) 애플리케이션으로 언제나 종료될 수 있는 것을 감안하여 설계해야 합니다.
[참고 — Kubernetes를 위한 영구 스토리지 적용하기]
Kubernetes의 데이터 지속성
상태 저장 애플리케이션을 실행할 때, 영구 스토리지가 없으면 데이터는 포드 또는 컨테이너의 수명 주기에 연결됩니다. 포드가 충돌하거나 종료되면 데이터는 손실됩니다.
이러한 데이터 손실을 방지하고 Kubernetes에서 상태 저장 애플리케이션을 실행하려면 세 가지 간단한 스토리지 요구 사항을 준수해야 합니다:
- 스토리지는 포드의 수명 주기에 의존해서는 안 됩니다.
- 스토리지는 Kubernetes 클러스터의 모든 포드 및 노드에서 사용할 수 있어야 합니다.
- 스토리지는 충돌이나 애플리케이션 오류에 관계없이 가용성이 높아야 합니다.
Kubernetes볼륨(Kubernetes volumes)
임시 스토리지(Ephemeral storage)
- 컨테이너는 temporary filesystem(tmpfs)를 사용하여 파일을 읽고 쓸 수 있습니다. 그러나 임시 스토리지는 세 가지의 저장소 요구 사항을 충족하지 않습니다. 컨테이너가 충돌하는 경우 temporary filesystem은 손실되고, 컨테이너는 깨끗한 상태로 다시 시작됩니다. 또한 여러 컨테이너가 temporary filesystem을 공유할 수 없습니다.
임시 볼륨(Ephemeral volumes)
- 임시 Kubernetes Volume은 임시 스토리지가 직면한 두 가지 문제를 모두 해결합니다. 임시 Volume의 수명은 Pod와 연결됩니다. 이를 통해 컨테이너를 안전하게 재시작하고 Pod내의 컨테이너간 데이터를 공유할 수 있습니다. 그러나 Pod가 삭제되는 즉시 Volume도 삭제가 되므로, 이는 여전히 세 가지 요구 사항을 충족하지 못합니다.
https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/
- 따라서, 데이터베이스(파드)처럼 데이터 보존이 필요합니다.
상태가 있는(Stateful) 애플리케이션 : PV & PVC
스토리지와 포드 분리: 영구 볼륨(Persistent Volumes)
- Persistent Volumes을 사용하면 애플리케이션, 컨테이너, 포드, 노드 또는 클러스터 자체의 수명 주기와 관계없이 데이터가 지속됩니다.
- Persistent Volume(PV) 객체는 애플리케이션 데이터를 유지하는 데 사용되는 스토리지 볼륨을 나타냅니다. PV는 Kubernetes Pods의 수명 주기와 별개로 자체의 수명 주기를 가집니다.
PV는 기본적으로 두 가지로 구성됩니다.
- PersistentVolume이라고 불리우는 백엔드 기술
- Kubernetes에 볼륨을 마운트하는 방법을 알려주는 접근 모드
백엔드 기술(Backend technology)
- csi: Container Storage Interface(CSI) → (예: Amazon EFS , Amazon EBS , Amazon FSx 등)
- iscsi: iSCSI(SCSI over IP) 스토리지
- local: 노드에 마운트된 로컬 저장 장치
- nfs: 네트워크 파일 시스템(NFS) 스토리지
접근 모드(Access mode)
- ReadWriteOnce: 볼륨은 동시에 하나의 노드에서만 읽기/쓰기를 허용합니다.
- ReadOnlyMany: 볼륨은 동시에 여러 노드에서 읽기 전용 모드를 허용합니다.
- ReadWriteMany: 볼륨은 동시에 여러 노드에서 읽기/쓰기를 허용합니다.
영구 볼륨 클레임(Persistent volume claims)
- Persistent Volume(PV)은 실제 스토리지 볼륨을 나타냅니다. Kubernetes는 PV를 포드에 연결하는데 필요한 추가 추상화 계층인 PersistentVolumeClaim(PVC)을 가지고 있습니다.
- PV 는 실제 스토리지 볼륨을 나타내며, PVC는 Pod가 실제 스토리지를 얻기 위해 수행하는 스토리지 요청을 나타냅니다.
기본적으로 PV 객체를 직접 Pod에 마운트할 수 없습니다. 이것은 명시적으로 요청되어져야 합니다. 그리고 그 요청은 PVC객체를 생성하고 Pod에 연결함으로써 달성됩니다. 이것이 이 추가 추상화 계층이 존재하는 유일한 이유입니다. PVC와 PV는 1:1의 관계가 있습니다(PV는 하나의 PVC에만 연결될 수 있음).
https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/
CSI(Container Storage Interface) 드라이버
- CSI (Container Storage Interface)는 Kubernetes에서 다양한 스토리지 솔루션을 쉽게 사용할 수 있도록 설계된 추상화입니다. 다양한 스토리지 공급업체는 CSI 표준을 구현하는 자체 드라이버를 개발하여 스토리지 솔루션이 Kubernetes와 함께 작동하도록 할 수 있습니다(연결되는 스토리지 솔루션의 내부에 관계없이). AWS는 Amazon EBS , Amazon EFS 및 Amazon FSx for Lustre 용 CSI 플러그인을 제공하고 있습니다.
- Kubernetes 개발자는 Kubernetes 내부에 내장된 provisioner (in-tree)를 모두 삭제하고, 별도의 controller Pod을 통해 동적 provisioning을 사용할 수 있도록 만들었습니다.
정적 프로비저닝(Static provisioning)
- “영구 볼륨 클레임” 섹션에서 설명한 바와 같이, 먼저 관리자가 하나 이상의 PV를 생성하고 애플리케이션 개발자는 PVC를 생성합니다. 이를 정적 프로비저닝이라고 합니다. Kubernetes에서 PV 및 PVC를 수동으로 만들어야 하므로 정적 입니다. 대규모 환경에서는 관리하기가 점점 더 어려워질 수 있으며, 특히 수백 개의 PV와 PVC를 관리하는 경우에는 더욱 그렇습니다.
동적 프로비저닝(Dynamic provisioning)
- 동적 프로비저닝을 사용하면 PV객체를 생성할 필요가 없습니다. 대신에, PVC를 생성할 때 내부적으로 자동으로 생성됩니다. Kubernetes는 Storage Class라는 다른 객체를 사용하여 이를 수행합니다.
- Storage Class는 컨테이너 애플리케이션에 사용되는 백엔드 영구 스토리지(예: Amazon EFS 파일 스토리지, Amazon EBS 블록 스토리지 등)의 클래스를 정의하는 추상화입니다.
- Storage Class 객체는 Kubernetes가 매우 다양한 스토리지 기술을 처리할 수 있는 이유입니다. Pod 관점에서 볼 때, EFS 볼륨, EBS 볼륨, NFS 드라이브 또는 기타 어떤 것이든, 그 Pod는 PVC 객체만 보게 됩니다. 실제 스토리지 기술을 다루는 모든 내부적인 논리는 Storage Class 객체가 사용하는 프로비저너에 의해 구현됩니다.
https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/
- 퍼시스턴트 볼륨의 사용이 끝났을 때 해당 볼륨은 어떻게 초기화할 것인지 별도로 설정할 수 있는데, 쿠버네티스는 이를 Reclaim Policy 라고 부릅니다.
- CSI 를 사용하면, K8S 의 공통화된 CSI 인터페이스를 통해 다양한 프로바이더를 사용할 수 있습니다.
- 일반적인 CSI driver의 구조입니다. AWS EBS CSI driver 역시 아래와 같은 구조를 가지는데, 오른쪽 StatefulSet 또는 Deployment로 배포된 controller Pod이 AWS API를 사용하여 실제 EBS volume을 생성하는 역할을 합니다. 왼쪽 DaemonSet으로 배포된 node Pod은 AWS API를 사용하여 Kubernetes node (EC2 instance)에 EBS volume을 attach 해줍니다.
Node-specific Volume Limits
- AWS EC2 Type에 따라 볼륨 최대 제한 : 25개 ~ 39개
# 확인
$ kubectl describe node | grep Allocatable: -A1
Allocatable:
attachable-volumes-aws-ebs: 25
--
기본 컨테이너 환경의 임시 파일시스템 사용
# 파드 배포
# date 명령어로 현재 시간을 10초 간격으로 /home/pod-out.txt 파일에 저장
$ curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/date-busybox-pod.yaml
$ cat date-busybox-pod.yaml | yh
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
terminationGracePeriodSeconds: 3
containers:
- name: busybox
image: busybox
command:
- "/bin/sh"
- "-c"
- "while true; do date >> /home/pod-out.txt; cd /home; sync; sync; sleep 10; done"
$ kubectl apply -f date-busybox-pod.yaml
# 파일 확인
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 8s
$ kubectl exec busybox -- tail -f /home/pod-out.txt
# 파드 삭제 후 다시 생성 후 파일 정보 확인 > 이전 기록이 보존되어 있는지?
# 아니다. PV가 필요한 이유
kubectl delete pod busybox
kubectl apply -f date-busybox-pod.yaml
kubectl exec busybox -- tail -f /home/pod-out.txt
# 실습 완료 후 삭제
$ kubectl delete pod busybox
호스트 Path 를 사용하는 PV/PVC : local-path-provisioner 스트리지 클래스 배포
# 배포
$ curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
$ kubectl apply -f local-path-storage.yaml
namespace/local-path-storage created
serviceaccount/local-path-provisioner-service-account created
clusterrole.rbac.authorization.k8s.io/local-path-provisioner-role created
clusterrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
deployment.apps/local-path-provisioner created
storageclass.storage.k8s.io/local-path created
configmap/local-path-config created
# 확인
$ kubectl get-all -n local-path-storage
NAME NAMESPACE AGE
configmap/kube-root-ca.crt local-path-storage 20s
configmap/local-path-config local-path-storage 20s
pod/local-path-provisioner-759f6bd7c9-mz2jq local-path-storage 20s
serviceaccount/default local-path-storage 20s
serviceaccount/local-path-provisioner-service-account local-path-storage 20s
deployment.apps/local-path-provisioner local-path-storage 20s
replicaset.apps/local-path-provisioner-759f6bd7c9 local-path-storage 20s
$ kubectl get pod -n local-path-storage -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
local-path-provisioner-759f6bd7c9-mz2jq 1/1 Running 0 21s 192.168.1.94 ip-192-168-1-59.ap-northeast-2.compute.internal <none> <none>
$ kubectl describe cm -n local-path-storage local-path-config
Name: local-path-config
Namespace: local-path-storage
Labels: <none>
Annotations: <none>
Data
====
helperPod.yaml:
----
apiVersion: v1
kind: Pod
metadata:
name: helper-pod
spec:
containers:
- name: helper-pod
image: busybox
imagePullPolicy: IfNotPresent
setup:
----
#!/bin/sh
set -eu
mkdir -m 0777 -p "$VOL_DIR"
teardown:
----
#!/bin/sh
set -eu
rm -rf "$VOL_DIR"
config.json:
----
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
]
}
BinaryData
====
Events: <none>
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 118m
local-path rancher.io/local-path Delete WaitForFirstConsumer false 85s
$ kubectl get sc local-path
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 100s
PV/PVC 를 사용하는 파드 생성
# PVC 생성
$ curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath1.yaml
$ cat localpath1.yaml | yh
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: localpath-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: "local-path"
$ kubectl apply -f localpath1.yaml
# PVC 확인
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
localpath-claim Bound pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e 1Gi RWO local-path 21m
$ kubectl describe pvc
# 파드 생성
$ curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath2.yaml
$ cat localpath2.yaml | yh
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: localpath-claim
$ kubectl apply -f localpath2.yaml
# 파드 확인
$ kubectl get pod,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/app 1/1 Running 0 21m
pod/busybox 1/1 Running 0 2m23s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e 1Gi RWO Delete Bound default/localpath-claim local-path 21m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/localpath-claim Bound pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e 1Gi RWO local-path 22m
$ kubectl describe pv # Node Affinity 확인 : 해당 Node에 PV가 존재함
$ kubectl exec -it app -- tail -f /data/out.txt
...
# 워커노드 중 현재 파드가 배포되어 있다만, 아래 경로에 out.txt 파일 존재 확인
$ ssh ec2-user@$N3 tree /opt/local-path-provisioner
/opt/local-path-provisioner
└── pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e_default_localpath-claim
└── out.txt
1 directory, 1 file
# 해당 워커노드 자체에서 out.txt 파일 확인 : 아래 굵은 부분은 각자 실습 환경에 따라 다름
$ ssh ec2-user@$N1 tail -f /opt/local-path-provisioner/pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e_default_localpath-claim/out.txt
...
파드 삭제 후 파드 재생성해서 데이터 유지 되는지 확인
# 파드 삭제 후 PV/PVC 확인
$ kubectl delete pod app
$ kubectl get pod,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/busybox 1/1 Running 0 4m29s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e 1Gi RWO Delete Bound default/localpath-claim local-path 23m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/localpath-claim Bound pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e 1Gi RWO local-path 24m
$ ssh ec2-user@$N3 tree /opt/local-path-provisioner
/opt/local-path-provisioner
└── pvc-917aead0-f9bc-4cb2-9e50-b5d6da11078e_default_localpath-claim
└── out.txt
1 directory, 1 file
# 파드 다시 실행
kubectl apply -f localpath2.yaml
# 확인
$ kubectl exec -it app -- head /data/out.txt
$ kubectl exec -it app -- tail -f /data/out.txt
# 다음 실습을 위해서 파드와 PVC 삭제
kubectl delete pod app
kubectl get pv,pvc
kubectl delete pvc localpath-claim
# 확인
$ kubectl get pv
No resources found
$ ssh ec2-user@$N3 tree /opt/local-path-provisioner
/opt/local-path-provisioner
0 directories, 0 files
AWS EBS Controller
- AWS CSI 드라이버는 크게 2개 구성요소가 있습니다. AWS API를 호출하면서 AWS 스토리지를 관리하는 CSI-Controller와 kubelet과 상호작용하면서 AWS스토리지를 pod에 마운트하는 CSI-Node가 있습니다.
- persistentvolume, persistentvolumeclaim의 accessModes는 ReadWriteOnce로 설정해야 합니다. EBS스토리지 기본 설정이 동일 AZ에 있는 EC2 인스턴스만 연결 할 수 있기 때문입니다.
설치 Amazon EBS CSI driver as an Amazon EKS add-on
# 아래는 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
...
# IRSA 설정 : 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
# IRSA 확인
$ kubectl get sa -n kube-system ebs-csi-controller-sa -o yaml | head -5
$ eksctl get iamserviceaccount --cluster eks-hayley
NAMESPACE NAME ROLE ARN
kube-system ebs-csi-controller-sa arn:aws:iam::911283464785:role/AmazonEKS_EBS_CSI_DriverRole
...
# Amazon EBS CSI driver addon 추가
$ 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
# 확인
$ eksctl get addon --cluster ${CLUSTER_NAME}
$ kubectl get deploy,ds -l=app.kubernetes.io/name=aws-ebs-csi-driver -n kube-system
$ kubectl get pod -n kube-system -l 'app in (ebs-csi-controller,ebs-csi-node)'
NAME READY STATUS RESTARTS AGE
ebs-csi-controller-5d4666c6f8-rs7v9 6/6 Running 0 40s
ebs-csi-controller-5d4666c6f8-zqcsg 6/6 Running 0 40s
ebs-csi-node-6b946 3/3 Running 0 40s
ebs-csi-node-gd549 3/3 Running 0 40s
ebs-csi-node-glql8 3/3 Running 0 40s
$ kubectl get pod -n kube-system -l app.kubernetes.io/component=csi-driver
NAME READY STATUS RESTARTS AGE
ebs-csi-controller-5d4666c6f8-rs7v9 6/6 Running 0 52s
ebs-csi-controller-5d4666c6f8-zqcsg 6/6 Running 0 52s
ebs-csi-node-6b946 3/3 Running 0 52s
ebs-csi-node-gd549 3/3 Running 0 52s
ebs-csi-node-glql8 3/3 Running 0 52s
# 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
# csinodes 확인
$ kubectl get csinodes
NAME DRIVERS AGE
ip-192-168-1-59.ap-northeast-2.compute.internal 1 172m
ip-192-168-2-120.ap-northeast-2.compute.internal 1 172m
ip-192-168-3-239.ap-northeast-2.compute.internal 1 172m
# gp3 스토리지 클래스 생성
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 3h3m
local-path rancher.io/local-path Delete WaitForFirstConsumer false 66m
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
allowAutoIOPSPerGBIncrease: 'true'
encrypted: 'true'
EOT
$ kubectl apply -f gp3-sc.yaml
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 3h3m
gp3 ebs.csi.aws.com Delete WaitForFirstConsumer true 1s
local-path rancher.io/local-path Delete WaitForFirstConsumer false 66m
$ kubectl describe sc gp3 | grep Parameters
Parameters: allowAutoIOPSPerGBIncrease=true,encrypted=true,type=gp3
PVC/PV 파드 테스트
# 워커노드의 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
[
{
"VolumeId": "vol-0b5a582d3b2847e61",
"VolumeType": "gp3",
"InstanceId": "i-02eb4047eb5f2679e",
"State": "attached"
},
{
"VolumeId": "vol-07f37bcab8743bbff",
"VolumeType": "gp3",
"InstanceId": "i-0afbf50137104fc24",
"State": "attached"
},
{
"VolumeId": "vol-0d3037972ed279ab3",
"VolumeType": "gp3",
"InstanceId": "i-03faaddd636088a40",
"State": "attached"
}
]
# 워커노드에서 파드에 추가한 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
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/ebs-claim Pending gp3 4s
# 파드 생성
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
# 추가된 EBS 볼륨 상세 정보 확인
$ aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq
# PV 상세 확인 : nodeAffinity 내용의 의미는? 해당 AZ에 배포
kubectl get pv -o yaml | yh
...
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2c
...
$ 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
PV NAME PVC NAME NAMESPACE NODE NAME POD NAME VOLUME MOUNT NAME SIZE USED AVAILABLE %USED IUSED IFREE %IUSED
pvc-c1dfda9b-e4a0-4f5d-8655-fd9d2561865d ebs-claim default ip-192-168-3-239.ap-northeast-2.compute.internal app persistent-storage 3Gi 28Ki 3Gi 0.00 12 262132 0.00
## 파드 내에서 볼륨 정보 확인
$ kubectl exec -it app -- sh -c 'df -hT --type=overlay'
kubectl exec -it app -- sh -c 'df -hT --type=ext4'Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 30G 3.7G 27G 13% /
$ kubectl exec -it app -- sh -c 'df -hT --type=ext4'
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme1n1 ext4 3.8G 28K 3.8G 1% /data
볼륨 증가 — 링크 ⇒ 늘릴수는 있어도 줄일수는 없다
# 현재 pv 의 이름을 기준하여 4G > 10G 로 증가 : .spec.resources.requests.storage의 4Gi 를 10Gi로 변경
$ kubectl get pvc ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
4Gi
$ kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
4Gi
$ kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
persistentvolumeclaim/ebs-claim patched
$ kubectl patch pvc ebs-claim -p '{"status":{"capacity":{"storage":"10Gi"}}}' # status 는 바로 위 커멘드 적용 후 EBS 10Gi 확장 후 알아서 10Gi 반영됨
# 확인 : 볼륨 용량 수정 반영이 되어야 되니, 수치 반영이 조금 느릴수 있다
$ kubectl exec -it app -- sh -c 'df -hT --type=ext4'
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme1n1 ext4 3.8G 28K 3.8G 1% /data
$ kubectl df-pv
PV NAME PVC NAME NAMESPACE NODE NAME POD NAME VOLUME MOUNT NAME SIZE USED AVAILABLE %USED IUSED IFREE %IUSED
pvc-c1dfda9b-e4a0-4f5d-8655-fd9d2561865d ebs-claim default ip-192-168-3-239.ap-northeast-2.compute.internal app persistent-storage 3Gi 28Ki 3Gi 0.00 12 262132 0.00
$ aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq
# 삭제
$ kubectl delete pod app & kubectl delete pvc ebs-claim
AWS Volume SnapShots Controller
Volumesnapshots 컨트롤러 설치
# (참고) EBS CSI Driver에 snapshots 기능 포함 될 것으로 보임
$ kubectl describe pod -n kube-system -l app=ebs-csi-controller
# Install Snapshot CRDs
$ curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
$ curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
$ curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
$ kubectl apply -f snapshot.storage.k8s.io_volumesnapshots.yaml,snapshot.storage.k8s.io_volumesnapshotclasses.yaml,snapshot.storage.k8s.io_volumesnapshotcontents.yaml
$ kubectl get crd | grep snapshot
$ kubectl api-resources | grep snapshot
volumesnapshotclasses vsclass,vsclasses snapshot.storage.k8s.io/v1 false VolumeSnapshotClass
volumesnapshotcontents vsc,vscs snapshot.storage.k8s.io/v1 false VolumeSnapshotContent
volumesnapshots vs snapshot.storage.k8s.io/v1 true VolumeSnapshot
# Install Common Snapshot Controller
$ curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
$ curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
$ kubectl apply -f rbac-snapshot-controller.yaml,setup-snapshot-controller.yaml
$ kubectl get deploy -n kube-system snapshot-controller
NAME READY UP-TO-DATE AVAILABLE AGE
snapshot-controller 0/2 2 0 1s
$ kubectl get pod -n kube-system -l app=snapshot-controller
NAME READY STATUS RESTARTS AGE
snapshot-controller-76494bf6c9-66r5t 1/1 Running 0 7s
snapshot-controller-76494bf6c9-tl6pg 1/1 Running 0 7s
# Install Snapshotclass
$ curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml
$ kubectl apply -f snapshotclass.yaml
$ kubectl get vsclass # 혹은 volumesnapshotclasses
NAME DRIVER DELETIONPOLICY AGE
csi-aws-vsc ebs.csi.aws.com Delete 1s
사용 예 : 테스트 PVC/파드 생성
# PVC 생성
$ kubectl apply -f awsebs-pvc.yaml
# 파드 생성
$ kubectl apply -f awsebs-pod.yaml
# 파일 내용 추가 저장 확인
$ kubectl exec app -- tail -f /data/out.txt
# VolumeSnapshot 생성 : Create a VolumeSnapshot referencing the PersistentVolumeClaim name >> EBS 스냅샷 확인
$ curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ebs-volume-snapshot.yaml
$ cat ebs-volume-snapshot.yaml | yh
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: ebs-volume-snapshot
spec:
volumeSnapshotClassName: csi-aws-vsc
source:
persistentVolumeClaimName: ebs-claim
$ kubectl apply -f ebs-volume-snapshot.yaml
# 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
NAME READYTOUSE RESTORESIZE DELETIONPOLICY DRIVER VOLUMESNAPSHOTCLASS VOLUMESNAPSHOT VOLUMESNAPSHOTNAMESPACE AGE
snapcontent-cf84e78b-d6ac-415f-ad95-a4e506699e42 true 4294967296 Delete ebs.csi.aws.com csi-aws-vsc ebs-volume-snapshot default 30s
# VolumeSnapshot ID 확인
$ kubectl get volumesnapshotcontents -o jsonpath='{.items[*].status.snapshotHandle}' ; echo
snap-0997465c9230e316f
# AWS EBS 스냅샷 확인
aws ec2 describe-snapshots --owner-ids self | jq
aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[]' --output table
# app & pvc 제거 : 강제로 장애 재현
$ kubectl delete pod app && kubectl delete pvc ebs-claim
스냅샷으로 복원
# 스냅샷에서 PVC 로 복원
$ kubectl get pvc,pv
No resources found
$ cat <<EOT > ebs-snapshot-restored-claim.yaml
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
EOT
$ cat ebs-snapshot-restored-claim.yaml | yh
$ kubectl apply -f ebs-snapshot-restored-claim.yaml
persistentvolumeclaim/ebs-snapshot-restored-claim created
# 확인
$ kubectl get pvc,pv
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/ebs-snapshot-restored-claim Pending gp3 5m
# 파드 생성
$ curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ebs-snapshot-restored-pod.yaml
$ cat ebs-snapshot-restored-pod.yaml | yh
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
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
$ kubectl apply -f ebs-snapshot-restored-pod.yaml
# 파일 내용 저장 확인 : 파드 삭제 전까지의 저장 기록이 남아 있다. 이후 파드 재생성 후 기록도 잘 저장되고 있다
$ kubectl exec app -- cat /data/out.txt
# 삭제
$ kubectl delete pod app && kubectl delete pvc ebs-snapshot-restored-claim && kubectl delete volumesnapshots ebs-volume-snapshot
AWS EFS Controller
EFS 파일시스템 확인 및 EFS Controller 설치
# EFS 정보 확인
$ aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
fs-0af2a7f5ee43292ef
# IAM 정책 생성
$ curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/docs/iam-policy-example.json
$ aws iam create-policy --policy-name AmazonEKS_EFS_CSI_Driver_Policy --policy-document file://iam-policy-example.json
# 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::${ACCOUNT_ID}:policy/AmazonEKS_EFS_CSI_Driver_Policy \
--approve
# ISRA 확인
$ 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::90XXXXXX:role/eksctl-eks-hayley-addon-iamserviceaccount-ku-Role1-1BY3E5T889ZJI
$ eksctl get iamserviceaccount --cluster eks-hayley
NAMESPACE NAME ROLE ARN
kube-system aws-load-balancer-controller arn:aws:iam::90XXXXXX:role/eksctl-eks-hayley-addon-iamserviceaccount-ku-Role1-AMBNTVWDKQD5
kube-system ebs-csi-controller-sa arn:aws:iam::90XXXXXX:role/AmazonEKS_EBS_CSI_DriverRole
kube-system efs-csi-controller-sa arn:aws:iam::90XXXXXX:role/eksctl-eks-hayley-addon-iamserviceaccount-ku-Role1-1BY3E5T889ZJI
# EFS Controller 설치
$ helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
$ helm repo update
$ helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
--namespace kube-system \
--set image.repository=602401143452.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/eks/aws-efs-csi-driver \
--set controller.serviceAccount.create=false \
--set controller.serviceAccount.name=efs-csi-controller-sa
# 확인
$ helm list -n kube-system
$ 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-6f64dcc5dc-fdrns 3/3 Running 0 39s
efs-csi-controller-6f64dcc5dc-rcfkc 3/3 Running 0 39s
efs-csi-node-8wqcw 3/3 Running 0 39s
efs-csi-node-9sl2m 3/3 Running 0 39s
efs-csi-node-xbjmn 3/3 Running 0 39s
AWS → EFS → 파일 시스템 : 네트워크 → 탑재 대상 ID 확인
EFS 파일시스템을 다수의 파드가 사용하게 설정 : Workshop 링크
# 모니터링
watch 'kubectl get sc efs-sc; echo; kubectl get pv,pvc,pod'
# 실습 코드 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 | yh
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 | yh
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
# PVC 생성 및 확인
cat claim.yaml | yh
kubectl apply -f claim.yaml
kubectl get pvc
# 파드 생성 및 연동 : 파드 내에 /data 데이터는 EFS를 사용
cat pod1.yaml pod2.yaml | yh
kubectl apply -f pod1.yaml,pod2.yaml
kubectl df-pv
# 파드 정보 확인 : PV에 5Gi 와 파드 내에서 확인한 NFS4 볼륨 크리 8.0E의 차이는 무엇? 파드에 6Gi 이상 저장 가능한가?
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
# 공유 저장소 저장 동작 확인
tree /mnt/myefs # 작업용EC2에서 확인
tail -f /mnt/myefs/out1.txt # 작업용EC2에서 확인
kubectl exec -ti app1 -- tail -f /data/out1.txt
kubectl exec -ti app2 -- tail -f /data/out2.txt
EFS 파일시스템을 다수의 파드가 사용하게 설정 : Dynamic provisioning using EFS ← Fargate node는 현재 미지원 — Workshop
# 모니터링
$ watch 'kubectl get sc efs-sc; echo; kubectl get pv,pvc,pod'
# 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 | yh
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-92107410
directoryPerms: "700"
gidRangeStart: "1000" # optional
gidRangeEnd: "2000" # optional
basePath: "/dynamic_provisioning" # optional
$ sed -i "s/fs-92107410/$EfsFsId/g" storageclass.yaml
$ kubectl apply -f storageclass.yaml
$ kubectl get sc efs-sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
efs-sc efs.csi.aws.com Delete Immediate false 1s
# 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 | yh
$ kubectl apply -f pod.yaml
$ kubectl get pvc,pv,pod
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/efs-claim Pending efs-sc 76s
NAME READY STATUS RESTARTS AGE
pod/busybox 1/1 Running 0 96m
pod/efs-app 0/1 Pending 0 76s
# PVC/PV 생성 로그 확인
$ 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"
- EFS → 액세스 포인트 확인
EKS Persistent Volumes for Instance Store & Add NodeGroup
신규 노드 그룹 ng2 생성 — Blog : c5d.large 의 EC2 인스턴스 스토어(임시 블록 스토리지) 설정 작업
c5d.large 는 인스턴스 스토어(임시블록스토리지)를 가지고 있습니다.
인스턴스 스토어는 EC2 스토리지(EBS) 정보에 출력되지는 않습니다.
# 인스턴스 스토어 볼륨이 있는 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.12xlarge | 1800 |
| c5d.large | 50 |
| c5d.24xlarge | 3600 |
| c5d.4xlarge | 400 |
| c5d.18xlarge | 1800 |
| c5d.2xlarge | 200 |
| c5d.metal | 3600 |
| c5d.xlarge | 100 |
| c5d.9xlarge | 900 |
+---------------+--------+
# 신규 노드 그룹 생성
$ 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=192.168.2.41
ssh ec2-user@$N4 hostname
# 확인
$ ssh ec2-user@$N4 sudo nvme list
Node SN Model Namespace Usage Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 vol0a9b1f26cbac3e225 Amazon Elastic Block Store 1 32.21 GB / 32.21 GB 512 B + 0 B 1.0
/dev/nvme1n1 AWS26F6377433BB12A04 Amazon EC2 NVMe Instance Storage 1 50.00 GB / 50.00 GB 512 B + 0 B 0
$ ssh ec2-user@$N4 sudo lsblk -e 7 -d
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1 259:0 0 30G 0 disk
nvme1n1 259:1 0 46.6G 0 disk /data
$ ssh ec2-user@$N4 sudo df -hT -t xfs
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p1 xfs 30G 3.3G 27G 11% /
/dev/nvme1n1 xfs 47G 365M 47G 1% /data
$ ssh ec2-user@$N4 sudo tree /data
/data
0 directories, 0 files
$ ssh ec2-user@$N4 sudo cat /etc/fstab
#
UUID=0ccd3c9e-3e0f-4e59-ae3c-9498ef40c541 / xfs defaults,noatime 1 1
/dev/nvme1n1 /data xfs defaults,noatime 0 2
# (옵션) max-pod 확인
$ kubectl describe node -l disk=nvme | grep Allocatable: -A7
Allocatable:
attachable-volumes-aws-ebs: 25
cpu: 1930m
ephemeral-storage: 27905944324
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3068880Ki
pods: 100
# (옵션) kubelet 데몬 파라미터 확인 : --max-pods=29 --max-pods=100
ssh ec2-user@$N4 sudo ps -ef | grep kubelet
root 2973 1 1 13:22 ? 00:00:04 /usr/bin/kubelet --config /etc/kubernetes/kubelet/kubelet-config.json --kubeconfig /var/lib/kubelet/kubeconfig --container-runtime-endpoint unix:///run/containerd/containerd.sock --image-credential-provider-config /etc/eks/image-credential-provider/config.json --image-credential-provider-bin-dir /etc/eks/image-credential-provider --node-ip=192.168.2.41 --pod-infra-container-image=602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/eks/pause:3.5 --v=2 --cloud-provider=aws --container-runtime=remote --node-labels=eks.amazonaws.com/sourceLaunchTemplateVersion=1,alpha.eksctl.io/cluster-name=eks-hayley,alpha.eksctl.io/nodegroup-name=ng2,disk=nvme,eks.amazonaws.com/nodegroup-image=ami-02ddbc7e7f404a16a,eks.amazonaws.com/capacityType=ON_DEMAND,eks.amazonaws.com/nodegroup=ng2,eks.amazonaws.com/sourceLaunchTemplateId=lt-02f535365a4e4c3e5 --max-pods=29 --max-pods=100스토리지 클래스 재생성
local-path 스토리지 클래스 재생성 : 패스 변경
# 기존 삭제
#curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
$ 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
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
nvme1n1 0.00 0.00 0.43 0.42 0.01 0.04 123.87 0.00 0.20 0.06 0.35 0.33 0.03
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
# kubestr 툴 다운로드
$ wget https://github.com/kastenhq/kubestr/releases/download/v0.4.37/kubestr_0.4.37_Linux_amd64.tar.gz
$ tar xvfz kubestr_0.4.37_Linux_amd64.tar.gz && mv kubestr /usr/local/bin/ && chmod +x /usr/local/bin/kubestr
# 측정 : 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
PVC created kubestr-fio-pvc-zbzd6
Pod created kubestr-fio-pod-67q27
Running FIO test (fio-read.fio) on StorageClass (local-path) with a PVC of Size (10G)
Elapsed time- 3m42.735408402s
FIO test results:
FIO version - fio-3.30
Global options - ioengine=libaio verify= direct=1 gtod_reduce=
JobName:
blocksize= filesize= iodepth= rw=
read:
IOPS=20310.097656 BW(KiB/s)=81240
iops: min=15878 max=94036 avg=20318.447266
bw(KiB/s): min=63512 max=376144 avg=81274.265625
Disk stats (read/write):
nvme1n1: ios=2432736/10 merge=0/3 ticks=7639458/30 in_queue=7639488, util=99.958275%
- OK
Node Group
AWS Graviton (ARM) Instance 소개 - Github , Blog
AWS Graviton Migration Experience
AWS Graviton 마이그레이션 경험
symplesims.github.io
#
kubectl get nodes -L kubernetes.io/arch
# 신규 노드 그룹 생성
eksctl create nodegroup --help
eksctl create nodegroup -c $CLUSTER_NAME -r ap-northeast-2 --subnet-ids "$PubSubnet1","$PubSubnet2","$PubSubnet3" \
-n ng3 -t t4g.medium -N 1 -m 1 -M 1 --node-volume-size=30 --node-labels family=graviton --dry-run > myng3.yaml
cat myng3.yaml
eksctl create nodegroup -f myng3.yaml
# 확인
kubectl get nodes --label-columns eks.amazonaws.com/nodegroup,kubernetes.io/arch,eks.amazonaws.com/capacityType
kubectl describe nodes --selector family=graviton
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name ng3 | jq .nodegroup.taints
# taints 셋팅 -> 적용에 2~3분 정도 시간 소요
aws eks update-nodegroup-config --cluster-name $CLUSTER_NAME --nodegroup-name ng3 --taints "addOrUpdateTaints=[{key=frontend, value=true, effect=NO_EXECUTE}]"
# 확인
kubectl describe nodes --selector family=graviton | grep Taints
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name ng3 | jq .nodegroup.taints
# NO_SCHEDULE - This corresponds to the Kubernetes NoSchedule taint effect. This configures the managed node group with a taint that repels all pods that don't have a matching toleration. All running pods are not evicted from the manage node group's nodes.
# NO_EXECUTE - This corresponds to the Kubernetes NoExecute taint effect. Allows nodes configured with this taint to not only repel newly scheduled pods but also evicts any running pods without a matching toleration.
# PREFER_NO_SCHEDULE - This corresponds to the Kubernetes PreferNoSchedule taint effect. If possible, EKS avoids scheduling Pods that do not tolerate this taint onto the node.
Spot 노드 그룹
#
kubectl get nodes -l eks.amazonaws.com/capacityType=ON_DEMAND
kubectl get nodes -L eks.amazonaws.com/capacityType
NAME STATUS ROLES AGE VERSION CAPACITYTYPE
ip-192-168-1-65.ap-northeast-2.compute.internal Ready <none> 75m v1.28.5-eks-5e0fdde ON_DEMAND
ip-192-168-2-89.ap-northeast-2.compute.internal Ready <none> 75m v1.28.5-eks-5e0fdde ON_DEMAND
ip-192-168-3-39.ap-northeast-2.compute.internal Ready <none> 75m v1.28.5-eks-5e0fdde ON_DEMAND
# 노드 그룹 생성
NODEROLEARN=$(aws iam list-roles --query "Roles[?contains(RoleName, 'nodegroup-ng1')].Arn" --output text)
echo $NODEROLEARN
aws eks create-nodegroup \
--cluster-name $CLUSTER_NAME \
--nodegroup-name managed-spot \
--subnets $PubSubnet1 $PubSubnet2 $PubSubnet3 \
--node-role $NODEROLEARN \
--instance-types c5.large c5d.large c5a.large \
--capacity-type SPOT \
--scaling-config minSize=2,maxSize=3,desiredSize=2 \
--disk-size 20
# The command can be used to wait until a specific EKS node group is active and ready for use.
aws eks wait nodegroup-active --cluster-name $CLUSTER_NAME --nodegroup-name managed-spot
# 확인
kubectl get nodes -L eks.amazonaws.com/capacityType,eks.amazonaws.com/nodegroup
NAME STATUS ROLES AGE VERSION CAPACITYTYPE NODEGROUP
ip-192-168-1-229.ap-northeast-2.compute.internal Ready <none> 102s v1.31.5-eks-5d632ec SPOT managed-spot
ip-192-168-1-68.ap-northeast-2.compute.internal Ready <none> 87m v1.31.5-eks-5d632ec ON_DEMAND ng1
ip-192-168-2-138.ap-northeast-2.compute.internal Ready <none> 103s v1.31.5-eks-5d632ec SPOT managed-spot
ip-192-168-2-27.ap-northeast-2.compute.internal Ready <none> 88m v1.31.5-eks-5d632ec ON_DEMAND ng1
ip-192-168-3-183.ap-northeast-2.compute.internal Ready <none> 87m v1.31.5-eks-5d632ec ON_DEMAND ng1
자원 삭제
- Amazon EKS 클러스터 삭제(10분 정도 소요)
eksctl delete cluster --name $CLUSTER_NAME
- (클러스터 삭제 완료 확인 후) AWS CloudFormation 스택 삭제
aws cloudformation delete-stack --stack-name myeks
'IT > Infra&Cloud' 카테고리의 다른 글
[aws] EKS Networking (0) | 2025.02.16 |
---|---|
[aws] Amzaon EKS 설치 및 기본 사용 (0) | 2025.02.09 |
[gcp] Gemini (0) | 2024.08.04 |
[gcp] Security Command Center(Detective Controls) (0) | 2024.07.15 |
[gcp] Cloud Identity SSO 설정 (0) | 2024.07.15 |
- Total
- Today
- Yesterday
- GCP
- S3
- 도서
- k8s calico
- 혼공파
- NFT
- GKE
- AI
- 혼공챌린지
- cni
- k8s cni
- 혼공단
- gcp serverless
- operator
- VPN
- k8s
- SDWAN
- 파이썬
- terraform
- security
- IaC
- EKS
- cloud
- AWS
- handson
- NW
- 국제 개발 협력
- OS
- PYTHON
- controltower
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |