..

aews) ebs csi 체험기

3주차 스터디 내용은 ebs controller를 통해 pv와 pvc사용하는 방법을 공부했다. ebs controller 사용을 위해선 csi설치가 필요하다. eks 생성시 만들어지는 스토리지 드라이버를 사용하지 않고, 클러스터 외부에 있는 aws ebs 스토리지를 사용 할 수 있도록 해주는 것이다. 🔗

# default로 있는 sc를 확인해보면 Provisioner가 kubernetes.io/aws-ebs로 되어있다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k describe sc gp2
Name:            gp2
IsDefaultClass:  Yes
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
Provisioner:           kubernetes.io/aws-ebs
Parameters:            fsType=ext4,type=gp2
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     WaitForFirstConsumer

ebs csi 설치

  1. 먼저 ebs를 관리할 수 있도록 csi driver에게 권한이 할당되어야한다. irsa를 사용하여 sa와 iam권한을 연결해준다. aws에서 관리하는 정책을 연결할 것이기때문에 따로 만들지 않아도 된다.
# irsa 설정 (이름은 동일해야한다)
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

# 확인
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# eksctl get iamserviceaccount ebs-csi-controller-sa --cluster myeks
NAMESPACE	NAME			ROLE ARN
kube-system	ebs-csi-controller-sa	arn:aws:iam::643127389001:role/AmazonEKS_EBS_CSI_DriverRole

  1. ebs csi driver addon
eksctl create addon --name aws-ebs-csi-driver --cluster ${CLUSTER_NAME} --service-account-role-arn arn:aws:iam::${ARN}:role/AmazonEKS_EBS_CSI_DriverRole --force

# 배포된 리소스 확인
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl get deploy,ds -l=app.kubernetes.io/name=aws-ebs-csi-driver -n kube-system
NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ebs-csi-controller   2/2     2            2           11m

NAME                                  DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR              AGE
daemonset.apps/ebs-csi-node           3         3         3       3            3           kubernetes.io/os=linux     11m
daemonset.apps/ebs-csi-node-windows   0         0         0       0            0           kubernetes.io/os=windows   11m

# 파드안에 여러 컨테이너가 있는 것을 볼 수 있다. 확인인해보면 provisioner, attacher, snapshotter 등 각 기능들이 컨테이너로 들어가 있다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl get pod -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver
NAME                                  READY   STATUS    RESTARTS   AGE
ebs-csi-controller-67658f895c-7dj8c   6/6     Running   0          25m
ebs-csi-controller-67658f895c-9wjps   6/6     Running   0          25m
ebs-csi-node-7lcnl                    3/3     Running   0          25m
ebs-csi-node-fzn9d                    3/3     Running   0          25m
ebs-csi-node-pxrvt                    3/3     Running   0          25m


# csi가 설치된 노드들도 확인해보자
# spec.drivers아래 보면 설치된 driver와 그 아래, allocatables에서 노드당 최대 연결할 수 있는 볼륨을 (ebs를)확인할 수 있다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl describe csinodes
Name:               ip-192-168-1-58.ap-northeast-2.compute.internal
Labels:             <none>
Annotations:        storage.alpha.kubernetes.io/migrated-plugins:
                      kubernetes.io/aws-ebs,kubernetes.io/azure-disk,kubernetes.io/azure-file,kubernetes.io/cinder,kubernetes.io/gce-pd
CreationTimestamp:  Thu, 11 May 2023 18:05:00 +0900
Spec:
  Drivers:
    ebs.csi.aws.com:
      Node ID:  i-03abd706aae96c603
      Allocatables:
        Count:        25
        # 기본적으로 25개이며, 특정 인스턴스 타입에 따라 39개까지 가능하다.
      Topology Keys:  [topology.ebs.csi.aws.com/zone]
Events:               <none>
...

ebs csi 동작

pv요청이 있을 시, csi-controller(deployment로 배포된)가 aws api를 호출하여 ebs를 생성하고 volume에 attach 한다. csi-node(daemonset으로 배포된)를 통해 volume을 pod에 mount시킨다.

1-0

이미지 출처는 악분님 블로그에서 가져왔다. 🔗

테스트

# 설치된 csi로 provisioner 설정하여 sc 생성
# 기존 기본으로 있는 스토리지 드라이버의 provisioner는 provisioner는 kubernetes.io/aws-ebs
---
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'
---


# 테스트 po,pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-claim
spec:
  accessModes:
    - ReadWriteOnce
    # etx4, xfs같은 fs은 nfs처럼 동시에 연결하여 사용할 수 없기 때문에 RWO으로 설정
  resources:
    requests:
      storage: 4Gi
  storageClassName: gp3
