EKS 스터디 3주차

Dongmin Han·2023년 5월 13일
1

EKS-STUDY

목록 보기
3/7
post-thumbnail

요약

이번주에는 스토리지와 관련된 부분을 배웠습니다. 순서는 EKS 배포 환경, LB, External DNS 등 2주차 내용에 이어 추가적으로 진행하는 부분, 스토리지에 대한 이해와 각 방식에 대한 실습을 진행한 뒤 EBS, EFS 를 적용해보는 것으로 마무리됩니다.

환경 배포

환경배포파일이 저번주와 달라졌습니다. 기존에 것에서 저번주차와 이번주차 진행할 내용들이 추가되었습니다.
앞으로 실습내용을 작업용 EC2에서도 확인하기 위해 EFS를 마운트합니다.

mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport ***<자신의 EFS FS ID>***.efs.ap-northeast-2.amazonaws.com:/ /mnt/myefs

kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io [원하는 이름]@myeks
실습환경에서 AWS LB/ExternalDNS, kube-ops-view를 추가적으로 설치합니다. 설치과정은 아래에서 확인할 수 있습니다.

배포설정

먼저, 기본적인 환경세팅을 진행합니다. 아까 위에서 나왔던 context 변경부터 진행합니다.

context 변경

# context 변경 
(EKS-study@myeks:N/A) [root@myeks-bastion-EC2 ~] # kubectl config rename-context EKS-study@myeks.ap-northeast-2.eksctl.io $NICK@myeks
Context "EKS-study@myeks.ap-northeast-2.eksctl.io" renamed to "kane@myeks".
(kane@myeks:N/A) [root@myeks-bastion-EC2 ~] #

storage class 확인

kubectl get sc gp2 -o yaml | yh
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"name":"gp2"},"parameters":{"fsType":"ext4","type":"gp2"},"provisioner":"kubernetes.io/aws-ebs","volumeBindingMode":"WaitForFirstConsumer"}
    storageclass.kubernetes.io/is-default-class: "true"
  creationTimestamp: "2023-05-07T14:02:40Z"
  name: gp2
  resourceVersion: "266"
  uid: b8040117-dce8-4173-b8d4-2a77d7a8124f
parameters:
  fsType: ext4
  type: gp2
provisioner: kubernetes.io/aws-ebs
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

이제 eks의 모든 노드들이 csinode인지 확인합니다.
csinodes
"kubectl get csinodes" 명령은 Kubernetes 클러스터에서 사용 가능한 모든 CSINode(CSI Node)을 나열합니다. CSINode은 Container Storage Interface(CSI) 스펙을 준수하는 노드입니다. CSI 스펙은 Kubernetes 클러스터에서 다양한 스토리지 시스템과의 통합을 위한 표준 인터페이스를 제공합니다.

$k get csinodes
NAME                                               DRIVERS   AGE
ip-192-168-1-137.ap-northeast-2.compute.internal   0         26m
ip-192-168-2-116.ap-northeast-2.compute.internal   0         26m
ip-192-168-3-151.ap-northeast-2.compute.internal   0         26m 

--label-columns 옵션을 통해 eks node를 조회한 모습입니다.

$kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
NAME                                               STATUS   ROLES    AGE   VERSION                INSTANCE-TYPE   CAPACITYTYPE   ZONE
ip-192-168-1-137.ap-northeast-2.compute.internal   Ready    <none>   29m   v1.24.11-eks-a59e1f0   t3.medium       ON_DEMAND      ap-northeast-2a
ip-192-168-2-116.ap-northeast-2.compute.internal   Ready    <none>   29m   v1.24.11-eks-a59e1f0   t3.medium       ON_DEMAND      ap-northeast-2b
ip-192-168-3-151.ap-northeast-2.compute.internal   Ready    <none>   29m   v1.24.11-eks-a59e1f0   t3.medium       ON_DEMAND      ap-northeast-2c

$eksctl get iamidentitymapping --cluster myeks
ARN											USERNAME				GROUPS					ACCOUNT
arn:aws:iam::871103481195:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-WBR1ZJTQNHTR	system:node:{{EC2PrivateDNSName}}	system:bootstrappers,system:nodes

2주차 추가진행

보안그룹 추가

"--protocol '-1'"은 모든 프로토콜을 허용하도록 설정하는 것을 의미합니다. 이 플래그를 사용하여 TCP, UDP 및 ICMP를 비롯한 모든 프로토콜에 대한 트래픽을 허용할 수 있습니다.

"--cidr"은 허용할 IP 대역을 지정합니다. 여기서는 "192.168.1.100/32"라는 단일 IP 주소를 허용하도록 설정하였습니다.

$aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32
{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-0bb4b08586cfb50c5",
            "GroupId": "sg-0c78cd151603fbd80",
            "GroupOwnerId": "871103481195",
            "IsEgress": false,
            "IpProtocol": "-1",
            "FromPort": -1,
            "ToPort": -1,
            "CidrIpv4": "192.168.1.100/32"
        }
    ]
}

aws-load-balancer-controller 설치한다.

$helm repo add eks https://aws.github.io/eks-charts
"eks" has been added to your repositories

$helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "eks" chart repository
Update Complete. ⎈Happy Helming!$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

