가시다님께서 진행하신 AEWS라는 스터디를 진행하면서 작성하는 글입니다.
AEWS= AWS EKS Workshop Study
AEWS 스터디는 4월 23일 ~ 6월 4일동안 총 7번 진행될 예정입니다.
PKOS 스터디에서 정말 좋은 경험을 했어서 다시 이렇게 참가하게 되었습니다.
가시다님께서 진행하시는 스터디에 관심있으신 분들은 Cloudnet@Blog에 들어가시면 자세한 정보를 확인하실 수 있습니다.
1,2주차와는 다르게 EFS 생성이 될 수 있게 가시다님께서 Cloudformation템플릿을 변경해주셨습니다.
Cloudformation 배포 완료 후 EFS가 생성이 됐는지 AWS콘솔에서 보겠습니다.
잘 생성이 되었습니다.
이제 EFS를 마운트하고 마운트 되었느지 확인해보겠습니다.
# EFS 마운
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-031920bded8982b4e.efs.ap-northeast-2.amazonaws.com:/ /mnt/myefs
**df -hT --type nfs4**
mount | grep nfs4
# 연결 확인
**echo "efs file test" > /mnt/myefs/memo.txt**
cat /mnt/myefs/memo.txt
**rm -f /mnt/myefs/memo.txt**
그리고 AWS LB/ExternalDNS, kube-ops-view 설치해보겠습니다.
# 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=hanryang.link
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnzHostedZoneId
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -
# kube-ops-view
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
kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
kubectl **annotate** service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=**kubeopsview**.$MyDomain"
echo -e "Kube Ops View URL = http://**kubeopsview**.$MyDomain:8080/#scale=1.5"
kube-ops-view는 클러스터의 상태를 시각적으로 볼 수 있는 간단한 페이지입니다. 모니터링 및 운영 관리의 목적으로 사용되진 않으나 Cluster Autoscaler와 같이 클러스터 오토스케일링 작업 시, 스케일 인/아웃의 과정을 시각적으로 관찰할 수 있습니다.
상태 저장 애플리케이션을 실행할 때, 영구 스토리지가 없을 경우, 데이터는 pod 또는 Container의 수명 주기에 연결됩니다. 그리고 Pod가 충돌하거나 종료되면 데이터는 손실됩니다.
그래서 k8s는 데이터 손실을 막기 위해서 3가지 간단한 스토리지 요구사항을 준수해야합니다.
Persistent Volumes은 위에서 설명한 스토리지 요구사항 세 가지를 충족합니다.
k8s는 다양한 유형의 pv를 지원합니다.
PV는 3가지 접근 모드를 지원합니다.
Kubernetes는 PV를 포드에 연결하는데 필요한 추가 추상화 계층인 PersistentVolumeClaim(PVC)을 가지고 있습니다.
PV 는 실제 스토리지 볼륨을 나타내며, PVC는 Pod가 실제 스토리지를 얻기 위해 수행하는 스토리지 요청
CSI는 Kubernetes에서 다양한 스토리지 솔루션을 쉽게 사용할 수 있도록 설계된 추상화입니다.
AWS는 Amazon EBS , Amazon EFS 및 Amazon FSx for Lustre 용 CSI 플러그인을 제공하고 있습니다.
일반적인 CSI driver 구조는 아래와 같습니다.
AWS는 EC2 Type에 따라 노드에 연결할 수 있는 볼륨 수에 제한이 있습니다.
제가 현재 설치한 EC2 Type에는 25개 볼륨만 연결할 수 있는 것을 확인할 수 있습니다.
먼저 관리자가 하나 이상의 PV를 생성하고 애플리케이션 개발자는 PVC를 생성합니다. 이를 정적 프로비저닝이라고 합니다. Kubernetes에서 PV 및 PVC를 수동으로 만들어야 하므로 정적입니다.
이 정적 프로비저닝은 대규모 환경에서는 관리하기가 어렵습니다.
동적 프로비저닝을 사용하면 PV객체를 생성할 필요가 없습니다. 대신에, PVC를 생성할 때 내부적으로 자동으로 생성됩니다.
Kubernetes는 Storage Class라는 다른 객체를 사용하여 이를 수행합니다.
AWS에서는 아래와 같이 사용하실 수 있습니다.
우선 기본 컨테이너 환경의 임시 파일 시스템을 사용하여 데이터가 어떻게 되는지에 대해서 확인해 보겠습니다.
date-busybox-pod.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"
배포를 한 후에 파일을 확인해 보겠습니다.
해당 기록이 pod를 재생성했을 때에도 남아있는지 확인해보겠습니다.
**kubectl delete pod busybox**
kubectl apply -f date-busybox-pod.yaml
**kubectl exec busybox -- tail -f /home/pod-out.txt**
임시 파일 시스템을 사용하면 pod가 삭제될 때 같이 데이터도 삭제된다는 것을 확인하실 수 있습니다.
# 배포
**curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml**
~~~~**kubectl apply -f local-path-storage.yaml**
# 확인
kubectl **get-all** -n local-path-storage
PVC 생성
# pvc yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: localpath-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: "local-path"
pvc만 생성하고 아직 pod를 pvc와 연결하지 않았기에 pending 상태인 것을 describe로 확인할 수 있습니다.
pod 생성
# 배포할 pod
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
pod 를 생성한 후 pvc가 정상으로 bound 된 것을 확인 할 수 있습니다.
pod가 데이터를 잘 기록하고 있는지 확인해보겠습니다.
pod는 node 3에 배포가 되어있으므로 node3에 out.txt라는 파일이 존재하는지도 확인해보겠습니다.
pod를 삭제 후 재생성을 해도 데이터가 유지되는 지 확인해보겠습니다.
이렇게 PV/PVC를 사용하면 pod를 삭제해도 데이터가 유지된다는 것을 확인할 수 있었습니다.
AWS EBS Controller드라이버에서는Amazon EKS 클러스터가 영구 볼륨을 위해 Amazon EBS 볼륨의 수명 주기를 관리할 수 있게 합니다.
⇒ CSI-Controller와 CSI-Node는 pod로 실행됩니다.
EBS CSI드라이버가 AWS API를 사용해야 하므로 CSI-controller pod ISRA 설정,
EBS CSI driver addon 추가
```yaml
# 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**
# 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
```
ebs-csi-controller 파드에 6개 컨테이너가 생성되었는지 확인
CSI-Controller와 CSI-Node pod가 생성 확인
gp3 스토리지 클래스 생성
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'
pvc 생성
# awsebs-pvc**.yaml**
apiVersion: v1
kind: **PersistentVolumeClaim**
metadata:
name: **ebs-claim**
spec:
accessModes:
- **ReadWriteOnce**
resources:
requests:
**storage: 4Gi**
**storageClassName: gp3**
pvc와 연결할 파드 생성
# 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**
데이터 저장 확인
볼륨 증가
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
VolumeSnapshot은 스토리지 시스템에 있는 볼륨의 스냅샷을 나타냅니다.
Volumesnapshots 컨트롤러 설치를 해보겠습니다.
# 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 생성
**kubectl apply -f** awsebs-pvc**.yaml**
# 파드 생성
**kubectl apply -f** awsebs-pod**.yaml**
# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt
# ebs-volume-snapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: ebs-volume-snapshot
spec:
volumeSnapshotClassName: csi-aws-vsc
source:
persistentVolumeClaimName: ebs-claim
# VolumeSnapshot 생성
**kubectl apply -f ebs-volume-snapshot.yaml**
스냅샷 기능을 테스트 하기 위해서 pod와 pvc를 제거하겠습니다.
# app & pvc 제거 : 강제로 장애 재현
**kubectl delete pod app && kubectl delete pvc ebs-claim**
pvc를 스냅샷으로 복원해보겠습니다.
# 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
# pvc 생성
**kubectl apply -f ebs-snapshot-restored-claim.yaml**
pvc와 연결하기 위한 pod또한 생성하겠습니다.
# **ebs-snapshot-restored-pod.yaml**
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
파일 내용을 확인해보겠습니다.
AWS EFS Controller는 AWS에서 실행되는 Kubernetes 클러스터가 Amazon EFS 파일 시스템의 수명 주기를 관리할 수 있게 해주는 CSI 인터페이스를 제공합니다.
AWS EFS Controller를 설치해보겠습니다.
Amazon EFS CSI 드라이버를 Amazon EKS 클러스터에 배포하려면 사용자를 대신하여 CSI 드라이버 서비스 계정에서 AWS API를 호출할 수 있도록 하는 IAM 정책을 만들어야 합니다. 그리고 IRSA를 설정합니다.
# 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
# iam 정책
{
"Policy": {
"PolicyName": "AmazonEKS_EFS_CSI_Driver_Policy",
"PolicyId": "ANPA3BIZM37S3MLUQPB4K",
"Arn": "arn:aws:iam::758651871205:policy/AmazonEKS_EFS_CSI_Driver_Policy",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"CreateDate": "2023-05-13T14:12:45+00:00",
"UpdateDate": "2023-05-13T14:12:45+00:00"
}
# ISRA 설정
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
모두 설정 완료한 후에 EFS Controller 설치를 진행합니다.
# 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
설치가 잘 되었는지 확인을 해보겠습니다.
EFS 파일시스템을 다수의 파드가 사용하게 설정
# 실습 코드 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
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
**kubectl apply -f claim.yaml**
**kubectl get pvc**
cat pod1.yaml pod2.yaml | yh
apiVersion: v1
kind: Pod
metadata:
name: app1
spec:
containers:
- name: app1
image: busybox
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
apiVersion: v1
kind: Pod
metadata:
name: app2
spec:
containers:
- name: app2
image: busybox
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out2.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
**kubectl apply -f pod1.yaml,pod2.yaml**
정말 여러번 반복하고 보면서 이해가 조금씩 되긴 했지만 아직도 쿠버네티스는 여전히 어렵다..
다시 복습하면서 도전과제들을 정리해봐야겠다..