---
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
---


# 동적으로 잘 생성되어 붙은 것을 확인
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl get pvc,pv,pod
NAME                              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/ebs-claim   Bound    pvc-8c30b306-2853-4937-aedd-cb59bad3760b   4Gi        RWO            gp3            4m16s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
persistentvolume/pvc-8c30b306-2853-4937-aedd-cb59bad3760b   4Gi        RWO            Delete           Bound    default/ebs-claim   gp3                     54s

NAME      READY   STATUS    RESTARTS   AGE
pod/app   1/1     Running   0          58s

(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl get VolumeAttachment
NAME                                                                   ATTACHER          PV                                         NODE                                             ATTACHED   AGE
csi-6b459f4ca202aeb1c2e56851103ce56553ebcd5bdeddbf63c15fd4e01f0ac766   ebs.csi.aws.com   pvc-8c30b306-2853-4937-aedd-cb59bad3760b   ip-192-168-3-6.ap-northeast-2.compute.internal   true       52s

콘솔에서도 확인 가능

1-1

이렇게 생성된 ebs가 붙은 노드와 파드가 동일한 존에 있어야 할당이 된다. 기본적으로 노드가 각 존에 배치되지만, 만약 특정존에만 노드를 생성하였거나, 해당 존의 노드에 이미 할당된 수만큼의 볼륨이 붙었다면 새로운 노드가 배포되어야한다. 이를 위해 –balance-similar-node-groups 옵션을 사용하여 활성화 해줘야한다. (해당 옵션을 위해선 cluster autoscaler가 선행되어야한다 (🔗)https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/autoscaling.html) 지금 테스트환경에서는 볼륨을 붙이기에 충분히 여유있으므로, 위 작업 없이 진행하였다.

(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# cat test.yaml
...
apiVersion: v1
kind: Pod
metadata:
  name: test-app
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: failure-domain.beta.kubernetes.io/zone
						# 더이상 해당 key를 사용하지 않는다고 한다.
            # node label을 확인해보면 topology.kubernetes.io/zone 
            operator: In
            values:
            - ap-northeast-2a
  terminationGracePeriodSeconds: 3
  containers:
  - ...
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: test-claim

(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k apply -f test.yaml
persistentvolumeclaim/test-claim created
Warning: spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead
pod/test-app created


(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k get po,pv,pvc
NAME           READY   STATUS    RESTARTS   AGE
pod/app        1/1     Running   0          103m
pod/test-app   1/1     Running   0          10m

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
persistentvolume/pvc-40f668d9-2750-45f4-a5f2-bd50deb3ef6f   4Gi        RWO            Delete           Bound    default/test-claim   gp3                     10m
persistentvolume/pvc-8c30b306-2853-4937-aedd-cb59bad3760b   4Gi        RWO            Delete           Bound    default/ebs-claim    gp3                     103m

NAME                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/ebs-claim    Bound    pvc-8c30b306-2853-4937-aedd-cb59bad3760b   4Gi        RWO            gp3            106m
persistentvolumeclaim/test-claim   Bound    pvc-40f668d9-2750-45f4-a5f2-bd50deb3ef6f   4Gi        RWO            gp3            10m


(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k get volumeattachment
NAME                                                                   ATTACHER          PV                                         NODE                                              ATTACHED   AGE
csi-287da6a2b2414ed393feade04016f839a01e0bc577fb44f5a32891bd92e151b7   ebs.csi.aws.com   pvc-40f668d9-2750-45f4-a5f2-bd50deb3ef6f   ip-192-168-1-58.ap-northeast-2.compute.internal   true       10m
csi-6b459f4ca202aeb1c2e56851103ce56553ebcd5bdeddbf63c15fd4e01f0ac766   ebs.csi.aws.com   pvc-8c30b306-2853-4937-aedd-cb59bad3760b   ip-192-168-3-6.ap-northeast-2.compute.internal    true       103m

콘솔에서 확인 시 a존에 ebs가 생긴 것을 확인 할 수 있다. 1-2

snapshot

snapshot

# ebs controller pod 안을 보면  csi-snapshotter 컨테이너가 동작 중인 것을 알 수 있다.
# 해당 기능을 사용하기 위해서는 먼저 crd 설치가 필요하다

# volume sanpshot crd 설치
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl apply -f snapshot.storage.k8s.io_volumesnapshots.yaml,snapshot.storage.k8s.io_volumesnapshotclasses.yaml,snapshot.storage.k8s.io_volumesnapshotcontents.yaml
customresourcedefinition.apiextensions.k8s.io/volumesnapshots.snapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumesnapshotclasses.snapshot.storage.k8s.io created
customresourcedefinition.apiextensions.k8s.io/volumesnapshotcontents.snapshot.storage.k8s.io created

# rbac 설정과 snapshot controller 설치
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl apply -f rbac-snapshot-controller.yaml,setup-snapshot-controller.yaml

# snapshotclass 설치 
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# curl -s -O https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/master/examples/kubernetes/snapshot/manifests/classes/snapshotclass.yaml
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl apply -f snapshotclass.yaml


# 사용하게될 snapshotclass 
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k get volumesnapshotclass
NAME          DRIVER            DELETIONPOLICY   AGE
csi-aws-vsc   ebs.csi.aws.com   Delete           4m39s

# snapshot을 하려하는 pvc와 위의 snapshotclass를 이용한다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# cat 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

(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k get volumesnapshot
NAME                  READYTOUSE   SOURCEPVC   SOURCESNAPSHOTCONTENT   RESTORESIZE   SNAPSHOTCLASS   SNAPSHOTCONTENT                                    CREATIONTIME   AGE
ebs-volume-snapshot   true         ebs-claim                           4Gi           csi-aws-vsc     snapcontent-c79ef8ed-e0d3-4beb-89a4-32acaf19d552   24s            24s

1-3

restore

(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# cat ebs-snapshot-restore.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
---
apiVersion: v1
kind: Pod
metadata:
  name: restore-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


(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k get po,pvc
NAME              READY   STATUS    RESTARTS   AGE
pod/app           1/1     Running   0          119m
pod/restore-app   1/1     Running   0          20s
pod/test-app      1/1     Running   0          26m

NAME                                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/ebs-claim                     Bound    pvc-8c30b306-2853-4937-aedd-cb59bad3760b   4Gi        RWO            gp3            122m
persistentvolumeclaim/ebs-snapshot-restored-claim   Bound    pvc-b22ea2c5-2926-4b79-8da0-ad285acde7e1   4Gi        RWO            gp3            2m21s
persistentvolumeclaim/test-claim                    Bound    pvc-40f668d9-2750-45f4-a5f2-bd50deb3ef6f   4Gi        RWO            gp3            26m

fstype: xfs

csi로 설치된 controller의 컨테이너 중 csi-provisioner container를 살펴보면 –default-fstype이 ext4로 지정되어있다. 만들어서 사용했던 gp3 sc도 ex4를 기본으로 설정되어 테스트했었는데, xfs으로 한 경우 snapshot이 안될 수도 안될 수도 있다하여 확인차테스트해보았다.

# 먼저 sc를 생성해주었다. 아까와다른 점은 parameters.fsType: xfs 뿐이다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# cat testxfssc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: xfs-gp3
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3
  allowAutoIOPSPerGBIncrease: 'true'
  encrypted: 'true'
  fsType: xfs

(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# k get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
xfs-gp3         ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   19s
...

# 위에서 po,pvc로 배포했던 yaml에서 sc만 수정 후 동일하게 배포했다.
# 생성된 pod에 들어가서 확인해보니 mount가 잘되어있다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl exec -it xfs-test-app  -- sh -c 'df -hT --type=xfs'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   xfs   4.0G   62M  4.0G   2% /data
/dev/nvme0n1p1 xfs    30G  3.6G   27G  12% /etc/hosts

xfs snapshot & restore

# snapshot
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# cat xfs-snapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: xfs-volume-snapshot
spec:
  volumeSnapshotClassName: csi-aws-vsc
  source:
    persistentVolumeClaimName: xfs-test-claim

# restore
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# cat xfs-restore-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: xfs-snapshot-restored-claim
spec:
  storageClassName: xfs-gp3
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  dataSource:
    name: xfs-volume-snapshot
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io

# sc를 잘 붙여주고 복구했다면, 이상없이 pod에 붙는 것을 확일 할 수 있다.
(nyoung@myeks:default) [root@myeks-bastion-EC2 ~]# kubectl exec -it restore-app-xfs -- sh -c 'df -hT --type=xfs'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme2n1   xfs   4.0G   62M  4.0G   2% /data
/dev/nvme0n1p1 xfs    30G  3.7G   27G  13% /etc/hosts

복구된 파일 내용을 확인해보면 snapshot을 찍고 restore되는 시간이 비어있는 것을 볼 수 있다.

1-4


참고