NAME: aws-load-balancer-controller
LAST DEPLOYED: Sun May  7 23:47:07 2023
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
AWS Load Balancer controller installed!

External DNS 적용 : 개인 도메인은 사전에 준비필요!

$echo $MyDomain, $MyDnzHostedZoneId
kaneawsdns.com, /hostedzone/Z06702063E7RRITLLMJRM
#사전에 관련 매니패스트 파일을 준비해주셨다.

$curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml

# External DNS 배포
$MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

위의 envsubst 명령어에 대한 간단한 설명

envsubst : 현재 환경변수를 수레에서 사용가능한 변수로 치한 → 앞의 MyDomain, MyDnzHostedZoneId를 알맞게 externaldns.yaml 파일에 적용킨 후 해당 yaml 파일을 kubectl apply 합니다.

매니패스트 파일은 service account, cluster role, cluster role binding, deployment, service, configmap 등이 포함되어 있습니다. 토글에는 자세한 내용이 담겨있습니다. 자세한 내용은 링크를 통해서 확인할 수 있습니다.

이제 UI 툴인 kube-ops-view를 설치해보겠습니다.

kubeops 설치

#repo 추가 
$helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
"geek-cookbook" has been added to your repositories
#kube-ops-view 설치
$helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system
NAME: kube-ops-view
LAST DEPLOYED: Sun May  7 23:53:13 2023
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace kube-system -l "app.kubernetes.io/name=kube-ops-view,app.kubernetes.io/instance=kube-ops-view" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:8080

#kube-ops-view 서비스의 타입을 "loadBalancer"로 변경하는 모습, patch는 수정할 때 쓰는 명령어
#`-p` 옵션은 json 형태로 수정할 때
$kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
service/kube-ops-view patched
#서비스에 대한 외부 도메인을 주석으로 달아줌(annotate 주석)
$kubectl annotate service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=kubeopsview.$MyDomain"
service/kube-ops-view annotated
$echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=1.5"
Kube Ops View URL = http://kubeopsview.kaneawsdns.com:8080/#scale=1.5

아래는 kubeops 실제 화면입니다. 큰 클러스터안에 노드별로 구분되어있는 것을 확인할 수 있습니다.

  • 파드의 개수를 늘린 모습, 가져다가 되면 구체적으로 정보가 나옵니다.

위의 DEFAULT로 나와있는 부분은 출력형식을 지정하는 것입니다.
파드는 네임스페이스에 따라 구분됩니다.

스토리지

이제 이번주차의 핵심인 스터리지에 대해서 실습을 진행합니다. 파드 내부의 데이터는 파드와 생명주기가 동일하여 파드가 삭제되면 데이터도 사라집니다. 만약, DB 애플리케이션과 같은 경우 데이터는 보존이 필요합니다. 이럴 경우 persistent volume 을 연결하여 사용합니다.

PV와 PVC에 대한 내용은 AWS Blog 문서를 참고했습니다.

Persistent Volume(PV)은 실제 스토리지 볼륨을 나타냅니다. 쿠버네티스는 PV 위에 추가 추상화 계층인 PersistentVolumeClaim(PVC)을 가지고 있습니다. 구분하는 이유는 아래의 문서에서 확인할 수 있습니다.

PV와 PVC의 구분은 Kubernetes 환경에서 두 가지 유형의 사용자가 있다는 개념과 관련이 있습니다.

  • Kubernetes 관리자: 이 사용자는 클러스터를 유지 관리하고 운영하며 영구 스토리지와 같은 계산 리소스를 추가합니다.
  • Kubernetes 애플리케이션 개발자: 이 사용자는 애플리케이션을 개발하고 배포합니다.

CSI는 Container Storage Interface로 다양한 스토리지 솔류션에 대한 인터페이스입니다. CSI를 통해 다음과 같은 2가지 방법으로 스토리지를 연결할 수 있습니다.

정적 프로비저닝(Static provisioning)
먼저 관리자가 하나 이상의 PV를 생성하고 애플리케이션 개발자는 PVC를 생성합니다.
동적 프로비저닝(Dynamic provisioning)
동적 프로비저닝을 사용하면 PV객체를 생성할 필요가 없습니다. 대신에, PVC를 생성할 때 내부적으로 자동으로 생성됩니다. Kubernetes는 Storage Class라는 다른 객체를 사용하여 이를 수행합니다.

  • volume 방식

방식은 위의 그림과 같이 3가지가 있습니다. 첫번째 방식은 파드의 생명주기와 데이터의 생명주기가 같습니다. 데이터를 보존해야할 경우 2~3번째 방식을 이용해야 합니다. 2번째 방식은 아래의 local-path에서 설명할 예정입니다.

emptyDir

먼저, 가시다님의 이미지를 통해 파드의 생명주기와 데이터의 보존주기가 같은 것을 확인한 결과입니다.

$curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/date-busybox-pod.yaml
# 10초 마다 /home/pod-out.txt 에 로그를 남기는 파드 생성
$cat date-busybox-pod.yaml | yh
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 get pod
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          11s
kubectl exec busybox -- tail -f /home/pod-out.txt
Sun May  7 15:26:19 UTC 2023
Sun May  7 15:26:29 UTC 2023
^C
$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
Sun May  7 15:26:45 UTC 2023
Sun May  7 15:26:55 UTC 2023
Sun May  7 15:27:05 UTC 2023
Sun May  7 15:27:15 UTC 2023

