본 게시물은 CloudNet@팀 Gasida(서종호) 님이 진행하시는
AWS EKS Workshop Study 내용을 기반으로 작성되었습니다.
Pod는 상태가 없는 Stateless 속성을 가지고 있음.
즉, Pod가 정지되면 내부 데이터는 모두 삭제됨.

데이터베이스와 같이 데이터 보존이 필요한 Stateful 애플리케이션은
Persistent Volume(PV)를 사용해 Pod 외부의 Storage를 연결하여 사용 가능함.

Pod가 생성될 때 자동으로 Volume을 Mount하여 Pod에 연결하는 기능을 동적 프로비저닝(Dynamic Provisioning)이라고 함.

k8s에서는 Reclaim Policy를 사용해, PV의 사용이 끝났을 때
해당 Volume을 Retain(보존) 혹은 Delete(삭제) 할 수 있음.

Kubernetes source code 내부에 존재하는 AWS EBS provisioner는 Kubernetes release lifecycle을 따라서 배포되므로, provisioner 신규 기능을 사용하기 위해서는 Kubernetes version을 업그레이드해야 하는 제약 사항이 있다.
이러한 제약을 극복하기 위해 Kubernetes 내부에 내장된 provisioner (in-tree)를 사용하지 않고, 별도의 controller Pod을 통해 동적 provisioning을 사용할 수 있도록 만든 것이 CSI (Container Storage Interface) driver 이다.
CSI를 사용하면 k8s의 공통화된 인터페이스를 통해 다양한 프로바이더를 사용할 수 있다.

일반적인 CSI driver의 구조와 동일하게 AWS EBS CSI Driver 또한 아래와 같은 구조를 가지고 있다.
오른쪽의 controller pod는 AWS API를 사용해 EBS Volume을 생성하는 역할을 하며, 왼쪽의 node pod는 k8s node(ec2 instance)에 EBS volume을 attach 해준다.

