이번 게시글은 가시다님의 AEWS [2기] 스터디 내용을 정리한 포스트 입니다.
이번 게시글은 3 주차의 스터디 내용인 EKS Storage와 NodeGroup에 대해 살펴봅니다.
Kubernetes Storage
쿠버네티스의 최소 단위 배포 단위인 Pod는 1개 이상의 컨테이너로 구성됩니다. 컨테이너는 기본적으로 데이터의 영속성을 보장하지 않는데요. 이 말은 컨테이너의 재기동, 혹은 중지 이벤트가 발생하면 내부의 데이터가 손실되는 휘발성 저장소로 구성된다는 점 입니다. 따라서 컨테이너 엔진인 Docker Engine에서도 `Volume` 옵션을 제공하여 로컬호스트의 파일시스템과 컨테이너의 볼륨을 마운트하여 데이터의 영속성을 보장하게 됩니다.
docker run -v <호스트 경로>:<컨테이너 경로> <이미지 이름>
쿠버네티스는 다양한 볼륨 설정을 구성할 수 있는데요 이번 게시글에서는 파드의 라이프사이클과 데이터 영역을 별도로 관리할 수 있는 PV, PVC에 대해 살펴보도록 하겠습니다.
우선 Persistent Volume은 파드에 장착(마운트)할 수 있는 물리적인 볼륨 단위를 의미합니다. PV를 생성하여 파드가 사용할 수 있는 물리 저장 공간을 생성 해준다고 이해하면 됩니다. Persistent Volume Claims은 파드가 실질적으로 어떤 PV를 사용하겠다. 라는걸 요청하는 Kubernetes의 추상화된 리소스 입니다. PV, PVC를 분리 관리한 이유에 대해선 아래와 같이 설명하고 있습니다. [1]
PV와 PVC의 구분은 Kubernetes 환경에서 두 가지 유형의 사용자가 있다는 개념과 관련이 있습니다.
Kubernetes 관리자: 이 사용자는 클러스터를 유지 관리하고 운영하며 영구 스토리지와 같은 계산 리소스를 추가합니다.
Kubernetes 애플리케이션 개발자: 이 사용자는 애플리케이션을 개발하고 배포합니다.
간단히 말해서, 개발자는 관리자가 제공하는 계산 리소스를 사용합니다. Kubernetes는 PV객체는 클러스터 관리자 범위에 속하고, 반면에 PVC 객체는 애플리케이션 개발자 범위에 속해야 한다는 개념으로 구축되었습니다.
Storage Class
Storage Class 없이 파드의 외부 저장소를 구현하기 위해선 수동으로 PV를 생성한 뒤 PVC를 생성하여 사용해야 합니다. 하지만 Storage Class를 사용하면 PVC에 대한 선언 후 바로 사용할 수 있습니다. 이를 동적 프로비저닝 이라고 부릅니다.
즉, PVC에 반드시 필요한 PV에 대한 부분을 사전 명세 해두어 PVC를 생성하기만 해도 Storage Class에 명시된 스펙을 기반으로 PV를 동적으로 프로비전 해 줍니다. 이번 실습에서는 `local-path`, `gp3`, `efs-sc` 등을 사용합니다.
그럼 실습을 통해 EKS 환경의 PV,PVC를 구현해보겠습니다.
EKS 배포
실습을 위해 EKS 클러스터를 배포합니다. AWS 콘솔을 로그인 한 뒤 CloudFormation에서 아래 YAML 파일로 스택을 생성하시면 됩니다. 진행 과정은 아래 링크를 참조해 주세요.
확인 및 기본설정
Bastion EC2 인스턴스의 공인 IP로 SSH 접근을 시도합니다. 공인 IP는 CloudFormation의 결과에 나오는 출력값 혹은 EC2 메뉴에서 bastion 호스트의 공인 IP를 확인합니다.
# ssh -i "Keyname" ec2-user@"Public IP"
ssh -i hj42700eks.pem ec2-user@3.36.133.15
아래 명령어로 기본 스토리지 클래스를 확인합니다. EKS는 gp2 스토리지 클래스를 기본으로 제공합니다.
kubectl get sc
스토리지 클래스의 yaml을 확인하여 상세 정보를 확인할 수 있습니다.
kubectl get sc gp2 -o yaml | yh
아래 명령을 통해 Bastion 호스트에서 각 워커노드로 패킷 송,수신 SSH 접속이 가능하도록 설정합니다.
# 노드 IP 확인 및 PrivateIP 변수 지정
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3
# 노드 보안그룹 ID 확인
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32
# 워커 노드 SSH 접속
for node in $N1 $N2 $N3; do ssh ec2-user@$node hostname; done
실습하기
임시 파일시스템 사용
PV,PVC를 적용하기 전 임시 파일시스템을 통해 파드(컨테이너)의 재기동 후에도 데이터를 보존하는지 확인해보겠습니다. 아래 명령을 통해 현재 시간을 10초 간격으로 /home/pod-out.txt 경로에 저장하는 파드를 배포합니다.
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/date-busybox-pod.yaml
kubectl apply -f date-busybox-pod.yaml
아래 명령을 통해 /home/pod-out.txt 경로에 시간을 저장하고 있는지 확인합니다.
kubectl exec busybox -- tail -f /home/pod-out.txt
아래 명령을 통해 파드 삭제 및 재배포 후 /home/pod-out.txt 경로에 시간을 저장하고 있는지 확인합니다. 파드의 라이프 사이클과 같이 재기동 되며 데이터가 사라진 것을 확인할 수 있습니다.
kubectl delete pod busybox
kubectl apply -f date-busybox-pod.yaml
kubectl exec busybox -- tail -f /home/pod-out.txt
호스트 Path 를 사용하는 PV/PVC
local-path-provisioner 스트리지 클래스를 사용하여 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
배포된 스토리지 클래스 정보를 확인합니다. 해당 스토리지 클래스는 local-path-storage 라는 파드를 배포하며, configmap을 통해 노드의 특정 경로를 볼륨 생성에 사용하는 것을 확인할 수 있습니다.
kubectl get sc local-path
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 22m
kubectl describe cm -n local-path-storage local-path-config
...생략
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
...
먼저 local-path 스토리지 클래스를 사용하는 PVC를 배포합니다.
# PVC 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath1.yaml
kubectl apply -f localpath1.yaml
localpath1.yaml 파일을 확인 해보면 localpath-claim 이름을 사용하는 PVC를 생성하고 local-path 스토리지 클래스를 사용하는 것을 확인할 수 있으며, 앞서 언급드린 것처럼 스토리지 클래스를 사용하기에 수동으로 PV를 생성할 필요는 없습니다.
# PVC 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath1.yaml
kubectl apply -f localpath1.yaml
cat localpath1.yaml | yh
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: localpath-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: "local-path"
아래 명령을 통해 PVC에 아직 파드를 연결하지 않아 Pending 상태인 것을 확인할 수 있습니다.
kubectl get pv,pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/localpath-claim Pending local-path 3m33s
아래 명령을 통해 PVC를 사용하는 파드를 배포합니다. 해당 파드는 `spec.volumes`를 통해 localpath-claim PVC와 연결하고 `spec.containers.volumeMounts`를 통해 컨테이너의 /data 경로에 마운트 하는 것을 확인할 수 있습니다.
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath2.yaml
kubectl apply -f 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
파드가 정상 배포된 후 PV,PVC의 상태 변화를 체크해 보겠습니다. 기존 Pending 이었던 PVC는 Bound로 STATUS 가 바뀌었고, 1Gi의 PV가 자동으로 생성된 것을 확인할 수 있습니다.
kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-2b6e6d4a-c410-4050-8464-d31039f6211a 1Gi RWO Delete Bound default/localpath-claim local-path 2m26s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/localpath-claim Bound pvc-2b6e6d4a-c410-4050-8464-d31039f6211a 1Gi RWO local-path 6m52s
아래 명령을 통해 특정 경로에 시간을 저장하고 있는지 확인합니다.
kubectl exec -it app -- tail -f /data/out.txt
아래 명령을 통해 워커노드의 특정 경로에 파드가 사용하는 볼륨이 생성되었는지 확인합니다. 위 local-path-provisioner를 생성할 시 confingmap에 정의되었던 워커노드 경로에 파일이 생성 된 것을 확인할 수 있습니다.
for node in $N1 $N2 $N3; do ssh ec2-user@$node tree /opt/local-path-provisioner; done
아래의 명령을 통해 파드의 재기동시에도 데이터가 보존되는지 확인합니다. 기존 데이터를 유지하고, 새롭게 시간을 저장하는 것을 확인할 수 있습니다.
kubectl delete pod app
kubectl apply -f localpath2.yaml
kubectl exec -it app -- head /data/out.txt
kubectl exec -it app -- tail -f /data/out.txt
다음 실습을 위해 삭제합니다.
kubectl delete pod app
kubectl get pv,pvc
kubectl delete pvc localpath-claim
AWS EBS Controller
이번 실습에는 AWS EBS 볼륨을 PV 저장소로 사용해 보도록 하겠습니다. 우선적으로 EKS 클러스터에 EBS CSI driver를 설치해야 합니다. 이는 EKS에서 AWS의 리소스인 EBS의 생성, 연결, 삭제 등 전반적인 라이프사이클을 관리 해 줍니다. 아래 그림을 보면 csi-controller는 API Server의 PV,PVC 생성 요청을 감지하고, AWS API에 EBS 생성을 요청합니다. csi-node는 kubelet의 mount 요청을 감지하여 AWS상의 EBS와 파드를 연결하는 것을 확인할 수 있습니다.
아래 명령을 통해 EBS CSI driver를 EKS 클러스터에 설치합니다.
# 아래는 aws-ebs-csi-driver 전체 버전 정보와 기본 설치 버전(True) 정보 확인
aws eks describe-addon-versions \
--addon-name aws-ebs-csi-driver \
--kubernetes-version 1.28 \
--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
# ISRA 확인
eksctl get iamserviceaccount --cluster myeks
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
kubectl get sa -n kube-system ebs-csi-controller-sa -o yaml | head -5
# 확인
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)'
kubectl get pod -n kube-system -l app.kubernetes.io/component=csi-driver
# 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
# gp3 스토리지 클래스 생성
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'
fsType: xfs # 기본값이 ext4
EOT
kubectl apply -f gp3-sc.yaml
kubectl get sc
kubectl describe sc gp3 | grep Parameters
EBS PV를 사용하기 전 AWS 콘솔에서 현재 사용중인 디스크를 확인합니다. Bastion 1대, Worker Node 3대, 총 4개의 EBS 볼륨을 사용중인것을 확인할 수 있습니다.
아래 명령을 통해 EBS gp3 유형을 사용하는 PVC 및 파드를 생성합니다.
# 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
AWS 콘솔의 볼륨 메뉴에 PVC를 통해 생성된 디스크를 확인할 수 있습니다.
아래 명령을 통해 파드의 속성에 Node Affinity가 걸린것을 확인할 수 있습니다. EBS 볼륨은 가용영역에 종속적이기 때문에 파드가 최초 ap-northeast-2b 영역의 워커노드에 배포 되었다면, 재기동 되더라도 해당 영역에 배포되야 접근 가능하기 때문입니다.
kubectl get pv -o yaml | yh
...
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2b
...
아래 명령을 통해 파드에 연결된 EBS의 볼륨사이즈를 증가시킬 수 있습니다. 기존 4GiB에서 10GiB로 변경 된것을 확인할 수 있습니다.
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"}}}}'
아래 명령을 통해 실습에 사용한 리소스를 삭제합니다.
kubectl delete pod app & kubectl delete pvc ebs-claim
AWS Volume SnapShots Controller
Volumesnapshots을 설치하면 파드에 연결된 PV(EBS),PVC에 대한 스냅샷 생성이 가능합니다. 우선 아래 명령을 통해 컨트롤러를 설치합니다.
# 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
# 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
kubectl get pod -n kube-system -l app=snapshot-controller
# 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
아래 명령을 통해 PVC, Pod를 배포합니다.
# PVC 생성
kubectl apply -f awsebs-pvc.yaml
# 파드 생성
kubectl apply -f awsebs-pod.yaml
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
아래 명령을 통해 Volumesnapshot을 생성하고 AWS 콘솔에 스냅샷이 생성되었는지 확인합니다.
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ebs-volume-snapshot.yaml
cat ebs-volume-snapshot.yaml | yh
kubectl apply -f ebs-volume-snapshot.yaml
아래 명령을 통해 기존 PVC와 Pod를 삭제하고 스냅샷을 통해 복구해보도록 하겠습니다.
kubectl delete pod app && kubectl delete pvc ebs-claim
스냅샷을 불러와 PVC, Pod를 생성합니다.
# 스냅샷에서 PVC 로 복원
kubectl get pvc,pv
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
# 확인
kubectl get pvc,pv
# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ebs-snapshot-restored-pod.yaml
cat ebs-snapshot-restored-pod.yaml | yh
kubectl apply -f ebs-snapshot-restored-pod.yaml
# 파일 내용 저장 확인 : 파드 삭제 전까지의 저장 기록이 남아 있다. 이후 파드 재생성 후 기록도 잘 저장되고 있다
kubectl exec app -- cat /data/out.txt
AWS EFS Controller
앞서 EBS Controller를 사용해 PV,PVC를 생성해 보았습니다. 이번에는 AWS 스토리지 서비스 중 하나인 EFS를 사용한 PV,PVC 연결을 진행해 보겠습니다. EFS 스토리지는 다중 가용영역에 배포된 파드간 볼륨 공유를 위해 사용됩니다. EBS는 특정 가용역역에 배포된 파드만 연결 가능한 반면 EFS는 가용 영역에 한정되지 않게 접근 가능합니다.
EFS도 EBS와 동일하게 CSI 드라이버를 설치해야 합니다. 아래 명령을 통해 설치를 진행합니다.
# EFS 정보 확인
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
# 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
eksctl get iamserviceaccount --cluster myeks
# 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"
아래 명령을 통해 EFS를 사용하는 스토리지 클래스를 생성합니다. efs-sc라는 이름의 스토리지 클래스를 사용하며 parameters.fileSystemId 필드는 CloudFormation에 의해 생성된 EFS 파일 시스템 Id 값이 적용된 것을 확인할 수 있습니다.
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/storageclass.yaml
sed -i "s/fs-92107410/$EfsFsId/g" storageclass.yaml
kubectl apply -f 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-042e374ceb93f0db2
directoryPerms: "700"
gidRangeStart: "1000" # optional
gidRangeEnd: "2000" # optional
basePath: "/dynamic_provisioning" # optional
subPathPattern: "${.PVC.namespace}/${.PVC.name}" # optional
ensureUniqueDirectory: "true" # optional
reuseAccessPoint: "false" # optional
아래 명령을 통해 PVC, Pod를 배포합니다. PVC의 accessModes가 ReadWriteMany로 설정되어 다수의 파드가 접근 가능한 모드이믈 확인할 수 있습니다.
# PVC/파드 생성 및 확인
curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/master/examples/kubernetes/dynamic_provisioning/specs/pod.yaml
kubectl apply -f pod.yaml
cat pod.yaml | yh
---
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
아래 명령을 통해 efs-csi-controller가 동적으로 볼륨을 프로비져닝 한 것을 확인할 수 있습니다.
kubectl logs -n kube-system -l app=efs-csi-controller -c csi-provisioner -f
I0323 09:24:37.703251 1 event.go:298] Event(v1.ObjectReference{Kind:"PersistentVolumeClaim", Namespace:"default", Name:"efs-claim", UID:"0218a67b-c628-4191-bfb9-54b2e6cd0db2", APIVersion:"v1", ResourceVersion:"42325", FieldPath:""}): type: 'Normal' reason: 'Provisioning' External provisioner is provisioning volume for claim "default/efs-claim"
I0323 09:24:38.002438 1 controller.go:923] successfully created PV pvc-0218a67b-c628-4191-bfb9-54b2e6cd0db2 for PVC efs-claim and csi volume name fs-042e374ceb93f0db2::fsap-0e69d876194e0ae12
I0323 09:24:38.002477 1 controller.go:1449] provision "default/efs-claim" class "efs-sc": volume "pvc-0218a67b-c628-4191-bfb9-54b2e6cd0db2" provisioned
I0323 09:24:38.002552 1 controller.go:1462] provision "default/efs-claim" class "efs-sc": succeeded
I0323 09:24:38.012458 1 event.go:298] Event(v1.ObjectReference{Kind:"PersistentVolumeClaim", Namespace:"default", Name:"efs-claim", UID:"0218a67b-c628-4191-bfb9-54b2e6cd0db2", APIVersion:"v1", ResourceVersion:"42325", FieldPath:""}): type: 'Normal' reason: 'ProvisioningSucceeded' Successfully provisioned volume pvc-0218a67b-c628-4191-bfb9-54b2e6cd0db2
EKS NodeGroup
EKS는 크게 컨트롤 플레인, 데이터 플레인 영역으로 구분됩니다. 컨트롤 플레인 부분은 AWS에 의해 완전 관리되며, 데이터 플레인은 사용자가 노드그룹을 직접 관리하거나 Fargate의 관리형 서비스로 생성할 수 있습니다. 또한 복수개의 노드그룹을 배치할 수 있습니다. 이번 실습에서는 EKS 클러스터에 AWS의 arm 기반인 graviton 아키텍처를 사용하는 노드그룹과 Spot 인스턴스를 사용하는 노드그룹을 생성하여 연결 해 봅니다.
아래 명령을 통해 Graviton을 사용하는 노드그룹을 생성합니다.
Graviton의 arm 아키텍처에 구동되는 컨테이너에 사용되는 이미지는 arm 기반의 컨테이너 이미지를 사용해야 합니다.
# 신규 노드 그룹 생성
eksctl create nodegroup --help
eksctl create nodegroup -c $CLUSTER_NAME -r $AWS_DEFAULT_REGION --subnet-ids "$PubSubnet1","$PubSubnet2","$PubSubnet3" --ssh-access \
-n ng3 -t t4g.medium -N 1 -m 1 -M 1 --node-volume-size=30 --node-labels family=graviton --dry-run > myng3.yaml
eksctl create nodegroup -f myng3.yaml
EKS 클러스터의 컴퓨팅 탭에 노드그룹이 추가된 것을 확인합니다.
아래 명령을 통해 워커노드의 아키텍처를 확인합니다. arm 기반 1대가 추가된 것을 확인할 수 있습니다.
kubectl get nodes --label-columns eks.amazonaws.com/nodegroup,kubernetes.io/arch
NAME STATUS ROLES AGE VERSION NODEGROUP ARCH
ip-192-168-1-145.ap-northeast-2.compute.internal Ready <none> 175m v1.28.5-eks-5e0fdde ng1 amd64
ip-192-168-2-250.ap-northeast-2.compute.internal Ready <none> 175m v1.28.5-eks-5e0fdde ng1 amd64
ip-192-168-3-12.ap-northeast-2.compute.internal Ready <none> 175m v1.28.5-eks-5e0fdde ng1 amd64
ip-192-168-3-189.ap-northeast-2.compute.internal Ready <none> 3m23s v1.28.5-eks-5e0fdde ng3 arm64
arm 기반의 워커노드엔 arm 기반 이미지를 사용해 파드를 배포해야 함으로 노드에 Taint 설정을 진행합니다.
aws eks update-nodegroup-config --cluster-name $CLUSTER_NAME --nodegroup-name ng3 --taints "addOrUpdateTaints=[{key=frontend, value=true, effect=NO_EXECUTE}]"
아래 명령을 통해 Taint가 잘 적용되었는지 확인합니다.
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name ng3 | jq .nodegroup.taints
[
{
"key": "frontend",
"value": "true",
"effect": "NO_EXECUTE"
}
]
Taint의 설정에 맞춰 Pod를 배포합니다. Pod는 Taint가 적용된 Graviton 아키텍처의 워커노드에 배포됩니다.
cat << EOT > busybox.yaml
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"
tolerations:
- effect: NoExecute
key: frontend
operator: Exists
EOT
kubectl apply -f busybox.yaml
파드와 노드그룹을 삭제합니다.
kubectl delete pod busybox
eksctl delete nodegroup -c $CLUSTER_NAME -n ng3
스팟 인스턴스 유형의 노드그룹을 생성합니다. 스팟 인스턴스의 유형 및 특징은 링크에서 확인할 수 있습니다.[2]
아래 명령어로 기존 워커노드의 인스턴스 유형을 확인합니다. On Demand 방식인걸 확인할 수 있습니다.
kubectl get nodes -L eks.amazonaws.com/capacityType
NAME STATUS ROLES AGE VERSION CAPACITYTYPE
ip-192-168-1-145.ap-northeast-2.compute.internal Ready <none> 3h9m v1.28.5-eks-5e0fdde ON_DEMAND
ip-192-168-2-250.ap-northeast-2.compute.internal Ready <none> 3h8m v1.28.5-eks-5e0fdde ON_DEMAND
ip-192-168-3-12.ap-northeast-2.compute.internal Ready <none> 3h9m v1.28.5-eks-5e0fdde ON_DEMAND
아래 명령어로 Spot 인스턴스 유형의 노드그룹을 생성합니다. 이 때 node-role 의 값을 본인의 EKS 노드 role 값으로 변경해야 합니다.
# 생성 : 아래 node-role 은 각자 자신의 노드롤 ARN을 입력하자
# role AWSServiceRoleForAmazonEKSNodegroup 테스트해보자
aws eks create-nodegroup \
--cluster-name $CLUSTER_NAME \
--nodegroup-name managed-spot \
--subnets $PubSubnet1 $PubSubnet2 $PubSubnet3 \
--node-role arn:aws:iam::654654143395:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-JxEpk1fAxbJT \
--instance-types c5.large c5d.large c5a.large \
--capacity-type SPOT \
--scaling-config minSize=2,maxSize=3,desiredSize=2 \
--disk-size 20
아래 명령어로 워커노드의 유형을 확인합니다.
kubectl get nodes -L eks.amazonaws.com/capacityType,eks.amazonaws.com/nodegroup
NAME STATUS ROLES AGE VERSION CAPACITYTYPE NODEGROUP
ip-192-168-1-145.ap-northeast-2.compute.internal Ready <none> 3h15m v1.28.5-eks-5e0fdde ON_DEMAND ng1
ip-192-168-1-55.ap-northeast-2.compute.internal Ready <none> 107s v1.28.5-eks-5e0fdde SPOT managed-spot
ip-192-168-2-235.ap-northeast-2.compute.internal Ready <none> 106s v1.28.5-eks-5e0fdde SPOT managed-spot
ip-192-168-2-250.ap-northeast-2.compute.internal Ready <none> 3h15m v1.28.5-eks-5e0fdde ON_DEMAND ng1
ip-192-168-3-12.ap-northeast-2.compute.internal Ready <none> 3h15m v1.28.5-eks-5e0fdde ON_DEMAND ng1
nodeselector를 사용해 파드를 Spot 인스턴스 노드에 배포합니다.
cat << EOT > busybox.yaml
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"
nodeSelector:
eks.amazonaws.com/capacityType: SPOT
EOT
kubectl apply -f busybox.yaml
# 파드가 배포된 노드 정보 확인
kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox 0/1 ContainerCreating 0 2s <none> ip-192-168-1-55.ap-northeast-2.compute.internal <none> <none>
# 삭제
kubectl delete pod busybox
eksctl delete nodegroup -c $CLUSTER_NAME -n managed-spot
이번 실습에 사용한 볼륨, 스냅샷, 도메인 등을 잘 확인하고 삭제합니다. 아래 명령을 통해 EKS 클러스터와 CloudFormation 스택을 삭제합니다.
eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME
참고
[1] : https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/
[2] : https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/using-spot-instances.html
'클라우드' 카테고리의 다른 글
[EKS] CI/CD (0) | 2024.04.16 |
---|---|
[EKS] Autoscaling (0) | 2024.04.03 |
[EKS] Networking (0) | 2024.03.14 |
[EKS] Amzaon EKS 설치 및 기본 사용 (0) | 2024.03.09 |
[k8s] Pod의 안정적인 유지 - liveness probe (0) | 2023.10.22 |