$kubectl delete pod busybox
pod "busybox" deleted

Local

HostPath는 Kubernetes 노드의 호스트 파일 시스템에 볼륨을 프로비저닝하는 방법입니다. 이것은 가장 간단한 방법이지만 제한적입니다. hostPath 볼륨은 단일 노드에서만 사용할 수 있으며 노드가 중단되면 데이터가 손실됩니다.
Local Path는 노드의 로컬 파일 시스템에 볼륨을 프로비저닝하는 방법입니다. HostPath와 유사하지만 더 유연합니다. 프로비저닝 볼륨은 여러 노드에서 사용할 수 있으며 노드가 중단되어도 데이터가 손실되지 않습니다. local-path에 대한 자세한 내용은 GitHub에서 확인할 수 있습니다.
아래는 Local-Path 방식을 이용하는 PV,PVC에 대한 실습입니다.
local path 관련 백업은 한승호님이 Velero blog에 올려주셨다.

# strage class 다운로드
$curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

# 배포
$kubectl apply -f local-path-storage.yaml
namespace/local-path-storage created
serviceaccount/local-path-provisioner-service-account created
clusterrole.rbac.authorization.k8s.io/local-path-provisioner-role created
clusterrolebinding.rbac.authorization.k8s.io/local-path-provisioner-bind created
deployment.apps/local-path-provisioner created
storageclass.storage.k8s.io/local-path created
configmap/local-path-config created

$kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path      rancher.io/local-path   Delete          WaitForFirstConsumer   false                  25s

Local Path를 통한 동적 프로비저닝에 대한 실습내용입니다.

$cat localpath1.yaml | yh
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: localpath-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: "local-path"

#PVC 생성
$kubectl apply -f localpath1.yaml
persistentvolumeclaim/localpath-claim created
$kubectl get pvc
NAME              STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
localpath-claim   Pending                                      local-path     4s

# [data -u]로그를 남기는 파드 생성
$kubectl apply -f localpath2.yaml
pod/app created

# 자동으로 PV가 생성되는 모습
$kubectl get pod,pv,pvc
NAME      READY   STATUS              RESTARTS   AGE
pod/app   0/1     ContainerCreating   0          6s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   REASON   AGE
persistentvolume/pvc-88293146-ce9b-4dd0-b473-1ceb489301f3   1Gi        RWO            Delete           Bound    default/localpath-claim   local-path              2s

NAME                                    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/localpath-claim   Bound    pvc-88293146-ce9b-4dd0-b473-1ceb489301f3   1Gi        RWO            local-path     48s

위의 내용을 통해 동적프로비저닝을 확인했습니다. 현재와 같은 방식은 노드의 볼륨을 사용하기에, PV는 파드가 존재하는 노드에 생성됩니다.

$kubectl describe pv    # Node Affinity 확인 -> 자신의 워커노드에서만 생성하도록
Name:              pvc-88293146-ce9b-4dd0-b473-1ceb489301f3
...
Node Affinity:
  Required Terms:
    Term 0:        kubernetes.io/hostname in [ip-192-168-2-116.ap-northeast-2.compute.internal]
...

# 파드가 동작중인 노드 확인
$k get po -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE                                               NOMINATED NODE   READINESS GATES
app    1/1     Running   0          42s   192.168.2.185   ip-192-168-2-116.ap-northeast-2.compute.internal   <none>           <none>

# 접속해서 파드가 남긴 로그 확인
ssh ec2-user@$N2 tree /opt/local-path-provisioner
/opt/local-path-provisioner
└── pvc-88293146-ce9b-4dd0-b473-1ceb489301f3_default_localpath-claim
    └── out.txt

ssh ec2-user@$N2 tail -f /opt/local-path-provisioner/pvc-88293146-ce9b-4dd0-b473-1ceb489301f3_default_localpath-claim/out.txt
Sun May 7 15:30:47 UTC 2023
Sun May 7 15:30:52 UTC 2023
Sun May 7 15:30:57 UTC 2023
Sun May 7 15:31:02 UTC 2023
Sun May 7 15:31:07 UTC 2023
Sun May 7 15:31:12 UTC 2023

# 파드 삭제 
$kubectl delete pod app
pod "app" deleted

# 파드가 삭제되어도 데이터는 유지됨
$ssh ec2-user@$N2 tree /opt/local-path-provisioner
/opt/local-path-provisioner
└── pvc-88293146-ce9b-4dd0-b473-1ceb489301f3_default_localpath-claim
    └── out.txt

1 directory, 1 file

# 파드를 재생성하여 데이터 보존을 한번 더 확인
$kubectl apply -f localpath2.yaml
pod/app created
$kubectl exec -it app -- head /data/out.txt
...
Sun May 7 15:30:02 UTC 2023
Sun May 7 15:30:07 UTC 2023

