⭐ 가시다(gasida) 님이 진행하는 AWS EKS Workshop 실습 스터디 게시글입니다.
이번 게시글은 Storage 에 관한 내용을 진행합니다.
EKS 에서의 Storage 를 이해하기 전에, Pod 에서 Storage 를 관심 가져야 하는 이유부터 알아보려합니다.
0. Pod 의 Storage 관리
Pod 의 가장 기본적인 특성은 ‘Pod 가 종료되면, Pod 내부에 갖고있는 데이터와 상태가 저장되지 않는다’ 는 점 입니다. 이는 Pod 의 가장 기본적인 개념입니다.
하나의 Pod는 1개 이상의 Container 로 구성되어있고, 각각의 Container 는 Container 내부에 저장공간을 갖고있습니다. 따라서 Pod 는 해당 저장공간을 이용하며, 데이터 상태의 저장 및 변경의 목적으로 사용합니다.
그러나 Pod 내부의 데이터는 Pod가 정지되면 모두 삭제가 됩니다.
이는 무슨 문제가 야기되냐면, Pod에서 실컷 데이터의 상태를 변경했으나, Pod내부에서만 저장이 되었을뿐, 전체 시트템에서는 저장,변경내역이 기록되지않아서, 사용자는 데이터를 변경했지만, 시스템에선 데이터의 상태가 변하지 않는 문제를 야기합니다.
이 상태를 이쁘게 표현하면 Pod 는 State(상태) 를 갖고있지 않다. Pod는 데이터를 저장하고 있지 않다. 휘발성이다.
즉, Pod는 Stateless 하다. 라고 표현합니다.
위에서 했던 문제를 해결하기 위해 PV, PVC 를 도입했습니다.
Pod 에서 데이터를 보존해준다면, 위의 문제를 해결할 수 있기 때문입니다.
PV, PVC는 Pod의 Container 에 마운트됩니다. 따라서 Pod 내부의 변경내용은 PV,PVC를 통해 기록됩니다.
즉, PV, PVC 가 Pod에 마운트된다면, Pod 가 종료되어도 데이터는 Pod외부에서 저장되어있다는 것을 의미합니다.
이 상태를 이쁘게 표현하면 Pod는 State(상태) 를 갖고있다. Pod는 데이터를 외부에 저장하고 있다. 비휘발성이다.
즉, Pod가 Stateful하다. 라고 표현합니다.
PV, PVC는 스토리지 부분에서 일반적으로 사용하는 오브젝트입니다.
그렇다면, 클라우드 공급자(EKS, GKE …)에만 존재하는 스토리지 특화 오브젝트를 사용할 수 있으면 어떨까요?
이 질문에 대한 답으로 EBS, EFS 를 알아보려합니다.
1. AWS EBS Controller
AWS EBS(Elastic Block Store) 는 AWS 에서 제공하는 블록스토리지 서비스입니다.
EBS의 가장 큰 장점은 Storage 가 Instance 에 존재하지 않는다는 점입니다.
EBS는 Kubernetes 컴포넌트가 위치하는 Instance 에 존재하지 않습니다.
따라서 마운트 하는 볼륨의 양을 Instance Spec 에 구애받지 않고 설정가능하며, Pod 가 마운트 하여 사용하면 끝입니다.
다른 특징으로는 EBS는 동일한 Single AZ(Availability Zone, AWS에만 있는 개념) 로만 대응된다는 점이 있습니다.
EBS볼륨은 한번에 하나의 EC2 Instance에만 마운트 될 수 있습니다. EBS의 기본 설계 상, 여러 인스턴스에서 동시에 EBS볼륨에 접근하여 데이터를 읽고 쓰는것이 불가능합니다.
이를 통해 EBS는 데이터의 일관성을 유지할 수 있게 됩니다.
EBS 의 PV, PVC에서 accessMode 는 ReadWriteOnce(RWO) 모드로 설정하는것도 이 때문입니다.
배포
# 아래는 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
배포확인
2024-03-24 04:41:28 [ℹ] waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
(jjongiam@myeks:default) [root@myeks-bastion ~]# eksctl get iamserviceaccount --cluster myeks
NAMESPACE NAME ROLE ARN
kube-system aws-load-balancer-controller arn:aws:iam::909418839780:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-nLlxAY6Vw8pD
kube-system ebs-csi-controller-sa arn:aws:iam::909418839780:role/AmazonEKS_EBS_CSI_DriverRole
ADD-on 추가
# 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**
# csinodes 확인
kubectl get csinodes
배포확인
(jjongiam@myeks:default) [root@myeks-bastion ~]# 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
(jjongiam@myeks:default) [root@myeks-bastion ~]#
(jjongiam@myeks:default) [root@myeks-bastion ~]# kubectl get csinodes
NAME DRIVERS AGE
ip-192-168-1-195.ap-northeast-2.compute.internal 1 131m
ip-192-168-2-211.ap-northeast-2.compute.internal 1 131m
ip-192-168-3-206.ap-northeast-2.compute.internal 1 131m
(jjongiam@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-mlhzg 6/6 Running 0 5m17s
ebs-csi-controller-765cf7cf9-sg5fc 6/6 Running 0 5m17s
ebs-csi-node-5plhb 3/3 Running 0 5m17s
ebs-csi-node-6xpqb 3/3 Running 0 5m17s
ebs-csi-node-lswwr 3/3 Running 0 5m17s
EBS를 사용하는 스토리지 클래스를 생성
kubectl get sc
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
- allowVolumeExpansion: 온라인으로 볼륨 확장가능한 옵션
- provisioner: 클라우드 공급자
- type: gp3
실행, 확인
**kubectl apply -f gp3-sc.yaml**
kubectl get sc
kubectl describe sc gp3 | grep Parameters
PVC/PV 정보확인
# 워커노드의 EBS 볼륨 확인 : tag(키/값) 필터링 - [링크](https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/cli-usage-filter.html)
**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
PVC/PV 배포
# 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**
새로운 창 하나 띄워서 모니터링하기
# 워커노드에서 파드에 추가한 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 생성 완료)
Pod배포
# 파드 생성
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**
현재 상태 확인
- 현재 상태가 바뀌며 gp3가 pod에 마운트 된 모습
- 여기서 중요한건 Pod 가 직접 gp3에 마운트 되서 데이터를 사용하고 있다는 것
kubectl get pvc,pv,pod
kubectl get VolumeAttachment
볼륨 창에도 새로운 PVC(myeks-dynamic-pvc) 가 생성되었음
NodeAffinity 내용 확인
**kubectl get pv -o yaml | yh**
...(중략)
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.ebs.csi.aws.com/zone
operator: In
values:
- ap-northeast-2c
nodeArrinity 의 역할: 특정 value 값에 해당하는 노드에만 적용될 수 있도록 만든 설정.
예시에서는 ap-northeast-2c 의 노드에만 적용할 수 있도록 설정되어 있다.
- ap-northeast-2a, ap-northeast-2b 에서 생긴 노드에 대해서는 적용할 수 없다는 것이다.
볼륨증가
kubectl get **pvc** ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
**kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'**
- 4gb → 10gb
볼륨증가확인
(jjongiam@myeks:default) [root@myeks-bastion ~]# kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
4Gi
(jjongiam@myeks:default) [root@myeks-bastion ~]# kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
persistentvolumeclaim/ebs-claim patched
(jjongiam@myeks:default) [root@myeks-bastion ~]# kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme1n1 xfs 4.0G 61M 3.9G 2% /data
/dev/nvme0n1p1 xfs 30G 3.8G 27G 13% /etc/hosts
(jjongiam@myeks:default) [root@myeks-bastion ~]# kubectl df-pv
PV NAME PVC NAME NAMESPACE NODE NAME POD NAME VOLUME MOUNT NAME SIZE USED AVAILABLE %USED IUSED IFREE %IUSED
pvc-30c39939-9422-4f74-a54e-5a26af2027fb ebs-claim default ip-192-168-3-206.ap-northeast-2.compute.internal app persistent-storage 3Gi 60Mi 3Gi 1.50 4 2097148 0.00
(jjongiam@myeks:default) [root@myeks-bastion ~]#
(jjongiam@myeks:default) [root@myeks-bastion ~]# kubectl exec -it app -- sh -c 'df -hT --type=xfs'
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme1n1 xfs 10G 105M 9.9G 2% /data
/dev/nvme0n1p1 xfs 30G 3.8G 27G 13% /etc/hosts
실습 내용 제거
**kubectl delete pod app & kubectl delete pvc ebs-claim**
2. AWS EFS Controller
EFS는 NFS(Network File System) 을 기반으로 하는 AWS 의 서비스이다.
1개의 AZ에만 사용할 수 있는 EBS에 비해, EFS는 여러개의 AZ에 걸쳐 데이터를 저장,사용이 가능하다.
구성 아키텍쳐
구성아키텍쳐를 보면 EBS와 공통점을 알 수 있는데
그것은 EKS 의 범위와 EFS의 범위가 분리되어있다는 것이다.
배포
# 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"
모니터링
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
PV/PVC배포
**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
kubectl get pods
**kubectl exec -ti app1 -- sh -c "df -hT -t nfs4"**
**kubectl exec -ti app2 -- sh -c "df -hT -t nfs4"**
공용저장소 저장 동작 확인
# 공유 저장소 저장 동작 확인
**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**
실습 내용 제거
# 쿠버네티스 리소스 삭제
kubectl delete pod app1 app2
kubectl delete pvc efs-claim && kubectl delete pv efs-pv && kubectl delete sc efs-sc
'외부활동' 카테고리의 다른 글
[AEWS] 2기 스터디: Autoscaling 의 다양한 방법 (0) | 2024.04.07 |
---|---|
[AEWS] 2기 스터디: Observability (1) | 2024.03.31 |
[AEWS] 2기 스터디: AWS VPC + ALB (3) | 2024.03.17 |
[AEWS] 2기 스터디: 스터디 기본지식 + EKS내용정리 (0) | 2024.03.10 |
코딩테스트 준비 플랫폼 추천: 코드트리 (0) | 2024.03.03 |