EKS Storage 기초 개념
Kubernetes의 Storage
Kubernetes의 Pod는 내부의 Container 마다 로컬디스크가 생성되는데, 해당 로컬디스크는 Pod가 삭제되면 같이 삭제된다. 또한 Pod내부의 Container는 각각 고유한 로컬디스크를 가지기 때문에 Container 간에 파일을 공유할 수 없게 된다.
하지만 POD로 구동되는 서비스 중에는 Pod가 중지되어도 삭제되지않고 주기적으로 보관되어야 하는 데이터를 가질 수 도 있으며, 다른 Container 서비스와 파일을 공유해서 사용하고 싶은 경우가 발생한다.
이런 이유로 인해 별도의 리소스인 Volume이 만들어졌다.
Kubernetes의 Volume
Kuvernetes는 다양한 종류의 Volume을 지원하며, 각 Volume 별로 고유한 장단점이 존재한다.
https://kubernetes.io/ko/docs/concepts/storage/volumes/#volume-types
Persistent Volume과 Persistent Volume Claim
Persistent Volume(PV)과 어떤 외부 디스크를 쓸지에 대한 정의이고,
Persistent Volume Claim은 해당 외부 디스크를 어떻게 쓸지에 대한 정책 또는 요청사항이라고 볼 수 있다.
Persistent Volume이란?
Persistent Volume(PV)는 Kubernetes의 Volume의 하나로
영구적인 스토리지를 제공하는데 사용되는 Kubernetes 리소스이며,
Pod의 생성,삭제와 같은 Pod 생명주기와는 별개의 자체 생명 주기를 가진다.
PV는 추상적인 개념으로 실제 물리적인 디스크는 다른 곳에서 준비(plugin)하고 PV로 사용 선언해야한다.
- csi : Container Storage Interface(CSI) → (예: Amazon EFS , Amazon EBS , Amazon FSx 등)
- iscsi : iSCSI(SCSI over IP) 스토리지
- local : 노드에 마운트된 로컬 저장 장치
- nfs : 네트워크 파일 시스템(NFS) 스토리지
PV는 선언할 때 Access Mode라는 설정을 넣어줘야하는데 각 의미는 다음과 같다.
- ReadWriteOnce : 하나의 Node에만 연결 가능
- ReadOnlyMany : 여러개의 Node에 연결가능(읽기만 가능)
- ReadWriteMany : 여러개의 Node에 연결 가능(읽기/쓰기 가능)
- ReadWriteOncePod : 하나의 Pod에만 연결가능 (Kubernetes v1.27 [beta])
Access Mode는 실제 사용할 물리디스크 종류(Volume Plugin)에 따라 지원되지 않는 Mode도 있다.
(참고 : https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes)
Persistent Volume Claim이란?
아쉽지만 PV만으로는 직접 Pod에 연결할 수는 없다.
Pod에서 특정 PV를 사용하겠다고 요청해야하는데 이 요청이 Persistent Volume Claim(PVC)이다.
PV와 PVC의 생명주기
위에서 언급했듯이 PV와 PVC는 Pod와는 별개의 생명주기(Lifecycle)를 가지는데, 이는 4단계로 분류할 수 있다.
- Provisioning : PV가 생성되는 단계로, 정적(Static)프로비저닝 또는 동적(Dynamic)프로비저닝을 통해 생성될 수 있다.
- Static Provisioning : 사용자(예: 클러스터 관리자 등)가 PV를 직접 생성하는 방법이다. 이렇게 미리 PV를 생성하고 PVC 생성 시 연결해서 사용할 수 있는데, 만약 PV에서 생성한 용량이 10GB이고 과 PVC에서 요청한 용량이 20GB일 경우 PVC 생성에 실패한다.
- Dynamic Provisioning : PV를 직접 생성하지 않고 PVC 생성 시 PV가 내부적으로 자동 생성된다. 요청이 있을 때 필요한 만큼 생성이 가능하며, StorageClass라는 정책을 통해 Storage Provider(AWS EBS 등), 용량, Access Mode 등과 같은 특성을 설정한다.
- Binding : PVC와 PV가 연결되는 단계이다. PVC는 원하는 용량, 스토리지 클래스, 접근 권한과 같은 특성을 지정하여 PV를 바인딩할 수 있다.
- Using : PV가 Pod에 연결되서 사용되는 단계이다. Pod는 PVC를 사용해 PV에 액세스할 수 있다.
- Reclaiming : PV와 PVC가 삭제되는 단계이다. PVC 사용이 끝나고 삭제될 때 PV를 삭제할지 재사용할지 등을 설정할 수 있다.
- Delete : PV를 삭제하고 연결된 외부 디스크(ex: AWS EBS)까지 삭제한다.
- Retain : PVC를 삭제해도 PV는 보존한다. (만약 재사용하고자 한다면 PV만 논리적으로 삭제하고 실제 연결된 외부 디스크는 PV로 재정의해서 사용해야 한다.)
Recycling : PV에 데이터를 삭제한 뒤 다른 PVC에서 사용될 수 있도록 준비(현재는 Kubernetes에서 지원하지 않음)
PersistentVolume(EFS) 사용해보기
EKS 설치 확인
# EKS 설치 확인
(test_admin@myeks:N/A) [root@myeks-bastion-EC2 ~]# eksctl get cluster
NAME REGION EKSCTL CREATED
myeks ap-northeast-2 True
(test_admin@myeks:N/A) [root@myeks-bastion-EC2 ~]# eksctl get nodegroup --cluster myeks
CLUSTER NODEGROUP STATUS CREATED MIN SIZE MAX SIZE DESIRED CAPACITY INSTANCE TYPE IMAGE ID ASG NAME TYPE
myeks ng1 ACTIVE 2023-05-13T15:42:19Z 3 3 3 t3.medium AL2_x86_64 eks-ng1-d2c40ae3-71f8-4cbe-3e99-4bf9802a56dc managed
EFS 확인 및 EFS CSI Controller 설치
# AWS에 생성된 EFS 확인
aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
# EFS CSI Controller 설치를 위한 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(IAM Roles for Service Accounts) 설정
# 고객관리형 정책 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
# 위의 작업 완료 후 cloudfomation 스택이 생성됨
# eksctl-myeks-addon-iamserviceaccount-kube-system-efs-csi-controller-sa
# ISRA 확인
kubectl get sa -n kube-system efs-csi-controller-sa -o yaml | head -5
eksctl get iamserviceaccount --cluster ${CLUSTER_NAME}
# helm chart로 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 CSI Controller 설치 확인
(test_admin@myeks:N/A) [root@myeks-bastion-EC2 ~]# helm list -n kube-system
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
aws-efs-csi-driver kube-system 1 2023-05-14 01:16:07.606002263 +0900 KST deployed aws-efs-csi-driver-2.4.3 1.5.5
(test_admin@myeks:N/A) [root@myeks-bastion-EC2 ~]# kubectl get pod -n kube-system -l "app.kubernetes.io/name=aws-efs-csi-driver,app.kubernetes.io/instance=aws-efs-csi-driver"
NAME READY STATUS RESTARTS AGE
efs-csi-controller-6f64dcc5dc-lwt7q 3/3 Running 0 19s
efs-csi-controller-6f64dcc5dc-wnbxn 3/3 Running 0 19s
efs-csi-node-489db 3/3 Running 0 19s
efs-csi-node-4xwhl 3/3 Running 0 19s
efs-csi-node-k27g8 3/3 Running 0 19s
- 참고자료 : https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/efs-csi.html
- Region 별 Amazon 컨테이너 이미지 레지스트리 : https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/add-ons-images.html
EFS 를 사용하기 위한 StorageClass 생성
# StorageClass 배포를 위한 리소스 파일 생성 및 배포
cat <<EOF > storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
parameters:
provisioningMode: efs-ap
fileSystemId: fs-92107410 # 생성한 EFS의 파일시스템 아이디로 입력
directoryPerms: "700"
gidRangeStart: "1000" # optional
gidRangeEnd: "2000" # optional
basePath: "/wordpress" # optional
EOF
kubectl apply -f storageclass.yaml
kubectl get sc efs-sc
EFS를 Dynamic Povisioning 하는 Pod 배포해보기
StorageClass를 사용했기 때문에 별도로 PV를 정의하지 않고 동적 프로비저닝이 된다.
cat <<EOF >pv.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc # EFS StorageClass에서 정의한 이름으로 적어줘야 함
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
name: efs-app
spec:
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
EOF
# 기타 EFS 확인 명령어
# PVC/PV 생성 로그 확인
kubectl logs -n kube-system -l app=efs-csi-controller -c csi-provisioner -f
# 파드 정보 확인
kubectl exec -it efs-app -- sh -c "df -hT -t nfs4"
# 공유 저장소 저장 동작 확인
tree /mnt/myefs # 작업용EC2에서 확인
kubectl exec efs-app -- bash -c "cat data/out"
PersistentVolume(EBS) 사용해보기
https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/
EKS 환경에서 PersistentVolume을 사용해 MySQL과 WordPress 서비스를 생성해본다.
EBS CSI Controller 설치
# 사용가능한 aws-ebs-csi-driver 버전 전체 목록을 볼 수 있음
# True가 기본 설치되는 버전
aws eks describe-addon-versions \
--addon-name aws-ebs-csi-driver \
--kubernetes-version 1.24 \
--query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
--output text
v1.18.0-eksbuild.1
True
v1.17.0-eksbuild.1
False
...
# EFS와 같은 방식으로 ISRA 설정 진행
# 진행 시 Cloudformation 스택이 생성됨
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
# 현재 사용 중인 StorageClass 확인
(test_admin@myeks:default) [root@myeks-bastion-EC2 wordpress]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 4h43m
# gp3를 사용하는 새로운 StorageClass 생성
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
allowAutoIOPSPerGBIncrease: 'true'
encrypted: 'true'
#fsType: ext4
# 기본값이 ext4이며 xfs 등으로 변경가능
# 스냅샷의 경우 ext4로 기본으로 하고, xfs를 사용 시 문제가 될 수 있다고 하는데 테스트는 해보지 못함
EOT
kubectl apply -f gp3-sc.yaml
# 생성한 gp3 StorageClass 확인
(test_admin@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
gp2 (default) kubernetes.io/aws-ebs Delete WaitForFirstConsumer false 5h5m
gp3 ebs.csi.aws.com Delete WaitForFirstConsumer true 22m
(test_admin@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl describe sc gp3 | grep Parameters
Parameters: allowAutoIOPSPerGBIncrease=true,encrypted=true,type=gp3
Mysql과 Wordpress 배포
Mysql 배포를 위한 리소스 파일 생성
cat <<EOF >./mysql-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: gp3 # 새로 생성한 gp3 StorageClass 적용
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
EOF
Wordpress 배포를 위한 리소스 파일 생성
cat <<EOF >./wordpress-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: gp3 # 새로 생성한 gp3 StorageClass 적용
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
EOF
Mysql과 Wordpress 배포
# Kustomize를 사용
# secretGenerator를 통해 민감정보 관리
cat <<EOF >./kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
secretGenerator:
- name: mysql-pass
literals:
- password=<임의의 패스워드 입력>
EOF
# 배포할 Mysql과 Wordpress 배포 파일을 Kustomize에 추가
# '>>' 이므로 덧붙이기 redirection
cat <<EOF >>./kustomization.yaml
resources:
- mysql-deployment.yaml
- wordpress-deployment.yaml
EOF
# 배포 수행
(test_admin@myeks:default) [root@myeks-bastion-EC2 wordpress]# kubectl apply -k ./
secret/mysql-pass-5f7k9h9g86 created
service/wordpress created
service/wordpress-mysql created
persistentvolumeclaim/mysql-pv-claim created
persistentvolumeclaim/wp-pv-claim created
deployment.apps/wordpress created
deployment.apps/wordpress-mysql created
# 배포된 내용 확인
(test_admin@myeks:default) [root@myeks-bastion-EC2 wordpress]# kubectl get pv,pvc,pod
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-47b8e60e-e546-49e6-a8bb-21a8e3c288b8 20Gi RWO Delete Bound default/wp-pv-claim gp3 81s
persistentvolume/pvc-c5601c95-753d-472f-b5c7-55499ce07796 20Gi RWO Delete Bound default/mysql-pv-claim gp3 81s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/mysql-pv-claim Bound pvc-c5601c95-753d-472f-b5c7-55499ce07796 20Gi RWO gp3 84s
persistentvolumeclaim/wp-pv-claim Bound pvc-47b8e60e-e546-49e6-a8bb-21a8e3c288b8 20Gi RWO gp3 84s
NAME READY STATUS RESTARTS AGE
pod/wordpress-67bdb54bbf-hfpxr 1/1 Running 0 84s
pod/wordpress-mysql-7c94464769-7qcdm 1/1 Running 0 84s
# 접속할 서비스 주소 확인
k(test_admin@myeks:default) [root@myeks-bastion-EC2 wordpress]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 5h15m
wordpress LoadBalancer 10.100.214.86 a7bf0c0c5a8fe46c3a347f3276f8fce5-514849294.ap-northeast-2.elb.amazonaws.com 80:30121/TCP 102s
wordpress-mysql ClusterIP None <none> 3306/TCP 102s
Kustomize 참고자료 : https://velog.io/@pullee/Kustomize로-K8S-리소스-관리하기
해당 서비스 주소로 접속 확인
단순 테스트이므로 Wordpress 설치나 구성은 진행하지 않는다.
참고자료
- Amazon EKS에서 영구 스토리지를 사용하려면 어떻게 해야 하나요? : https://repost.aws/ko/knowledge-center/eks-persistent-storage
- Kubernetes를 위한 영구 스토리지 적용하기: https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/
- 쿠버네티스 볼륨(Volume) 개념 정리 : https://blog.eunsukim.me/posts/kubernetes-volume-overview
- Kustomize로 K8S 리소스 관리하기 : https://velog.io/@pullee/Kustomize로-K8S-리소스-관리하기
- Deploying WordPress and MySQL with Persistent Volumes : https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/
- Amazon EFS 지능형 계층화를 사용하여 Amazon EKS에서 WordPress 실행 : https://aws.amazon.com/ko/blogs/storage/running-wordpress-on-amazon-eks-with-amazon-efs-intelligent-tiering/