# 이전과는 다르게 로그가 그대로 유지됨
# 중간에 시간이 짤리는 것은 파드가 삭제 후 재시작하는 사이 시간
$kubectl exec -it app -- tail -f /data/out.txt
..
Sun May 7 15:31:27 UTC 2023
Sun May 7 15:31:32 UTC 2023
Sun May 7 15:31:37 UTC 2023
**Sun May 7 15:31:42 UTC 2023
Sun May 7 15:32:01 UTC 2023**

# 자원을 모두 제거
$kubectl delete pod app
pod "app" deleted

$kubectl delete pvc localpath-claim
persistentvolumeclaim "localpath-claim" deleted

# pv 삭제 후, 관련 볼륨이 사라진 것을 확인할 수 있음.
$ssh ec2-user@$N2 tree /opt/local-path-provisioner
/opt/local-path-provisioner

0 directories, 0 files

AWS EBS Controller

EBS-CSI를 사용하기 위한 절차입니다.
EBS는 동적 프로비저닝이 가능하기에, PVC 와 파드를 생성하면 자동적으로 PV가 할당됩니다.

  1. ISRA 설정 : AWS관리형 정책 AmazonEBSCSIDriverPolicy 사용

  2. add-on 추가: eksctl 이용

    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-driver 설정과정

# 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
2023-05-11 09:37:52 []  building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"
2023-05-11 09:37:53 []  deploying stack "eksctl-myeks-addon-iamserviceaccount-kube-system-ebs-csi-controller-sa"

$eksctl get iamserviceaccount --cluster myeks
NAMESPACE	NAME				ROLE ARN
kube-system	aws-load-balancer-controller	arn:aws:iam::871103481195:role/eksctl-myeks-addon-iamserviceaccount-kube-sy-Role1-RK7A5DTQNNSW
kube-system	ebs-csi-controller-sa		arn:aws:iam::871103481195:role/AmazonEKS_EBS_CSI_DriverRole

# eksctl을 이용해서 ebs-csi-driver 를 설치
$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
2023-05-11 09:41:40 []  Kubernetes version "1.24" in use by cluster "myeks"
2023-05-11 09:41:40 []  using provided ServiceAccountRoleARN "arn:aws:iam::871103481195:role/AmazonEKS_EBS_CSI_DriverRole"
2023-05-11 09:41:40 []  creating addon

$eksctl get addon --cluster ${CLUSTER_NAME}
NAME			VERSION			STATUS		ISSUES	IAMROLE								UPDATE AVAILABLE	CONFIGURATION VALUES
aws-ebs-csi-driver	v1.18.0-eksbuild.1	CREATING	0	arn:aws:iam::871103481195:role/AmazonEKS_EBS_CSI_DriverRole

$k get sa -n kube-system | grep ebs
ebs-csi-controller-sa                0         33s
ebs-csi-node-sa                      0         33s

# 스토리지 클래스 배포
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'
> EOT

$kubectl apply -f gp3-sc.yaml
storageclass.storage.k8s.io/gp3 created

$kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  27m
gp3             ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   5s

$kubectl describe sc gp3 | grep Parameters
Parameters:            allowAutoIOPSPerGBIncrease=true,encrypted=true,type=gp3
$kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  28m
gp3             ebs.csi.aws.com         Delete          WaitForFirstConsumer   true       

PV와 PVC, 파드를 생성하여 동적 프로비저닝을 확인하는 과정입니다.

$cat <<EOT > awsebs-pvc.yaml
> apiVersion: v1
> kind: PersistentVolumeClaim
> metadata:
>   name: ebs-claim
> spec:
>   accessModes:
>     - ReadWriteOnce #### EBS는 여러 노드와 동시에 연결이 가능하기에, Once로 세팅
>   resources:
>     requests:
>       storage: 4Gi
>   storageClassName: gp3
> EOT

$kubectl apply -f awsebs-pvc.yaml
persistentvolumeclaim/ebs-claim created

$kubectl get pvc,pv
NAME                              STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/ebs-claim   Pending                                      gp3            2s
$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
pod/app created
$kubectl get pvc,pv,pod
NAME                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/ebs-claim   Bound    pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7   4Gi        RWO            gp3            34s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
persistentvolume/pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7   4Gi        RWO            Delete           Bound    default/ebs-claim   gp3                     12s

NAME      READY   STATUS              RESTARTS   AGE
pod/app   0/1     ContainerCreating   0          16s

# 추가된 EBS Volume(PV) 확인
$kubectl get VolumeAttachment
NAME                                                                   ATTACHER          PV                                         NODE                                              ATTACHED   AGE
csi-58bd6537e51d9961539e7d5eda35c6d08ab98b890c83e527325b84f7d562eba3   ebs.csi.aws.com   pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7   ip-192-168-2-93.ap-northeast-2.compute.internal   true       14s

# 추가된 EBS Volume(PV) 확인
$aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq

