이번 3주차 스터디에서는 EKS 에서 노드의 데이터를 영구적으로 저장하기 위한 AWS 에서 제공하는 다양한 스토리지 종류 및 특징과 워커 노드를 관리하기 위한 노드 그룹을 학습했다.
쿠버네티스의 가장 작은 실행 단위인 파드(Pod)는 하나 이상의 컨테이너로 구성된다.
파드의 데이터 저장을 살펴보기 전에 컨테이너 이미지 생성 원리부터 먼저 살펴보자.
컨테이너 내부에서 특정 OS 를 실행하기 위한 시스템 바이너리 파일, 의존성 라이브러리 등이 필요하고, 이를 저장하기 위해서 파일 시스템이 필요하다.
컨테이너는 호스트 OS 와 격리된 네임스페이스를 가지고 있기 때문에 컨테이너 내부에서 아무리 상위 디렉토리로 이동하려고 해도 호스트 OS 의 디렉토리로 접근할 수 없다.
마치 컨테이너를 위한 USB 를 장착한 것과 같다고 생각하면 된다.
출처: [컨테이너 인터널 #2] 컨테이너 파일시스템 [카카오 엔터프라이즈]
이때 컨테이너의 파일 시스템을 구현하기 위해 오버레이 파일 시스템을 이용하며, 위의 이미지와 같은 구조로 되어 있다.
Lower Dir 은 읽기 전용으로 되어 있고, 운영체제를 실행하기 위한 최소한의 시스템 바이너리 파일, 의존성 라이브러리 등이 저장된다.
그 다음 Upper Dir 을 올리는데, 컨테이너에서 쓰기 작업이 발생하면 Upper Dir 에서 저장된다.
그리고 맨 위의 Merge View 는 오버레이 파일 시스템이 마운트 되는 디렉토리를 의미하는데, Lower Dir 부터 Upper Dir 에 저장된 파일들이 모두 합쳐져서 보이기 때문에 Merge View 라고 표현한다.
쿠버네티스 파드의 컨테이너든 도커 컨테이너든 기본적으로 Upper Dir 에 데이터를 쓰고 지울 수 있는데, 문제는 컨테이너를 종료시키면 Upper Dir 에 저장한 데이터도 함께 사라진다는 것이다.
컨테이너의 파일 시스템에 데이터를 저장하는 대신 도커는 컨테이너의 데이터를 저장하기 위해 크게 3가지 방법을 사용한다.
출처: Manage data in Docker [docker docs]
/var/lib/docker/volumes
경로에 컨테이너의 데이터가 저장된다는 것을 의미한다. 컨테이너와 Docker area
로 이어지는 선에 해당하는 것이 volume
의 개념이다. 쿠버네티스에서는 다양한 컨테이너 런타임(containerd, CRI-O, 도커 엔진 등)을 지원하고 있다.
쿠버네티스 파드 안에 실행되는 컨테이너도 기본적으로 컨테이너 이미지 안에 포함된 파일 시스템 레이어의 데이터를 읽고 쓸 수 있다.
하지만 이 방법으로는 데이터를 영속적으로 관리할 수 없기 때문에 쿠버네티스에서는 아래와 같은 방법을 사용하고 있다.
hostPath 볼륨은 쿠버네티스 노드 파일 시스템에 영속적으로 데이터를 관리하기 위해 사용한다.
hostPath 볼륨의 데이터는 파드가 종료되더라도 삭제되지 않는다.
도커 컨테이너의 바인드 마운트와 유사하다고 보면 된다.
hostPath 를 사용하는 이유는 파드에서 노드의 로그 파일이나 kubeconfig(쿠버네티스 구성 파일), CA 인증서에 접근하기 위해서다.
아래의 명령어를 실행해서 kube-apiserver 가 사용하는 볼륨을 살펴보자.
kubectl describe po -n kube-system kube-apiserver
그 중에서 Volumes
에 해당하는 부분을 살펴보면 아래와 같다.
Volumes:
ca-certs:
Type: HostPath (bare host directory volume)
Path: /etc/ssl/certs
HostPathType: DirectoryOrCreate
etc-ca-certificates:
Type: HostPath (bare host directory volume)
Path: /etc/ca-certificates
HostPathType: DirectoryOrCreate
etc-pki:
Type: HostPath (bare host directory volume)
Path: /etc/pki
HostPathType: DirectoryOrCreate
k8s-certs:
Type: HostPath (bare host directory volume)
Path: /etc/kubernetes/pki
HostPathType: DirectoryOrCreate
usr-local-share-ca-certificates:
Type: HostPath (bare host directory volume)
Path: /usr/local/share/ca-certificates
HostPathType: DirectoryOrCreate
...
하지만 쿠버네티스 공식 문서에서는 hostPath 볼륨을 애플리케이션에서 발생하는 데이터의 영속적 관리를 위해서는 사용하지 않을 것을 권장하고 있다.
Warning:
Using the
hostPath
volume type presents many security risks. If you can avoid using ahostPath
volume, you should. For example, define a[local
PersistentVolume](https://kubernetes.io/docs/concepts/storage/volumes/#local), and use that instead.
hostPath 를 사용하더라도 읽기 전용으로 사용하라고 권장하는데, hostPath 는 마운트 네임스페이스를 격리하는 것이 아닌 노드의 파일 시스템에 직접 액세스 하는 것이기 때문에 보안 상 PV(Persistent Volume)를 사용할 것을 제시하고 있다.
PV(Persistent Volume)는 hostPath 와 달리 마운트 네임스페이스를 격리해서 데이터를 저장한다.
PV 는 노드의 파일 시스템이나 네트워크 파일 시스템으로 마운트 하는 것이 가능하다. 클라우드 서비스의 AWS 의 EBS, EFS 등에 연결해서 사용할 수도 있다.
파드에서 PV 에 접근하는 방식은 아래의 이미지와 같다.
출처: [kubernetes] Volume vs Persistent Volume vs Storage Class [네이버 블로그]
파드가 PV 에 직접 접근하는 것이 아닌 PVC 를 거쳐서 PV 에 접근한다.
PVC(Persitent Volume Claim)는 PV 에 접근하기 위한 카드 키 같은 개념이다.
파드가 PVC 를 통해 PV 에 접근하는 yaml 파일을 살펴보면 아래와 같다.
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
PV 는 hostPath 를 이용해서 호스트 노드의 /mnt/data
경로에 있는 디렉토리를 10GB 사용한다. manual
이라는 이름을 가진 스토리지 클래스(Storage Class)를 사용한다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: manual
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
PVC 의 resources.requests.storage
속성은 PVC 가 필요로 하는 최소한의 스토리지 용량을 의미하는데, 여기서는 최소 3GB 이상의 PV 를 필요하다는 것을 의미한다. 이 클레임은 스토리지 클래스의 이름이 manual
인 PV 에 연결한다.
파드에서는 task-pv-claim
이라는 클레임 이름을 가진 PVC 를 할당했다. 앞에서 정의한 PVC 에 따라 manual
스토리지 클래스에 해당하는 PV 의 /usr/share/nginx/html
에 경로에 데이터를 저장한다. 실제로는 노드 파일 시스템 관점에서 보면 /mnt/data/usr/share/nginx/html
경로에 파드의 데이터가 저장되는 것이다.
PVC 를 사용하는 이유는 클러스터를 이용하는 개발자가 PV 에 대해 정확히 몰라도 데이터를 저장할 수 있도록 지원하기 위함이다.
데이터를 노드 파일 시스템에 저장할 지, 네트워크 파일 시스템에 저장할 지는 시스템 관리자가 정하고, 개발자는 그 부분까지는 신경쓰지 않도록 역할을 분리한 것이다.
아래의 이미지처럼 PV 는 시스템 관리자가 직접 설정해주고, 개발자는 PVC 의 스토리지 클래스만 알면 데이터를 영속적으로 관리할 수 있다.
출처: 쿠버네티스 PV/PVC [velog]
또한, RBAC 을 이용해서 사용자나 서비스 계정에 따라 PVC 를 포함한 다양한 리소스에 권한을 제한할 수 있다.
아래의 파일은 PVC 를 생성할 수 있는 권한을 정의한 예시이다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pvc-creator
rules:
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["create"]
pvc-creator
라는 이름의 역할은 PVC 를 생성할 수 있는 권한을 가질 수 있도록 했다.
아래의 파일은 pvc-creator
역할을 alice 라는 유저에게 부여하는 예시이다.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pvc-creator-binding
subjects:
- kind: User
name: alice
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pvc-creator
apiGroup: rbac.authorization.k8s.io
PV 를 사용했을 때 데이터가 영구적으로 저장된다는 것을 확인하기 위해 PV 를 사용하지 않으면 파드의 삭제와 함께 데이터도 삭제되는지 확인해보자.
아래의 yaml 파일은 10초마다 현재 시간을 /home/pod-out.txt
파일의 마지막에 추가하는 파드를 정의한 것이다.
# 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"
파드를 배포하고 파일의 내용을 살펴보면 아래와 같이 출력된다.
kubectl apply -f date-busybox-pod.yaml
#pod/busybox created
kubectl exec busybox -- tail -f /home/pod-out.txt
#Tue Mar 19 06:17:19 UTC 2024
#Tue Mar 19 06:17:29 UTC 2024
#...
마지막으로 저장된 시간은 Tue Mar 19 06:17:29 UTC 2024
이다.
이 파드를 삭제하고 다시 실행해서 파일을 살펴보면 아래와 같다.
kubectl delete pod busybox
#pod "busybox" deleted
kubectl apply -f date-busybox-pod.yaml
#pod/busybox created
kubectl exec busybox -- tail -f /home/pod-out.txt
#Tue Mar 19 06:17:45 UTC 2024
출력 내용이 이전에 삭제한 파드에서 마지막으로 저장한 시간과 다르다는 것을 알 수 있다.
즉, 기본적으로 파드는 컨테이너 이미지 파일 시스템에 파일을 저장하기 때문에 파드를 삭제하면 파일 시스템 안에 저장된 데이터도 함께 삭제되는 것이다.
PV 는 local-path-provisioner(Github 링크) 를 이용해서 노드의 파일 시스템에 PV 를 생성하도록 했다.
이를 이용하면 storageClass 에 local-path
로 입력해서 PV 를 사용할 수 있다.
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
로 생성된 것을 확인할 수 있다.
kubectl get sc local-path
#NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
#local-path rancher.io/local-path Delete WaitForFirstConsumer false 2m15s
우선 아래와 같은 PVC 를 먼저 생성한다.
# localpath1.yaml
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 Pending local-path 15s
kubectl describe pvc
#Name: localpath-claim
#Namespace: default
#StorageClass: local-path
#Status: Pending
#Volume:
#Labels: <none>
#Annotations: <none>
#Finalizers: [kubernetes.io/pvc-protection]
#Capacity:
#Access Modes:
#VolumeMode: Filesystem
#Used By: <none>
#Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Normal WaitForFirstConsumer 32s (x12 over 3m17s) persistentvolume-controller waiting for first consumer to be created before binding
그리고 이번에는 5초마다 현재 시간을 파일에 저장하는 파드를 실행하고, 위에서 생성한 PVC 를 이용해서 PV 에 데이터를 저장할 수 있도록 아래의 yaml 파일을 사용했다.
# localpath2.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: localpath-claim
kubectl apply -f localpath2.yaml
생성된 파드, PV, PVC 를 확인하면 아래와 같다.
kubectl get pod,pv,pvc
#NAME READY STATUS RESTARTS AGE
#pod/app 1/1 Running 0 24s
#NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
#persistentvolume/pvc-fc125d30-6353-4808-83a6-b9d3c8d3a7cc 1Gi RWO Delete Bound default/localpath-claim local-path 18s
#NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
#persistentvolumeclaim/localpath-claim Bound pvc-fc125d30-6353-4808-83a6-b9d3c8d3a7cc 1Gi RWO local-path 8m29s
PV 와 PVC 의 STATUS 가 Bound
로 되어 있다는 것을 확인할 수 있다.
파드 안에서 데이터가 저장되고 있는 지 확인해본다.
kubectl exec -it app -- tail -f /data/out.txt
#Tue Mar 19 06:37:56 UTC 2024
#Tue Mar 19 06:38:01 UTC 2024
#Tue Mar 19 06:38:06 UTC 2024
#Tue Mar 19 06:38:11 UTC 2024
#Tue Mar 19 06:38:16 UTC 2024
#Tue Mar 19 06:38:21 UTC 2024
#Tue Mar 19 06:38:26 UTC 2024
#Tue Mar 19 06:38:31 UTC 2024
#Tue Mar 19 06:38:36 UTC 2024
#Tue Mar 19 06:38:41 UTC 2024
#...
참고로 지금 생성된 PV 는 현재 노드에서만 사용할 수 있다.
다른 노드의 파일 시스템에서 PV 의 경로를 확인해보면 출력되지 않는 것을 확인할 수 있다.
for node in $N1 $N2 $N3; do ssh ec2-user@$node tree /opt/local-path-provisioner; done
#/opt/local-path-provisioner
#`-- pvc-fc125d30-6353-4808-83a6-b9d3c8d3a7cc_default_localpath-claim
# `-- out.txt
#1 directory, 1 file --> 1번 노드에만 PV 가 생성되었고, 1번 노드만 사용 가능
#/opt/local-path-provisioner [error opening dir]
#0 directories, 0 files
#/opt/local-path-provisioner [error opening dir]
#0 directories, 0 files
파드를 삭제하고 다시 실행해서 데이터의 내용을 살펴보자.
이번에는 파일의 마지막이 아닌 파일의 처음 부분을 확인해볼 것이다.
kubectl delete pod app
kubectl exec -it app -- head /data/out.txt # 파일의 첫 부분을 확인
#Tue Mar 19 06:37:56 UTC 2024
#Tue Mar 19 06:38:01 UTC 2024
#Tue Mar 19 06:38:06 UTC 2024
#Tue Mar 19 06:38:11 UTC 2024
#...
파드가 삭제되었지만 파드의 데이터가 정상적으로 PV 에 저장되어 영구적으로 남아있는 것을 확인할 수 있다.
출처: [쿠버네티스 쉽게 이해하기 11] 데이터 저장소 사용을 위한 PV/PVC [티스토리]
파드에는 위의 이미지처럼 네트워크 볼륨을 파드에 마운트 할 수 있다.
아래의 파일은 네트워크 볼륨을 마운트 하는 예시이다.
# NFS PersistentVolume 정의
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
nfs:
path: /path/to/nfs/share
server: nfs-server-ip
# NFS PersistentVolumeClaim 정의
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
# Pod 정의
apiVersion: v1
kind: Pod
metadata:
name: nfs-pod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-pvc
하지만 이렇게 되면 쿠버네티스와 노드 간에 결합이 강해지는 문제가 생긴다.
예를 들어, nfs 네트워크 볼륨을 마운트 하기 위해서는 nfs 서버의 IP 나 호스트를 지정해야 하는데, nfs 서버의 IP 가 변경되면 서비스를 이용할 수 없는 문제가 생긴다.
출처: [쿠버네티스 쉽게 이해하기 11] 데이터 저장소 사용을 위한 PV/PVC [티스토리]
쿠버네티스는 PV, PVC, CSI 를 중계자 역할로 만들었고, 그 중에서도 CSI 는 다양한 스토리지 솔루션(AWS EBS, EFS 등)을 이용할 때 사용한다.
CSI 는 클러스터에 별도로 설치해주어야 하며, 스토리지 솔루션의 스토리지 클래스를 생성할 수 있다.
아래의 파일은 example.com/nfs
라는 프로비저너를 통해 스토리지 클래스를 만드는 예시이다.
# NFS StorageClass 정의
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: example.com/nfs
volumeBindingMode: WaitForFirstConsumer
# NFS PersistentVolumeClaim 정의
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
storageClassName: nfs-storage
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
# Pod 정의
apiVersion: v1
kind: Pod
metadata:
name: nfs-pod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-pvc
이처럼 CSI 를 이용하면 AWS, GCP, Azure 등 스토리지 솔루션을 사용할 수 있다.
앞서 쿠버네티스에서 파드의 데이터를 영속적으로 관리하기 위한 방법을 다루었다.
그 중에서도 AWS EBS 에 연결해서 저장하는 실습 과정을 정리했다.
참고로 EC2 에서 사용할 수 있는 스토리지는 크게 4가지이다.
참고: Storage options for your Amazon EC2 instances [aws docs]
각각에 대한 설명은 자세한 생략하고 이 글에서는 S3 를 제외한 나머지 3개의 스토리지에 연결하는 것을 정리했다.
S3 도 CSI 를 지원하지만 같지만, 공식 문서에 따르면 아직 S3 는 Fargate 나 윈도우 기반 컨테이너 이미지와는 호환되지 않고, 정적 프로비저닝만 가능하다고 하다.
즉, 필요하면 동적으로 S3 버킷을 자동으로 생성해주는 동적 프로비저닝은 못한다는 의미이다.
AWS EBS 는 EC2 인스턴스의 데이터를 영구적으로 저장할 수 있는 스토리지 볼륨이다.
EBS 는 EC2 인스턴스와 물리적으로 동일한 장치에 있는 인스턴스 스토어(Instance Store)가 아닌 네트워크를 통해서 EC2 인스턴스에 마운트 한다.
그래서 EC2 와 EBS 가 같은 가용 영역에 있어야 사용할 수 있다.
EBS 는 기본적으로 하나의 EC2 인스턴스에만 마운트 할 수 있다.
동시에 여러 EC2 인스턴스에 같은 EBS 를 마운트 할 수 없는데, 이 경우에는 EFS 를 사용할 수 있다.
이 같은 특성 때문에 EBS 를 PV 로 사용하고자 한다면 PV 와 PVC 의 accessModes
는 ReadWriteOnce(RWO) 로 설정해주어야 한다.
RWO 액세스 모드는 한 번에 하나의 노드에만 마운트 되는데, 이는 동시에 여러 노드에서 동일한 볼륨에 쓰기를 시도하는 경우에 발생하는 데이터 충돌을 방지하기 위해서다.
EKS 에서 EBS 를 사용하기 위해서는 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 가 생성되었는지 확인한다.
eksctl get iamserviceaccount --cluster myeks
#NAMESPACE NAME ROLE ARN
#kube-system ebs-csi-controller-sa arn:aws:iam::265524074804:role/AmazonEKS_EBS_CSI_DriverRole
Amazon EBS CSI Driver addon 을 추가한다.
# 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}
#NAME VERSION STATUS ISSUES IAMROLEUPDATE AVAILABLE CONFIGURATION VALUES
#aws-ebs-csi-driver v1.28.0-eksbuild.1 CREATING 0 arn:aws:iam::265524074804:role/AmazonEKS_EBS_CSI_DriverRole
#...
설치가 완료되면 EBS 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
아래의 yaml 파일은 EBS gp3 스토리지 클래스를 정의한 것이다.
# 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
스토리지 클래스를 생성한다.
kubectl apply -f gp3-sc.yaml
스토리지 클래스를 확인하면 gp3 스토리지 클래스가 생성된 것을 확인할 수 있다.
kubectl get sc
#NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
#gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 48m
#gp3 ebs.csi.aws.com Delete WaitForFirstConsumer true 10s
#local-path rancher.io/local-path Delete WaitForFirstConsumer false 30m
EBS 에 연결하기 위한 PVC 를 아래와 같이 정의했다.
# awsebs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ebs-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
storageClassName: gp3
PVC 와 PV 를 확인해보면 아래와 같이 출력된다.
kubectl get pvc,pv
#NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
#persistentvolumeclaim/ebs-claim Pending gp3 11s
PVC 는 생성되었지만, PV 는 출력되지 않았다.
아직 파드와 PV 를 연결하지 않았기 때문이다.
파드를 생성하기 위해 아래의 yaml 파일을 사용했다. 이 파드도 5초마다 현재 시간을 파일에 저장하도록 했다.
# 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
파드를 배포하고 PVC, PV 를 확인해보면 아래와 같다.
kubectl apply -f awsebs-pod.yaml
kubectl get pvc,pv
#NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
#persistentvolumeclaim/ebs-claim Bound pvc-420a109f-312a-4150-8109-ceeda1ec4b2f 4Gi RWO gp3 3m4s
#NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
#persistentvolume/pvc-420a109f-312a-4150-8109-ceeda1ec4b2f 4Gi RWO Delete Bound default/ebs-claim gp3 35s
파드를 생성하면서 PVC 가 PV 를 요청했고, AWS EBS 가 새롭게 프로비저닝 되어 파드에 마운트 된 것을 확인할 수 있다.
추가로 PV 를 확인해보면 nodeAffinity
항목을 확인할 수 있다.
kubectl get pv -o yaml | yh
#...
# nodeAffinity:
# required:
# nodeSelectorTerms:
# - matchExpressions:
# - key: topology.ebs.csi.aws.com/zone
# operator: In
# values:
# - ap-northeast-2b
#...
nodeAffinity
는 리소스를 스케줄링 할 때 어떤 노드에 붙을 지 지정하는 옵션이다.
여기서는 PV 가 바인딩 될 수 있는 노드는 EBS CSI 를 사용하고, ap-northeast-2b 가용 영역에 있는 노드로만 제한했다.
AWS 콘솔에서도 PVC 에 의해 EBS 볼륨이 생성된 것을 확인할 수 있다.
AWS EBS 는 스냅샷 기능을 지원한다.
스냅샷은 EBS 의 특정 시점에 있는 데이터를 사진처럼 찍어서 나중에 필요할 때 백업을 목적으로 사용할 수 있다.
EBS Controller 와 마찬가지로 EKS 에 Snapshot Controller 를 설치하면 스냅샷을 생성하는 것이 가능하다.
아래의 명령어를 실행해서 Snapshot CRD 를 생성했다.
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
#volumesnapshotclasses.snapshot.storage.k8s.io 2024-03-19T07:29:42Z
#volumesnapshotcontents.snapshot.storage.k8s.io 2024-03-19T07:29:42Z
#volumesnapshots.snapshot.storage.k8s.io 2024-03-19T07:29:42Z
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
다음으로 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
정상적으로 설치되었는지 확인한다.
# Deployment 확인
kubectl get deploy -n kube-system snapshot-controller
#NAME READY UP-TO-DATE AVAILABLE AGE
#snapshot-controller 2/2 2 2 3m23s
# Pod 확인
kubectl get pod -n kube-system | grep snapshot-controller
#snapshot-controller-749cb47b55-4hz6q 1/1 Running 0 3m54s
#snapshot-controller-749cb47b55-cl7jc 1/1 Running 0 3m54s
마지막으로 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
#NAME DRIVER DELETIONPOLICY AGE
#csi-aws-vsc ebs.csi.aws.com Delete 67s
이전에 사용했던 PVC 와 파드를 다시 생성해서 VolumeSnapshot 을 생성해보고자 한다.
PVC 와 파드를 생성하고 나서 아래의 yaml 파일을 이용해서 EBS 의 스냅샷을 생성한다.
# 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
# 스냅샷 생성
kubectl apply -f ebs-volume-snapshot.yaml
정상적으로 생성되었는지 확인한다.
kubectl get volumesnapshot
#NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
#ebs-volume-snapshot false ebs-claim 4Gi csi-aws-vsc snapcontent-983260be-d89f-4746-9e8c-70e0a5b0efa3 4s 5s
AWS 콘솔에서도 스냅샷이 생성된 것을 확인할 수 있다.
볼륨 스냅샷을 이용해서 PVC 를 생성해보자.
우선 파드와 pvc, ebs-claim 을 삭제한다.
kubectl delete pod app && kubectl delete pvc ebs-claim
스냅샷을 이용한 PVC 복원에 사용한 yaml 파일은 아래와 같다.
# 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
복원을 진행한다.
kubectl apply -f ebs-snapshot-restored-claim.yaml
PVC 는 생성되었지만 PV 는 생성되지 않았다.
kubectl get pvc,pv
#NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
#persistentvolumeclaim/ebs-snapshot-restored-claim Pending gp3 48m
PV 생성을 위해 파드를 생성한다.
파드 생성을 위해 아래의 yaml 파일을 사용했다.
# 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
아래의 명령어를 이용해서 파드를 생성한다.
kubectl apply -f ebs-snapshot-restored-pod.yaml
PVC 와 PV 를 확인하면 PV 도 생성된 것을 확인할 수 있다.
kubectl get pvc,pv
#NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
#persistentvolumeclaim/ebs-snapshot-restored-claim Bound pvc-0cd0f9a9-77ef-4104-8e51-dad64eaf4831 4Gi RWO gp3 50m
#NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
#persistentvolume/pvc-0cd0f9a9-77ef-4104-8e51-dad64eaf4831 4Gi RWO Delete Bound default/ebs-snapshot-restored-claim gp3 8s
/data/out.txt
에 저장된 내용을 확인하면 아래와 같다.
kubectl exec app -- cat /data/out.txt
#Tue Mar 19 07:37:36 UTC 2024
#Tue Mar 19 07:37:41 UTC 2024
#Tue Mar 19 07:37:46 UTC 2024
#Tue Mar 19 07:37:51 UTC 2024
#Tue Mar 19 07:37:56 UTC 2024
#Tue Mar 19 07:38:01 UTC 2024
#Tue Mar 19 07:38:06 UTC 2024 ---> 스냅샷의 마지막 데이터
#Tue Mar 19 08:34:33 UTC 2024 ---> 복원 후 새롭게 생성된 데이터
#Tue Mar 19 08:34:38 UTC 2024
#Tue Mar 19 08:34:43 UTC 2024
#Tue Mar 19 08:34:48 UTC 2024
#Tue Mar 19 08:34:53 UTC 2024
#Tue Mar 19 08:34:58 UTC 2024
스냅샷의 마지막 데이터로부터 새롭게 데이터를 저장하고 있는 것을 확인할 수 있다.
EBS 는 한 번에 하나의 EC2 인스턴스에만 마운트 되기 때문에 여러 노드가 같은 데이터를 공유하기 어렵다는 문제가 있었다.
이를 해결하기 위해 AWS EFS 를 이용해보자.
EFS 를 이용한 클러스터 구성은 아래의 이미지와 같다.
출처: AWS EKS With EFS CSI Driver And IRSA Using CDK [dev.to]
EKS 에서 제공하는 addon(추가기능)을 이용해서 설치했다. (공식 문서 링크)
우선 IAM 역할을 생성한다.
export CLUSTER_NAME=my-eks
export ROLE_NAME=AmazonEKS_EFS_CSI_DriverRole
eksctl create iamserviceaccount \
--name efs-csi-controller-sa \
--namespace kube-system \
--cluster $CLUSTER_NAME \
--role-name $ROLE_NAME \
--role-only \
--attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEFSCSIDriverPolicy \
--approve
그 다음 IAM 역할에 EFS CSI Controller 와 관련된 정책을 assume 한다.
TRUST_POLICY=$(aws iam get-role --role-name $ROLE_NAME --query 'Role.AssumeRolePolicyDocument' | \
sed -e 's/efs-csi-controller-sa/efs-csi-*/' -e 's/StringEquals/StringLike/')
aws iam update-assume-role-policy --role-name $ROLE_NAME --policy-document "$TRUST_POLICY"
IRSA 를 확인한다.
eksctl get iamserviceaccount --cluster myeks
#NAMESPACE NAME ROLE ARN
#kube-system efs-csi-controller-sa arn:aws:iam::265524074804:role/AmazonEKS_EFS_CSI_DriverRole
EFS CSI Driver addon 을 추가하기 위해 아래의 명령어를 실행한다.
eksctl create addon \
--name aws-efs-csi-driver \
--cluster ${CLUSTER_NAME} \
--service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EFS_CSI_DriverRole \
--force
addon 이 설치되었는지 아래의 명령어를 실행해서 확인한다.
eksctl get addon --cluster ${CLUSTER_NAME}
#NAME VERSION STATUS ISSUES IAMROLE UPDATE AVAILABLE CONFIGURATION VALUES
#aws-efs-csi-driver v1.7.6-eksbuild.1 ACTIVE 0 arn:aws:iam::265524074804:role/AmazonEKS_EFS_CSI_DriverRole
EFS 에 여러 파드가 사용할 수 있는 지 확인해보자.
우선 EFS 를 배스천 호스트의 /mnt/myefs
경로에 마운트 하기 위해 아래의 명령어를 사용했다.
mount -t efs -o tls $EfsFsId:/ /mnt/myefs
여기서는 AWS 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
생성한 스토리지 클래스는 아래와 같다.
# storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
스토리지 클래스를 생성한다.
kubectl apply -f storageclass.yaml
PV 생성을 위해 EFS 파일 시스템 ID 를 적용한다.
EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml
pv.yaml
파일의 내용은 아래와 같다.
# pv.yaml
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-09b6e666a9f2be276
PV 를 생성한다.
kubectl apply -f pv.yaml
PVC 는 아래의 yaml 파일을 사용해서 생성했다.
# claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
PVC 를 생성한다.
kubectl apply -f claim.yaml
아래의 yaml 파일을 이용해서 파드를 생성했다.
# pod1.yaml
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
# pod2.yaml
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
현재 접속한 배스천 호스트에서 EFS 파일 시스템 디렉토리를 확인한다.
tree /mnt/myefs
#/mnt/myefs
#|-- out1.txt
#`-- out2.txt
각각 다른 파드에서 실행되고 있는 pod1 과 pod2 에서 저장한 데이터를 배스천 호스트에서도 확인할 수 있다.
EKS 에서는 클러스터에 부하가 높아지면 자동으로 노드(EC2 인스턴스)를 추가하는 오토 스케일링 기능을 제공한다.
이때, 오토 스케일링으로 생성되는 노드의 타입(프로세서, CPU, RAM 등)을 미리 지정해놓을 수 있다.
최대 노드를 몇 개까지 확장시킬 지, 평소에는 몇 개를 유지할 지 등 미리 사전에 설정할 수 있는 것이 노드 그룹(Node Group)이다.
참고로 오토 스케일링을 위한 도구로는 쿠버네티스 클러스터 오토스케일러(CA)와 Karpenter 가 있다.
여기서는 eksctl 을 이용해서 노드 그룹을 생성하고, 노드 그룹에서 설정한 인스턴스가 생성되는 것을 확인하고자 한다.
여기서는 ARM 기반 graviton 프로세서를 사용하는 t4g.medium 을 노드 그룹에 생성해보고자 한다.
노드 그룹을 생성하기 위해 아래의 명령어를 이용해서 yaml 파일을 생성했다.
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
yaml 파일 내용은 아래와 같다.
# myng3.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
managedNodeGroups:
- amiFamily: AmazonLinux2
desiredCapacity: 1
disableIMDSv1: true
disablePodIMDS: false
iam:
withAddonPolicies:
albIngress: false
appMesh: false
appMeshPreview: false
autoScaler: false
awsLoadBalancerController: false
certManager: false
cloudWatch: false
ebs: false
efs: false
externalDNS: false
fsx: false
imageBuilder: false
xRay: false
instanceSelector: {}
instanceType: t4g.medium
labels:
alpha.eksctl.io/cluster-name: myeks
alpha.eksctl.io/nodegroup-name: ng3
family: graviton
maxSize: 1
minSize: 1
name: ng3
privateNetworking: false
releaseVersion: ""
securityGroups:
withLocal: null
withShared: null
ssh:
allow: true
publicKeyPath: ~/.ssh/id_rsa.pub
subnets:
- subnet-082b50963d6c944ef
- subnet-03e23edb6dd18f876
- subnet-029b2b9933d83de6f
tags:
alpha.eksctl.io/nodegroup-name: ng3
alpha.eksctl.io/nodegroup-type: managed
volumeIOPS: 3000
volumeSize: 30
volumeThroughput: 125
volumeType: gp3
metadata:
name: myeks
region: ap-northeast-2
version: "1.28"
아래의 명령어를 실행해서 노드 그룹을 생성한다.
eksctl create nodegroup -f myng3.yaml
노드 그룹을 확인하면 arm64
로 ng3 노드 그룹이 생성된 것을 확인할 수 있다.
kubectl get nodes --label-columns eks.amazonaws.com/nodegroup,kubernetes.io/arch
#NAME STATUS ROLES AGE VERSION NODEGROUP ARCH
#ip-192-168-1-17.ap-northeast-2.compute.internal Ready <none> 41m v1.28.5-eks-5e0fdde ng1 amd64
#ip-192-168-2-118.ap-northeast-2.compute.internal Ready <none> 41m v1.28.5-eks-5e0fdde ng1 amd64
#ip-192-168-3-186.ap-northeast-2.compute.internal Ready <none> 41m v1.28.5-eks-5e0fdde ng1 amd64
#ip-192-168-3-236.ap-northeast-2.compute.internal Ready <none> 2m18s v1.28.5-eks-5e0fdde ng3 arm64
ng3 노드 그룹에 taint 를 설정해서 파드가 스케줄링 될 때 ng3 그룹에 배치되도록 할 것이다.
파드는 toleration 옵션에서 fronted 라는 키를 가지고, NO_EXECUTE 라는 effect 가 명시되어 있어야 ng3 에 배치될 수 있다.
참고로 NO_EXECUTE 옵션은 toleration 이 없으면 파드가 스케줄링 되지 않으며, 기존에 실행되던 파드도 toleration 이 없으면 종료시키는 옵션이다.
aws eks update-nodegroup-config --cluster-name $CLUSTER_NAME --nodegroup-name **ng3** --taints "addOrUpdateTaints=[{key=frontend, value=true, effect=**NO_EXECUTE**}]"
참고로 taint 는 nodeAffinity 와 비슷한 개념이라고 생각하면 된다.
nodeAffinity 는 어느 자원에 붙을 지 명시하는 것이고, taint 는 toleration (용인) 이 적혀있는 리소스만 붙을 수 있도록 접근을 제한하는 것이다.
taint 설정이 되었는지 확인한다.
kubectl describe nodes --selector family=graviton | grep Taints
#Taints: frontend=true:NoExecute
아래의 yaml 파일을 이용해서 파드를 배포한다.
# 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
kubectl apply -f busybox.yaml
파드가 배포되었는 지 확인한다.
k get pod -owide
#NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
#busybox 1/1 Running 0 6m54s 192.168.3.100 ip-192-168-3-236.ap-northeast-2.compute.internal <none> <none>
ip-192-168-3-236.ap-northeast-2.compute.internal
라는 이름을 가진 노드에 배포된 것을 확인할 수 있다.
ng3 이라는 노드 그룹에 속한 노드에 대한 정보를 찾아보면 아래와 같다.
kubectl describe node -l eks.amazonaws.com/nodegroup=ng3
#Name: ip-192-168-3-236.ap-northeast-2.compute.internal
#Roles: <none>
#Labels: alpha.eksctl.io/cluster-name=myeks
# alpha.eksctl.io/nodegroup-name=ng3
# beta.kubernetes.io/arch=arm64
# beta.kubernetes.io/instance-type=t4g.medium
# ...
#Non-terminated Pods: (5 in total)
# Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
# --------- ---- ------------ ---------- --------------- ------------- ---
# default busybox 0 (0%) 0 (0%) 0 (0%) 0 (0%) 9m41s
# kube-system aws-node-h6hzh 50m (2%) 0 (0%) 0 (0%) 0 (0%) 21m
# kube-system ebs-csi-node-9g5wq 30m (1%) 0 (0%) 120Mi (3%) 768Mi (23%) 21m
# kube-system efs-csi-node-2tcsb 0 (0%) 0 (0%) 0 (0%) 0 (0%) 21m
# kube-system kube-proxy-svrqf 100m (5%) 0 (0%) 0 (0%) 0 (0%) 21m
# ...
Name 속성에는 busybox 가 배포된 노드의 이름이 적혀있고, 실행 중인 파드를 살펴봐도 busybox 라는 이름으로 실행되고 있는 것을 확인할 수 있다.
시간이 갈수록 쿠버네티스에 대한 이해도가 높아지고 있는 것을 느끼고 있다.
쿠버네티스를 처음 학습할 때는 PV, PVC, CSI, taint, nodeAffinity 개념이 잘 이해되지 않았는데, 반복해서 보다보니 이해되기 시작했다.
컨테이너가 종료되면 왜 데이터가 영구적으로 남지 않고 휘발성으로 사라지는 이유를 이해하는데는 컨테이너를 직접 만들어보는 실습을 했던 것이 큰 도움이 됐다.
또한, 원활한 실습 환경을 마련해주신 가시다님 덕분에 쿠버네티스의 본질에 집중해서 공부할 수 있었다고 생각한다.
아무 것도 모르는 상태에서 나 혼자 이 모든 환경을 설정하고 삽질 했다면 시간도 많이 걸렸을 거고, 삽질하다가 지쳐버려서 포기했을 수도 있을 것 같다.
물론 삽질도 귀한 경험이지만, 쿠버네티스에 대한 이해나 AWS 생태계에 대한 이해 없이 삽질 한다는 건 정말 힘든 일 같다.
그리고 EFS CSI Driver 를 addon 으로 설치해봤는데, 생각보다 쉽게 진행되어서 놀랐다.
도전과제에 있길래 막연히 어렵다고만 생각했는데, 의외로 스터디에서 helm 으로 설치하는 방법보다 훨씬 간단했다.
실습을 진행할수록 AWS 가 정말 서비스들을 잘 만들었다는 감탄이 절로 나온다.
추상화가 잘 되어 있다보니 직접 환경 세팅에 들어가는 수고가 많이 줄어드는 것을 느꼈다.
1주차에는 내가 과연 이 스터디를 잘 따라갈 수 있을까 너무 걱정도 되고, 압박감도 많이 느꼈다.
EKS 를 실제로 써보는 것도 처음이고, 쿠버네티스도 막연하게 알고 있는 상태였기 때문에 부담을 느꼈던 것 같다.
하지만 시간이 지날수록 모르는 것들이 하나씩 이해가 되니까 재미있어졌고, 할 수 있다는 자신감도 생겼다.
모르는 것은 ChatGPT 와 Phind 와 같은 인공지능 서비스로 물어보면서 개념의 큰 흐름을 잡았다.
인공지능은 틀릴 수도 있으니 사실 확인이 필요한 부분은 공식 문서나 다른 블로그 자료를 참고하면서 완성도를 높여나갔다.
다음 스터디 주제도 꽤 기대된다!