2024. 3. 23. 16:28ㆍEKS@CloudNet
- CloudNet에서 주관하는 EKS 스터디 2기 내용입니다
- 매번 좋은 스터디를 진행해 주시는 CloudNet 팀 감사드립니다
0. Setup
- 기존과 비슷하게 가시다님이 배포해주신 원클릭 배포 파일을 사용하지만, IAM의 serviceAccount와 maxPodPerNode (node당 최대 pod 갯수) 내용이 추가되어 있다.
- CloudFormation에 yaml을 어떻게 적용할지 모른다면 우선 AWS 콘솔의 CloudFormation 페이지에 들어가서, "스택 생성" 을 누르면 yaml을 업로드하고 기존과 같이 스택을 생성할 수 있다.
- EFS filesystem 확인
가시다님이 미리 $EfsFsId 로 EFS 콘솔의 ID를 지정해놓으셨다. 따라서 아래의 명령어를 이용해서 원하는 디렉토리에 마운트하고, 내용을 확인할 수 있다.
echo $EfsFsId
mount -t efs -o tls $EfsFsId:/ /mnt/myefs
df -hT --type nfs4
echo "efs file test" > /mnt/myefs/memo.txt
cat /mnt/myefs/memo.txt
실행 후, /mnt/myefs 디렉토리에 마운트된 것을 확인한다.
- 스토리지 클래스/ CSI 노드 확인
스토리지 클래스: Dynamic Provisioning 을 실행할 때 필요한 디스크 타입을 지정할 수 있다. 사용할 수 있는 다양한 디스크 타입은 이 블로그 에서 자세히 확인할 수 있다.
이 중에 emptyDir와 hostPath가 헷갈렸는데,
emptyDir: Pod의 볼륨으로 pod와 수명을 같이하므로 pod이 내려갈 때 휘발된다!
hostPath: 노드의 로컬 볼륨으로, pod가 내려갈 때 휘발되지는 않지만 호스트 노드의 파일 시스템 경로를 사용하여 컨테이너 내 디렉토리에 마운트하는 방식으로 pod과 다른 노드의 파일시스템에는 접근할 수 없다
Dynamic Provisioning: pod에 스토리지를 연동하려면 매 pod마다 물리적인 볼륨, PV, PVC 를 생성해야 하는데, 이 과정을 k8s가 알아서 해 주는 방식이다.
CSI(Container Storage Interface) 노드:
별도의 controller pod을 통해서 dynamic provisioning을 사용할 수 있게 한다.
아래의 코드를 이용하면 우리가 스토리지 클래스로 EBS를 사용하고 있고, 사용하고 있는 CSI 노드의 정보도 확인할 수 있다.
kubectl get sc
kubectl get sc gp2 -o yaml | yh
kubectl get csinodes
- 지난번과 동일하게 노드 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
- 이후 실습을 위해 지난 시간에 다룬 AWS LoadBalancer도 helm으로 설치해 준다
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
1. 스토리지: Pod, PVC, PV
- Pod은 기본적으로 상태가 없는(stateless) 하므로, 데이터를 보관하기에 부적합하다. 상술했듯 Pod가 날아가면 내부의 데이터는 모두 날아가기 때문에 보존이 필요한 데이터는 외부 스토리지를 이용해야 한다.
- 이때 사용하는 외부 스토리지가 PV(Persistent Volume) 이다. PV는 stateful하고 Pod와 별개이므로 pod가 내려가도 정보를 저장할 수 있다. 노드의 hostPath가 노드 단위에서의 자원 관리였다면, PV는 클러스터 단위의 자원으로 스토리지 그 자체이다. node1의 Pod A가 node2의 PV B에 접근할 수 있다 (hostPath는 pod과 hostPath가 다른 노드에 있다면 접근이 불가능하다!)
# skeleton yaml for PV from k8s.docs
apiVersion: v1
kind: PersistentVolume
metadata:
name: foo-pv
spec:
storageClassName: ""
claimRef:
name: foo-pvc
namespace: foo
- PV에 사용자가 하는 요청이 PVC(Persistent Volume Claim)이다. PVC에 따라 쿠버네티스는 적정한 PV를 찾아 PVC에 할당해주고, 최종적으로 Pod에 붙인다. 이때 PV와 PVC는 일대일 대응관계이다.
# skeleton yaml form PVC from k8s.docs
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
- hostPath vs PV-PVC에 대해 직관적으로 설명되어 있는 그림이다.
실습: 기본 컨테이너 환경의 임시 파일시스템 사용
- busybox 파드를 하나 만들어서 10초 간격으로 현재 시간을 내부 스토리지에 저장하도록 한다. 그리고 터미널을 하나 더 띄워서 저장된 컨텐츠를 모니터링한다. pod의 yaml과 command는 아래와 같다.
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"
# 터미널 1 : pod 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/date-busybox-pod.yaml
cat date-busybox-pod.yaml | yh
kubectl apply -f date-busybox-pod.yaml
kubectl get pod
# 터미널 2: 모니터링
kubectl exec busybox -- tail -f /home/pod-out.txt
10초마다 한번씩 시간이 잘 기록되었다.
- 그러면 pod을 삭제했다 다시 생성해도 이전 기록이 남아 있을까? 삭제했다가 재생성한 뒤 해당 txt file을 조회해보면 컨텐츠가 사라진 것을 확인할 수 있다.
# 터미널 1: pod 삭제 후 재생성
kubectl delete pod busybox
kubectl apply -f date-busybox-pod.yaml
# 터미널 2: 확인
kubectl exec busybox -- tail -f /home/pod-out.txt
# 실습 후 삭제
kubectl delete pod busybox
실습: PV/PVC 사용
- 이번에는 pod안의 데이터를 보존하기 위해 local-path-provisioner를 사용하는 PV/PVC를 함께 배포해 차이를 보자. Dynamic Provisioning을 사용하기 위해 StorageClass에서 제공하는 Local Path Provisioner를 사용하는데, 둘의 차이와 장단점을 정리해 보았다.
hostPath | Local Path Provider |
- 간단하고 직관적이다 - host node에 직접 접근하므로 빠르다 - 다른 node의 hostpath에는 접근할 수 없다 |
- dynamic provisioning이 가능하여 가용성과 안정성이 높다 - 다른 node의 데이터에 접근할 수 있다 - 네트워크 스토리지에 비해 속도가 느릴 수 있다 |
실습을 위해 local-path-provisioner 스토리지 클래스를 배포하고, pv, pvc, 그리고 pvc를 사용하는 pod 까지 생성한다.
# StorageClass local-path-storage 배포
curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
kubectl apply -f local-path-storage.yaml
# PVC 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath1.yaml
cat localpath1.yaml | yh
kubectl apply -f localpath1.yaml
# Pod 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath2.yaml
cat localpath2.yaml | yh
kubectl apply -f localpath2.yaml
5초마다 현재시간이 찍히는 것을 확인할 수 있고, 현재 호스트에 붙도록 node Affinity가 설정되어 있다.
그러면 이제 pod을 내렸다 다시 올리고 데이터가 남아있는지 확인해보자.
# pod tkrwp
kubectl delete pod app
# 잠시 기다리기 #
# pod 재생성
kubectl apply -f localpath2.yaml
# 터미널 2: pv 확인
kubectl exec -it app -- head /data/out.txt
pod가 내려갔을 때 시간 기록이 잠시 끊겼지만 데이터는 잘 남아있는 것을 확인할 수 있다.
2. AWS EBS controller
EBS(Elastic Block Store) 는 AWS의 영구적인 블록 스토리지 서비스이다. S3이 객체 스토리지인 것과 가장 큰 차이이다.
블록 스토리지는 DB, 파일시스템, OS등 구조적 데이터 저장에 용이하고, 객체 스토리지는 파일, 이미지, 오디오 등 비 구조적 데이터 저장에 주로 사용된다. 아래 표는 S3과 EBS의 차이를 간략하게 정리한 것이다.
S3 | EBS | |
데이터 형식 | 객체 스토리지 | 블록 스토리지 |
용도 | 비구조 데이터-파일, 이미지, 오디오 | 구조 데이터-DB, 파일 시스템, OS |
액세스 방식 | 키로 식별, http 액세스 | 블록 단위 액세스, 파일 시스템 CRUD |
아래 그림은 AWS EBS와 pod가 연결되는 방식에 대한 도식화이다.
- EBS에 액세스하는 pod와 EBS는 같은 AZ에 있어야 한다.
- 인스턴스와 연결되는 PV, PVC는 이때 EBS를 이어주는 중간다리 역할을 하므로 accessMode를 ReadWriteOnce로 설정해야 한다.
실습: EBS 생성
- 아래 코드를 이용하면 1.28 버전 EKS에 맞는 EBS 드라이버를 설치하고, 확인할 수 있다.
aws eks describe-addon-versions \
--addon-name aws-ebs-csi-driver \
--kubernetes-version 1.28 \
--query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
--output text
- 실제 1.28버전만 설치되어 있음을 확인할 수 있다.
- 해당 클러스터에서 EBS를 관리하는 권한을 가진 Service Account를 생성해준다.
# 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
- 또한 EBS-CSI-Controller pod에 EBS/CSI 관련 컨테이너 6개가 들어와 있는 것을 볼 수 있다
(ebs-plubin, csi-orovisioner, cst-attacher, csi-snapshotter, csi-resizer, liveness-probe)
gp3 type의 스토리지를 생성해준다. gp3은 볼륨 크기와 무관하게 볼륨 전체의 iops와, throughput을 지정할 수 있다. 또한 가격적인 면에서도 기존 gp2보다 매력적이다. 기존 gp2는 GB당 3iops로 일정하게 확장되어 용량이 크지 않지만 ips가 많이 필요한 작업에는 적합하지 않았다. 하이퍼커넥트 기술블로그에 gp3 스토리지에 대해 더 자세히 설명되어 있다.
이제 pvc와 대응하는 pod을 생성해서 사용할 수 있다.
# 워커노드에서 파드에 추가한 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
gp3 스토리지가 각각 pod에 대응하여 attach되었음을 확인할 수 있다.
3. AWS EFS Controller
EFS(Elastic File System) 는 AWS의 클라우드 기반 스토리지 서비스이다. EBS가 리전기반인것과 달리 EFS는 클라우드 기반이기 때문에 여러 AZ의 pod에서 접근 및 수정이 가능하다. 아래 그림을 보면 알 수 있듯 각 pod에 연결된 pvc가 EFS Provisioner로 관리되므로 EFS 볼륨에 여러 pod가 액세스할 수 있다.
- S3, EBS, EFS의 차이에 대해서는 SmileShark 블로그 에 더 잘 정리되어 있다.
실습: EFS 생성 및 사용
- EFS 관리를 위한 IAM 을 생성해준다.
- EBS와 유사하게 IRSA를 설정해주고 확인한다.
-helm으로 EFS 컨트롤러를 설치한다.
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
- aws 콘솔에서 EFS 메뉴를 찾아 들어가면 GUI로 EFS 관련 정보를 확인할 수 있다.
- 해당 콘솔에 들어오면 EFS가 탑재되는 대상 IP를 알 수 있다.
- 생성한 EFS에 여러 pod가 접근하도록 설정할 수 있다.
PV를 생성하는데, 이때 PV는 스토리지를 새로 만드는 것이 아니라 volumeHandle을 자신의 EFS 파일시스템 ID로 변경해줌으로서 PVC-EFS 사이를 이어주는 중간다리만 하는 것으로 이해했다.
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
이제 EFS 에 여러 pod가 접근할 수 있다.
잊지 말아요 자원 삭제
eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME
우리의 지갑은 소중하니까요
CKA 준비와 실습 스터디를 병행하니 실무적인 접근과 fundamental한 접근을 동시에 학습할 수 있어서 시야가 넓어지는 것 같다.
읽어주셔서 감사합니다!
'EKS@CloudNet' 카테고리의 다른 글
[EKS@CloudNet] EKS Security (0) | 2024.04.14 |
---|---|
[EKS@CloudNet] EKS Autoscaling: Karpenter 외 (0) | 2024.04.07 |
[EKS@CloudNet] EKS Observability: Prometheus & Grafana (0) | 2024.03.31 |
[EKS@CloudNet] EKS Networking (3) | 2024.03.17 |
[EKS@CloudNet] Amazon EKS 설치 및 기본 사용 (2) | 2024.03.10 |