{
  "Volumes": [
    {
      "Attachments": [
        {
          "AttachTime": "2023-05-11T00:48:04+00:00",
          "Device": "/dev/xvdaa",
          "InstanceId": "i-0039e023911746fbf",
          "State": "attached",
          "VolumeId": "vol-02735906e67899e1b",
          "DeleteOnTermination": false
        }
      ],
      "AvailabilityZone": "ap-northeast-2b",
      "CreateTime": "2023-05-11T00:48:00.023000+00:00",
      "Encrypted": true,
      "KmsKeyId": "arn:aws:kms:ap-northeast-2:871103481195:key/e92fb931-6368-48c5-9896-43acdaa5f663",
      "Size": 4,
      "SnapshotId": "",
      "State": "in-use",
      "VolumeId": "vol-02735906e67899e1b",
      "Iops": 3000,
      "Tags": [
        {
          "Key": "kubernetes.io/created-for/pvc/namespace",
          "Value": "default"
        },
        {
          "Key": "kubernetes.io/created-for/pvc/name",
          "Value": "ebs-claim"
        },
        {
          "Key": "Name",
          "Value": "myeks-dynamic-pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7"
        },
        {
          "Key": "KubernetesCluster",
          "Value": "myeks"
        },
        {
          "Key": "CSIVolumeName",
          "Value": "pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7"
        },
        {
          "Key": "kubernetes.io/cluster/myeks",
          "Value": "owned"
        },
        {
          "Key": "kubernetes.io/created-for/pv/name",
          "Value": "pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7"
        },
        {
          "Key": "ebs.csi.aws.com/cluster",
          "Value": "true"
        }
      ],
      "VolumeType": "gp3",
      "MultiAttachEnabled": false,
      "Throughput": 125
    }
  ]
}

PersistentVolumeClaim을 생성할 때, AWS EBS는 다중연결이 가능하기에 access모드를 ReadWriteOnce로 설정해야 합니다. EBS의 다중 연결은 GlusterFS와 같은 공유 볼륨을 사용하는 경우와 유사합니다.

EBS의 다중 연결을 사용하면 특정 볼륨을 여러 대의 EC2 인스턴스에 연결할 수 있습니다. 각 EBS 볼륨은 Multi-Attach 설정시 해당 볼륨이 위치한 가용영역에서 최대 16개의 EC2에 연결할 수 있게 됩니다.

또한, EBS는 같은 AZ에 있어야 접근이 가능하므로 nodeaffinity를 통해 같은 AZ로 PV를 생성합니다.

아래는 EBS 볼륨을 확장하는 실습입니다.

$kubectl exec app -- tail -f /data/out.txt
Thu May 11 00:48:40 UTC 2023
Thu May 11 00:48:45 UTC 2023
Thu May 11 00:48:50 UTC 2023
Thu May 11 00:48:55 UTC 2023
Thu May 11 00:49:00 UTC 2023
Thu May 11 00:49:05 UTC 2023
Thu May 11 00:49:10 UTC 2023
Thu May 11 00:49:15 UTC 2023
Thu May 11 00:49:20 UTC 2023
Thu May 11 00:49:25 UTC 2023
Thu May 11 00:49:30 UTC 2023

$kubectl df-pv
 PV NAME                                   PVC NAME   NAMESPACE  NODE NAME                                        POD NAME  VOLUME MOUNT NAME   SIZE  USED  AVAILABLE  %USED  IUSED  IFREE   %IUSED
 pvc-7fb5199c-d3d9-4648-a0d0-5f9b9bc006d7  ebs-claim  default    ip-192-168-2-93.ap-northeast-2.compute.internal  app       persistent-storage  3Gi   28Ki  3Gi        0.00   12     262132  0.00

$kubectl exec -it app -- sh -c 'df -hT --type=overlay'
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   30G  3.6G   27G  12% /

# ebs volume 확장
$kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
persistentvolumeclaim/ebs-claim patched

# 자동으로 확장되는 모습
$kubectl exec -it app -- sh -c 'df -hT --type=ext4'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   ext4  9.8G   28K  9.7G   1% /data
$kubectl delete pod app & kubectl delete pvc ebs-claim
[1] 7288
pod "app" deleted
persistentvolumeclaim "ebs-claim" deleted
[1]+  Done                    kubectl delete pod app
  • EBS controller 에 의해 하나의 PV가 생성되고, 용량이 바뀐 모습

AWS Volume SnapShots Controller

아래는 위와 동일하게, PVC, PV를 생성해서 파드와 연결합니다. 파드는 data -u 로그를 남기며 강제로 파드와 PVC를 삭제한 후, snapshots을 통해 복구하는 실습을 진행합니다.

snapshot 생성

$kubectl apply -f ebs-volume-snapshot.yaml
volumesnapshot.snapshot.storage.k8s.io/ebs-volume-snapshot created

$kubectl get volumesnapshot
NAME                  READYTOUSE   SOURCEPVC   SOURCESNAPSHOTCONTENT   RESTORESIZE   SNAPSHOTCLASS   SNAPSHOTCONTENT                                    CREATIONTIME   AGE
ebs-volume-snapshot   false        ebs-claim                           4Gi           csi-aws-vsc     snapcontent-ff3a9b11-d8a5-4806-80eb-9c062c4062ad   3s             3s

#아래에 나와있는 snapshot id로 확인하면, 모두 잘 설정되어있다.
$kubectl get volumesnapshot ebs-volume-snapshot -o jsonpath={.status.boundVolumeSnapshotContentName} ; echo
snapcontent-ff3a9b11-d8a5-4806-80eb-9c062c4062ad