기본 컨테이너 환경의 임시 파일시스템 사용
# 파드 배포
# date 명령어로 현재 시간을 10초 간격으로 /home/pod-out.txt 파일에 저장
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
kubectl exec busybox -- tail -f /home/pod-out.txt
Sat Mar 23 09:55:21 UTC 2024
Sat Mar 23 09:55:31 UTC 2024
Sat Mar 23 09:55:41 UTC 2024
Sat Mar 23 09:55:51 UTC 2024
Sat Mar 23 09:56:01 UTC 2024
Sat Mar 23 09:56:11 UTC 2024
...
# 파드 삭제 후 다시 생성 후 파일 정보 확인 > 이전 기록이 보존되어 있지 않음
kubectl delete pod busybox
kubectl apply -f date-busybox-pod.yaml
kubectl exec busybox -- tail -f /home/pod-out.txt
Sat Mar 23 09:56:50 UTC 2024
Sat Mar 23 09:57:00 UTC 2024
Sat Mar 23 09:57:10 UTC 2024
# 실습 완료 후 삭제
kubectl delete pod busybox
hostPath를 사용하는 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
# 확인
kubectl get-all -n local-path-storage
NAME NAMESPACE AGE
configmap/kube-root-ca.crt local-path-storage 11s
configmap/local-path-config local-path-storage 10s
pod/local-path-provisioner-5d854bc5c4-l6p89 local-path-storage 10s
serviceaccount/default local-path-storage 11s
serviceaccount/local-path-provisioner-service-account local-path-storage 11s
deployment.apps/local-path-provisioner local-path-storage 10s
replicaset.apps/local-path-provisioner-5d854bc5c4 local-path-storage 10s
rolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind local-path-storage 10s
role.rbac.authorization.k8s.io/local-path-provisioner-role local-path-storage 11s
kubectl get pod -n local-path-storage -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
local-path-provisioner-5d854bc5c4-l6p89 1/1 Running 0 97s 192.168.3.27 ip-192-168-3-39.ap-northeast-2.compute.internal <none> <none>
kubectl describe cm -n local-path-storage local-path-config
kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 43m
local-path rancher.io/local-path Delete WaitForFirstConsumer false 4m47s
kubectl get sc local-path
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 5m6s
PV/PVC를 사용하는 Pod 생성
# PVC 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath1.yaml
cat localpath1.yaml | yh
kubectl apply -f localpath1.yaml
# PVC 확인
kubectl get pvc
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 9s (x2 over 17s) persistentvolume-controller waiting for first consumer to be created before binding
# 파드 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/localpath2.yaml
cat localpath2.yaml | yh
kubectl apply -f localpath2.yaml
# 파드 확인
kubectl get pod,pv,pvc
kubectl describe pv # Node Affinity 확인
Name: pvc-2a40c101-3c3d-40ab-9cad-dc1ce35ad4b8
Labels: <none>
Annotations: local.path.provisioner/selected-node: ip-192-168-1-171.ap-northeast-2.compute.internal
pv.kubernetes.io/provisioned-by: rancher.io/local-path
Finalizers: [kubernetes.io/pv-protection]
StorageClass: local-path
Status: Bound
Claim: default/localpath-claim
Reclaim Policy: Delete
Access Modes: RWO
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity:
Required Terms:
Term 0: kubernetes.io/hostname in [ip-192-168-1-171.ap-northeast-2.compute.internal]
Message:
Source:
Type: HostPath (bare host directory volume)
Path: /opt/local-path-provisioner/pvc-2a40c101-3c3d-40ab-9cad-dc1ce35ad4b8_default_localpath-claim
HostPathType: DirectoryOrCreate
Events: <none>
kubectl exec -it app -- tail -f /data/out.txt
Sat Mar 23 10:10:15 UTC 2024
Sat Mar 23 10:10:20 UTC 2024
...
# 워커노드 중 현재 파드가 배포되어 있다만, 아래 경로에 out.txt 파일 존재 확인
for node in $N1 $N2 $N3; do ssh ec2-user@$node tree /opt/local-path-provisioner; done
/opt/local-path-provisioner
└── pvc-2a40c101-3c3d-40ab-9cad-dc1ce35ad4b8_default_localpath-claim
└── out.txt
# 해당 워커노드 자체에서 out.txt 파일 확인 : 아래 굵은 부분은 각자 실습 환경에 따라 다름
ssh ec2-user@$N1 tail -f /opt/local-path-provisioner/pvc-f1615862-e4cd-47d0-b89c-8d0e99270678_default_localpath-claim/out.txt
Sat Mar 23 10:11:55 UTC 2024
Sat Mar 23 10:12:00 UTC 2024
Sat Mar 23 10:12:05 UTC 2024
Sat Mar 23 10:12:10 UTC 2024
...
Pod 삭제 후 재생성 시 데이터 유지 확인
# 파드 삭제 후 PV/PVC 확인
kubectl delete pod app
kubectl get pod,pv,pvc
for node in $N1 $N2 $N3; do ssh ec2-user@$node tree /opt/local-path-provisioner; done
/opt/local-path-provisioner
└── pvc-2a40c101-3c3d-40ab-9cad-dc1ce35ad4b8_default_localpath-claim
└── out.txt
# 파드 다시 실행
kubectl apply -f localpath2.yaml
# 확인
kubectl exec -it app -- head /data/out.txt
Sat Mar 23 10:10:00 UTC 2024
Sat Mar 23 10:10:05 UTC 2024
Sat Mar 23 10:10:10 UTC 2024
Sat Mar 23 10:10:15 UTC 2024
kubectl exec -it app -- tail -f /data/out.txt
Sat Mar 23 10:15:40 UTC 2024
Sat Mar 23 10:15:45 UTC 2024
Sat Mar 23 10:15:50 UTC 2024
Sat Mar 23 10:15:55 UTC 2024