$kubectl describe volumesnapshot.snapshot.storage.k8s.io ebs-volume-snapshot
Name:         ebs-volume-snapshot
Namespace:    default
...
Metadata:
...
    Time:            2023-05-13T05:58:36Z
  Resource Version:  11187
  UID:               ff3a9b11-d8a5-4806-80eb-9c062c4062ad
Spec:
  Source:
    Persistent Volume Claim Name:  ebs-claim
  Volume Snapshot Class Name:      csi-aws-vsc
Status:
  Bound Volume Snapshot Content Name:  snapcontent-ff3a9b11-d8a5-4806-80eb-9c062c4062ad
  Creation Time:                       2023-05-13T05:58:35Z
  Ready To Use:                        false
  Restore Size:                        4Gi
Events:
  Type    Reason            Age   From                 Message
  ----    ------            ----  ----                 -------
  Normal  CreatingSnapshot  19s   snapshot-controller  Waiting for a snapshot default/ebs-volume-snapshot to be created by the CSI driver.
  Normal  SnapshotCreated   18s   snapshot-controller  Snapshot default/ebs-volume-snapshot was successfully created by the CSI driver.

###
# 블로그에서는 중복된 조회 제거
$kubectl get volumesnapshotcontents
NAME                                               READYTOUSE   RESTORESIZE   DELETIONPOLICY   DRIVER            VOLUMESNAPSHOTCLASS   VOLUMESNAPSHOT        VOLUMESNAPSHOTNAMESPACE   AGE
snapcontent-ff3a9b11-d8a5-4806-80eb-9c062c4062ad   false        4294967296    Delete           ebs.csi.aws.com   csi-aws-vsc           ebs-volume-snapshot   default                   38s

# 파드와 pvc를 제거하여, pv가 제거된 모습
$kubectl delete pod app && kubectl delete pvc ebs-claim
pod "app" deleted
persistentvolumeclaim "ebs-claim" deleted

$kubectl get pvc,pv
No resources found

$k get volumesnapshot
NAME                  READYTOUSE   SOURCEPVC   SOURCESNAPSHOTCONTENT   RESTORESIZE   SNAPSHOTCLASS   SNAPSHOTCONTENT                                    CREATIONTIME   AGE
ebs-volume-snapshot   true         ebs-claim                           4Gi           csi-aws-vsc     snapcontent-ff3a9b11-d8a5-4806-80eb-9c062c4062ad   7m13s          7m13s

$cat <<EOT > 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
> EOT

$kubectl apply -f ebs-snapshot-restored-claim.yaml
persistentvolumeclaim/ebs-snapshot-restored-claim created

$kubectl get pvc,pv
NAME                                                STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/ebs-snapshot-restored-claim   Pending                                      gp3            3s

## 블로그에서는 curl 또한 제거
$curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ebs-snapshot-restored-pod.yaml
$cat ebs-snapshot-restored-pod.yaml | yh
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
pod/app created

$k get po
NAME   READY   STATUS    RESTARTS   AGE
app    1/1     Running   0          13s

#로그가 이어지는 것을 확인할 수 있다.
$k exec app -- cat /data/out.txt
...
Sat May 13 05:58:23 UTC 2023
Sat May 13 05:58:28 UTC 2023
Sat May 13 06:07:12 UTC 2023
Sat May 13 06:07:17 UTC 2023

$kubectl delete pod app && kubectl delete pvc ebs-snapshot-restored-claim && kubectl delete volumesnapshots ebs-volume-snapshot
pod "app" deleted
persistentvolumeclaim "ebs-snapshot-restored-claim" deleted
volumesnapshot.snapshot.storage.k8s.io "ebs-volume-snapshot" deleted

아래는 스냅샷을 AWS 콘솔을 통해 확인한 모습이다.

AWS EFS Controller

먼저 EFS(Amazon Elastic File System)는 그림과 같이 여러 가용영역과 호환가능한 파일시스템입니다.

아래는 EFS를 통해 PV,PVC를 생성하는 실습내용입니다.

먼저 작업용 EC2에도 efs mount를 진행합니다.

$mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-0b2bc433bc72b5727.efs.ap-northeast-2.amazonaws.com:/ /mnt/myefs
$df -hT --type nfs4
Filesystem                                              Type  Size  Used Avail Use% Mounted on
fs-0b2bc433bc72b5727.efs.ap-northeast-2.amazonaws.com:/ nfs4  8.0E     0  8.0E   0% /mnt/myefs

EFS-CSI 설치과정

# 정보 확인
$aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output textfs-0f70f92a10cc5af1a
# json 다운로드
$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
{
    "Policy": {
        "PolicyName": "AmazonEKS_EFS_CSI_Driver_Policy",
        "PolicyId": "ANPA4VUOQIVV6AGY3ABHQ",
        "Arn": "arn:aws:iam::871103481195:policy/AmazonEKS_EFS_CSI_Driver_Policy",
        "Path": "/",
        "DefaultVersionId": "v1",
        "AttachmentCount": 0,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2023-05-13T06:21:41+00:00",
        "UpdateDate": "2023-05-13T06:21:41+00:00"
    }
}
# efs-csi 생성
$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
2023-05-13 15:22:26 []  created serviceaccount "kube-system/efs-csi-controller-sa"

## 불필요
$kubectl get sa -n kube-system efs-csi-controller-sa -o yaml | head -5
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::871103481195:role/eksctl-myeks-addon-iamserviceaccount-kube-sy-Role1-183ANMNPO7E68

$eksctl get iamserviceaccount --cluster myeks
NAMESPACE	NAME				ROLE ARN
kube-system	aws-load-balancer-controller	arn:aws:iam::871103481195:role/eksctl-myeks-addon-iamserviceaccount-kube-sy-Role1-1OWZIVWLLX9JM
kube-system	ebs-csi-controller-sa		arn:aws:iam::871103481195:role/AmazonEKS_EBS_CSI_DriverRole
kube-system	efs-csi-controller-sa		arn:aws:iam::871103481195:role/eksctl-myeks-addon-iamserviceaccount-kube-sy-Role1-183ANMNPO7E68

#efs 구성요소를 helm을 통해 배포
$helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
"aws-efs-csi-driver" has been added to your repositories
$helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "aws-efs-csi-driver" chart repository
Update Complete. ⎈Happy Helming!$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
Release "aws-efs-csi-driver" does not exist. Installing it now.
NAME: aws-efs-csi-driver
LAST DEPLOYED: Sat May 13 16:18:05 2023
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
To verify that aws-efs-csi-driver has started, run:

$helm list -n kube-system
NAME              	NAMESPACE  	REVISION	UPDATED                                	STATUS  	CHART                   	APP VERSION
aws-efs-csi-driver	kube-system	1       	2023-05-13 16:18:05.885009102 +0900 KST	deployed	aws-efs-csi-driver-2.4.3	1.5.5

$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-6hbkc   0/3     ContainerCreating   0          9s
efs-csi-controller-6f64dcc5dc-wf4ks   0/3     ContainerCreating   0          9s
efs-csi-node-qs7sr                    0/3     ContainerCreating   0          9s
efs-csi-node-xtvvm                    0/3     ContainerCreating   0          9s
efs-csi-node-zvzcs                    0/3     ContainerCreating   0          9s

이제 K8S에서 제공해준 샘플 코드를 다운받아, 실습을 진행합니다.

$git clone https://github.com/kubernetes-sigs/aws-efs- qq-driver.git /root/efs-csiCloning into '/root/efs-csi'...
remote: Enumerating objects: 16052, done.
remote: Counting objects: 100% (269/269), done.
remote: Compressing objects: 100% (185/185), done.
remote: Total 16052 (delta 65), reused 236 (delta 57), pack-reused 15783
Receiving objects: 100% (16052/16052), 16.90 MiB | 20.51 MiB/s, done.
Resolving deltas: 100% (7777/7777), done.

$cd /root/efs-csi/examples/kubernetes/multiple_pods/specs && tree
.
├── claim.yaml
├── pod1.yaml
├── pod2.yaml
├── pv.yaml
└── storageclass.yaml
0 directories, 5 files

$cat storageclass.yaml | yh
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com

$kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/efs-sc created

$k get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
efs-sc          efs.csi.aws.com         Delete          Immediate              false                  10s
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  139m
gp3             ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   109m

# 현재 EFS ID를 지정하는 모습
$EfsFsId=$(aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text)
$sed -i "s/fs-4af69aab/$EfsFsId/g" pv.yaml
$echo $EfsFsId
fs-0f70f92a10cc5af1a

$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-0f70f92a10cc5af1a
# 정적 프로비저닝이라 pv를 먼저 생성하는 모습
$kubectl apply -f pv.yaml
persistentvolume/efs-pv created

$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
persistentvolumeclaim/efs-claim created

$k get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
efs-pv   5Gi        RWX            Retain           Bound    default/efs-claim   efs-sc                  57s

# 이전과 유사한 data -u 로그를 찍는 파드를 2개 생성
$kubectl apply -f pod1.yaml,pod2.yaml
pod/app1 created
pod/app2 created

$k get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS        CLAIM               STORAGECLASS   REASON   AGE
efs-pv   5Gi        RWX            Retain           Terminating   default/efs-claim   efs-sc                  3m32s

$cat pod1.yaml pod2.yaml | yh
apiVersion: v1
kind: Pod
metadata:
  name: app1
...
    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
..
    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

$k exec -it app1 -- sh -c "df -hT -t nfs4"
Filesystem           Type            Size      Used Available Use% Mounted on
127.0.0.1:/          nfs4            8.0E         0      8.0E   0% /data
$k exec -it app1 -- cat /data/out1.txt
...
Sat May 13 07:25:50 UTC 2023
Sat May 13 07:25:55 UTC 2023

# 작업용 EC2에도 공유되는 모습.
$tree /mnt/myefs/
/mnt/myefs/
├── out1.txt
└── out2.txt

0 directories, 2 files

$kubectl delete pvc efs-claim && kubectl delete pv efs-pv && kubectl delete sc efs-sc
persistentvolumeclaim "efs-claim" deleted
persistentvolume "efs-pv" deleted
storageclass.storage.k8s.io "efs-sc" deleted

아래는 EFS의 네트워크 모습입니다. 현재 노드가 있는 3개의 가용영역이 모두 있는 것을 확인할 수 있습니다.

위에서는 정적 프로비저닝으로 테스트를 진행했지만, EFS에는 동적 프로비저닝 기능이 1.2 버전부터 지원하여 동적 프로비저닝도 진행해봤다. 위의 내용에서 동적프로비저닝을 테스트하면 다음과 같이 자동으로 PV가 할당되지 않아 PVC와 파드가 생성되지 못한다.

# 모니터링 
Every 2.0s: kubectl get sc efs-sc; echo; kubectl get pv,pvc,pod                                            Sat May 13 16:30:46 2023

NAME     PROVISIONER	   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
efs-sc   efs.csi.aws.com   Delete          Immediate           false                  10m

NAME                              STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/efs-claim   Pending                                      efs-sc         42s

NAME	   READY   STATUS    RESTARTS   AGE
pod/app1   0/1     Pending   0          33s
pod/app2   0/1     Pending   0          33s

파드의 로그

Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  15s (x4 over 100s)  default-scheduler  0/3 nodes are available: 3 pod has unbound immediate PersistentVolumeClaims. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling.
  Normal   Scheduled         7s                  default-scheduler  Successfully assigned default/app1 to ip-192-168-2-52.ap-northeast-2.compute.internal

PVC 로그

Events:
  Type     Reason                Age                  From                                                                                                   Message
  ----     ------                ----                 ----                                                                                                   -------
  Normal   Provisioning          96s (x7 over 2m39s)  efs.csi.aws.com_ip-192-168-3-190.ap-northeast-2.compute.internal_d0fa0b24-c916-44ba-befc-922b6eb49470  External provisioner is provisioning volume for claim "default/efs-claim"
  Warning  ProvisioningFailed    96s (x7 over 2m39s)  efs.csi.aws.com_ip-192-168-3-190.ap-northeast-2.compute.internal_d0fa0b24-c916-44ba-befc-922b6eb49470  failed to provision volume with StorageClass "efs-sc": rpc error: code = InvalidArgument desc = Missing provisioningMode parameter
  Normal   Provisioning          96s (x7 over 2m39s)  efs.csi.aws.com_ip-192-168-1-203.ap-northeast-2.compute.internal_74db5f2e-b1e1-4f54-b9b2-cede890dddf7  External provisioner is provisioning volume for claim "default/efs-claim"
  Warning  ProvisioningFailed    96s (x7 over 2m39s)  efs.csi.aws.com_ip-192-168-1-203.ap-northeast-2.compute.internal_74db5f2e-b1e1-4f54-b9b2-cede890dddf7  failed to provision volume with StorageClass "efs-sc": rpc error: code = InvalidArgument desc = Missing provisioningMode parameter
  Normal   ExternalProvisioning  80s (x8 over 2m39s)  persistentvolume-controller                                                                            waiting for a volume to be created, either by external provisioner "efs.csi.aws.com" or manually created by system administrator

EFS 동적 프로비저닝

EFS에 대한 동적프로비저닝에 대한 설명은 AWS BlogGitHub에서 자세하게 확인할 수 있습니다.

다른 과정은 앞에서와 똑같고, 스토리지 클래스와 파드만 달라진다.

cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  fileSystemId: fs-0b2bc433bc72b5727 # 자신의 EFS ID
  directoryPerms: "700"
cat pod.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  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

이유를 모르겠으나, 동적프로비저닝은 오류가 발생했다. 문서 그대로 진행하고, 오류를 찾아봤으나 아직 해결하지 못했다.

Every 2.0s: kubectl get sc efs-sc; echo; kubectl get pv,pvc,pod                                            Sat May 13 22:45:27 2023

NAME     PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
efs-sc   efs.csi.aws.com   Delete          Immediate           false                  7m39s

NAME                              STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/efs-claim   Pending                                      efs-sc         7m18s

NAME          READY   STATUS    RESTARTS   AGE
pod/efs-app   0/1     Pending   0          7m18s

PVC에서 발생한 로그

Warning  ProvisioningFailed  26s                efs.csi.aws.com_ip-192-168-3-97.ap-northeast-2.compute.internal_6f23dce4-74d1-4453-b25c-013fcb47be73  failed to provision volume with StorageClass "efs-sc": rpc error: code = Internal desc = Failed to fetch File System info: Describe File System failed: WebIdentityErr: failed to retrieve credentials
caused by: AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity
          status code: 403, request id: 92132222-c11b-4a7c-b546-ddf80b933c1c
  Normal  ExternalProvisioning  14s (x5 over 57s)  persistentvolume-controller  waiting for a volume to be created, either by external provisioner "efs.csi.aws.com" or manually created by system administrator

기타

추가적으로 실습을 진행하다가, 에러가 났던 부분입니다.
아래와 같이 PV 삭제할 때, terminating 단계에서 멈추며 진행이 안된 적이 있다. 관련 자료를 찾아보니 파이널라이저에 의해 삭제가 안되는 것을 알 수 있었다.

k get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS        CLAIM               STORAGECLASS   REASON   AGE
efs-pv                                     5Gi        RWX            Retain           Available                         efs-sc                  3m49s
pvc-5f762d6c-aff6-4a3a-8ff4-e947563c792a   5Gi        RWX            Delete           Terminating   default/efs-claim   efs-sc                  27m
profile
초보 데브옵스 엔지니어, 피드백은 언제나 환영입니다.

0개의 댓글