티스토리 뷰

728x90
반응형

개요

Kubernetes가 대세로 자리 잡은 이후 다양한 3rd Party 솔루션과의 연동이슈는 끊임없이 발생하고 있다. Container Orchestrator(CO)와의 단일화된 인터페이스를 제공하기 위해 CRI(Container Runtime Interface), CNI(Container Network Interface), CSI(Container Storage Interface)가 등장하고 발전해 오고 있다.

CSI의 등장이전에는 특정 볼륨에 대한 연결 방식을 Kubernetes가 제공하는 방식으로 구현되었다. 하지만 새로운 볼륨 플러그인이 지속적으로 추가되고, 특히 특정 볼륨을 제공하는 솔루션의 버전 변화에 일일이 대응하기에는 어려움이 있었다. 또한 볼륨 스토리지와의 연결을 위해 SDK 코드가 Kubernetes 바이너리에 추가되어야 했으며, 이는 안정성 및 보안 문제를 야기했으며, 테스트와 관리의 어려움이 발생하였다.

CSI는 Kubernetes와 같은 컨테이너 오케스트레이션 시스템의 컨테이너화된 워크로드에 임의의 블록 및 파일 스토리지 시스템을 노출하기 위한 표준으로 개발되었다. Container Storage Interface의 채택으로 Kubernetes 볼륨 계층은 무한한 확장이 가능한 상태로 발전하였다. 이제 볼륨 확장을 위해서는 Kubernetes와의 직접적인 연계 없이 구현할 수 있으며, 이미 정의된 인터페이스를 통해 통신하며, 이는 CSI 등장 이전에 문제점으로 제시되었던, 관리의 어려움, 안정성의 문제, 보안 문제를 모두 해소할 수 있게 되었다.

이번 포스팅에서 살펴볼 StorageClass는 CSI의 구현체를 가리키는 오브젝트이다. StorageClass를 통해 대표적으로 많이 사용되는 볼륨인 AWS EBS와 NFS 연동 과정에 대해 살펴보도록 하자.


Dynamic Provisioning

CSI는 Container Orchestrator와 다양한 Storage Provider간의 인터페이스를 담당하며, PersistentVolume을 자동으로 생성할 수 있다.

CSI는 아래와 같은 다양한 스토리지 Provider를 지원한다. Kubernetes 내에 이미 Provisioner를 제공하거나, 필요 시 deployment, statefulset 등으로 배포하여 볼륨과 연동한다.

[주요 스토리지의 Privisioner]

  • AWS EBS - provisioner: kubernetes.io/aws-ebs
  • GCE PD - provisioner: kubernetes.io/gce-pd
  • Glusterfs - provisioner: kubernetes.io/glusterfs
  • NFS - provisioner: example.com/external-nfs
  • OpenStack Cinder - provisioner: kubernetes.io/cinder
  • vSphere - provisioner: csi.vsphere.vmware.com / provisioner: kubernetes.io/vsphere-volume
  • Ceph RBD - provisioner: kubernetes.io/rbd
  • Quobyte - provisioner: kubernetes.io/quobyte
  • Azure Disk - provisioner: kubernetes.io/azure-disk
  • Azure File - provisioner: kubernetes.io/azure-file
  • Portworx Volume - provisioner: kubernetes.io/portworx-volume
  • ScaleIO - provisioner: kubernetes.io/scaleio
  • StorageOS - provisioner: kubernetes.io/storageos
  • Local - provisioner: kubernetes.io/no-provisioner

해당 Provisioner를 사용하여 개별 정의된 Parameter를 통해 세부 연결 방식을 정의한다. 세부적인 환경 구성은 아래를 참고한다.

https://kubernetes.io/ko/docs/concepts/storage/storage-classes/

 

스토리지 클래스

이 문서는 쿠버네티스의 스토리지클래스의 개념을 설명한다. 볼륨과 퍼시스턴트 볼륨에 익숙해지는 것을 권장한다. 소개 스토리지클래스는 관리자가 제공하는 스토리지의 "classes"를 설명할 수

kubernetes.io

[동작 순서]

  • 1. PersistentVolumeClaim은 StorageClass를 사용하여 동적 프로비저닝 트리거
  • 2. Dynamic Provisioning은 PersistentVolumeClaim에 의해 트리거
  • 3. 볼륨 프로비저닝이 호출되면 Parameter type의 value와 CreateVolume 호출을 volume provisioner로 전달
  • 4. volume provisioner는 새 볼륨을 생성한 후 생성한 볼륨을 나타내는 PersistentVolume 개체를 자동으로 생성
  • 5. Kubernetes는 새로운 PersistentVolume 개체를 PersistentVolumeClaim에 바인딩
  • 6. Pod는 PersistentVolumeClaim을 추가하여 volume 사용

AWS EBS

먼저 살펴볼 볼륨은 aws ebs이다. ebs는 aws managed service로 대표적인 블록 스토리지이다.

[storageclass-aws.yaml]

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: aws-sc-ebs
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
  fsType: ext4

aws-ebs의 StorageClass를 추가할 경우 다음과 같은 parameter를 함께 구성한다.

  • type : io1, gp2, sc1, st1, default(gp2)
  • iopsPerGB : io1 볼륨 전용. 1초당 GiB에 대한 I/O 작업 수. AWS 볼륨 플러그인은 요청된 볼륨 크기에 곱셈하여 볼륨의 IOPS를 계산하고 이를 20,000 IOPS로 제한한다.
  • fsType : fsType은 쿠버네티스에서 지원된다. default("ext4")
  • encrypted : EBS 볼륨의 암호화 여부
  • kmsKeyId : 선택 사항. 볼륨을 암호화할 때 사용할 키의 전체 Amazon 리소스 이름.

provisioner를 통해 aws-ebs와 연결하는 storageclass임을 알 수 있다. parameters.type이 gp2이며, aws ebs volume type은 다음을 참조한다.

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html

 

Amazon EBS volume types - Amazon Elastic Compute Cloud

Amazon EBS volume types Amazon EBS provides the following volume types, which differ in performance characteristics and price, so that you can tailor your storage performance and cost to the needs of your applications. The volumes types fall into these cat

docs.aws.amazon.com

[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k apply -f storageclass-aws.yaml 
storageclass.storage.k8s.io/aws-sc-ebs created
[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
aws-sc-ebs      kubernetes.io/aws-ebs   Delete          Immediate              false                  8s
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  34h
[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

위와 같이 storageclass 추가 후 aws-sc-ebs를 조회한 결과이다. 이미 eks 설치 시점에 gp2라는 aws-ebs provisioner가 default로 추가되어 있는 것을 확인할 수 있지만, 본 포스팅에서는 aws-sc-ebs를 활용하도록 한다.

[persistentvolumeclaim-aws.yaml]

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: aws-sc-ebs-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: aws-sc-ebs

aws-sc-ebs-pvc PersistentVolumeClaim은 aws-sc-ebs storageclass를 사용하여 Dynamic Provisioning을 위한 트리거를 동작한다. storage 크기는 5Gi로 생성한다.

[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k apply -f persistentvolumeclaim-aws.yaml 
persistentvolumeclaim/aws-sc-ebs-pvc created
[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pvc
NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
aws-sc-ebs-pvc   Bound    pvc-f64695d3-a6a2-4857-80ac-2bb9d344a55d   5Gi        RWO            aws-sc-ebs     7s
[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

위와 같이 persistentvolumeclaim 추가 후 aws-sc-ebs-pvc를 조회한 결과이다.

마지막으로 Dynamic Provisioning으로 생성된 PersistentVolume을 확인해 보자.

[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
pvc-f64695d3-a6a2-4857-80ac-2bb9d344a55d   5Gi        RWO            Delete           Bound    default/aws-sc-ebs-pvc   aws-sc-ebs              10s
[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

위와 같이 자동으로 생성된 PV를 확인할 수 있다. 이제 Application을 생성하여 볼륨이 정상적으로 생성되고 공유되는지 확인해 보자.

[namespace.yaml]

apiVersion: v1
kind: Namespace
metadata:
    name: app-test

[nginx-service.yaml]

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - nodePort: 30380
    port: 80
    protocol: TCP
  type: NodePort
  selector:
    run: my-nginx

[nginx-deployment.yaml]

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: aws-sc-ebs-pvc-nginx
          mountPath: /data
      volumes:
      - name: aws-sc-ebs-pvc-nginx
        persistentVolumeClaim:
          claimName: aws-sc-ebs-pvc

위와 같이 생성한 yaml 파일 반영 후 pv 볼륨을 확인해 보자. 자세한 Deployment 내 Volume 구성 방법은 아래 URL을 참고한다.

https://waspro.tistory.com/580?category=831751

 

Kubernetes Persistent Volume 생성하기 - PV, PVC

서론 본 포스팅에서는 Kubernetes에서 Persistent Volume & Persistent Volume Claim을 사용하는 방법에 대해 알아보도록 하겠습니다. 먼저 Persistent Volume(이하 PV)에 대해 알아보겠습니다. PV는 Kubernetes와..

waspro.tistory.com

[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pods
NAME                       READY   STATUS    RESTARTS   AGE
my-nginx-87f6dbffb-p2647   1/1     Running   0          106s
my-nginx-87f6dbffb-twvkf   1/1     Running   0          106s
[root@ip-192-168-78-195 aws-sc (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k exec -it my-nginx-87f6dbffb-p2647 /bin/bash
root@my-nginx-87f6dbffb-p2647:/# cd /data
root@my-nginx-87f6dbffb-p2647:/data# ls
lost+found
root@my-nginx-87f6dbffb-p2647:/data# touch test
root@my-nginx-87f6dbffb-p2647:/data# ls -la
total 20
drwxr-xr-x 3 root root  4096 Apr  2 15:52 .
drwxr-xr-x 1 root root    51 Apr  2 15:45 ..
drwx------ 2 root root 16384 Apr  2 15:45 lost+found
-rw-r--r-- 1 root root     0 Apr  2 15:52 test
root@my-nginx-87f6dbffb-p2647:/data#

deployment replicas에 의해 my-nginx pod는 2개 배포되어 있다. 그 중 7로 끝나는 pod에 접속하여 pvc에 정의한 mount point인 /data 하위에 test라는 파일을 생성한다.

[root@ip-192-168-78-195 ~ (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k exec -it my-nginx-87f6dbffb-twvkf /bin/bash
root@my-nginx-87f6dbffb-twvkf:/# cd /data
root@my-nginx-87f6dbffb-twvkf:/data# ls -la
total 20
drwxr-xr-x 3 root root  4096 Apr  2 15:52 .
drwxr-xr-x 1 root root    51 Apr  2 15:45 ..
drwx------ 2 root root 16384 Apr  2 15:45 lost+found
-rw-r--r-- 1 root root     0 Apr  2 15:52 test
root@my-nginx-87f6dbffb-twvkf:/data#

다음으로 f로 끝나는 pod에 접속하여 방금 생성한 test 파일이 생성되었는지 여부를 확인한다. 위와 같이 정상적으로 파일 볼륨 생성 및 파일공유가 이뤄지는 것을 확인할 수 있다.


NFS

다음으로 NFS(Network File System)을 동적 프로비저닝하는 방법에 대해 알아보자. Kubernetes는 NFS Provisioning을 처리하기 위해 외부 프로비저닝 도구를 구성해야 한다.

nfs-subdir-external-provisioner는 기 구성되어 있는 NFS 서버를 PersistentVolumeClaim, StorageClass를 사용하여 PersistentVolume을 자동으로 프로비저닝하기 위한 provisioner이다.

본 테스트를 위해 NFS 서버는

https://waspro.tistory.com/586

 

5분만에 NFS 구축하기 (CentOS7)

개요 본 포스팅에서는 CentOS7에 NFS를 구축하는 방법에 대해 살펴보겠습니다. NFS는 Network File System으로 파일 시스템을 외부에서 접근하여 사용하고 공유할 수 있도록 하는 볼륨의 형태입니다. 본

waspro.tistory.com

를 참고하여 구성하며, NFS-UTIL과 PORTMAP을 구성하였다.

1. github repository clone

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

 

GitHub - kubernetes-sigs/nfs-subdir-external-provisioner: Dynamic sub-dir volume provisioner on a remote NFS server.

Dynamic sub-dir volume provisioner on a remote NFS server. - GitHub - kubernetes-sigs/nfs-subdir-external-provisioner: Dynamic sub-dir volume provisioner on a remote NFS server.

github.com

[root@ip-192-168-78-195 nfs (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
Cloning into 'nfs-subdir-external-provisioner'...
remote: Enumerating objects: 7221, done.
remote: Counting objects: 100% (6023/6023), done.
remote: Compressing objects: 100% (3024/3024), done.
remote: Total 7221 (delta 3193), reused 5570 (delta 2790), pack-reused 1198
Receiving objects: 100% (7221/7221), 7.42 MiB | 10.60 MiB/s, done.
Resolving deltas: 100% (3838/3838), done.
[root@ip-192-168-78-195 nfs (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

2. Setup Authorization

[root@ip-192-168-78-195 nfs (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# cd nfs-subdir-external-provisioner/
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# NS=$(kubectl config get-contexts|grep -e "^\*" |awk '{print $5}')
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# NAMESPACE=${NS:-default}
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# echo $NS
default
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# echo $NAMESPACE
default
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# sed -i'' "s/namespace:.*/namespace: $NAMESPACE/g" ./deploy/rbac.yaml ./deploy/deployment.yaml
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# kubectl create -f deploy/rbac.yaml
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

3. Configure nfs-subdir-external-provisioner

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /root/nfs
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.78.195
            - name: NFS_PATH
              value: /root/nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.78.195
            path: /root/nfs

nfs-client-provisioner 컨테이너를 배포하여 재정의된 nfs-subdir-external-privisioner를 구성한다. 유의할 점은 env tag 내 정보들을 환경에 맞게 수정 반영한다.

  • PROVISIONER_NAME : PROVISIONER 이름을 지정하며, StorageClass에서 provisioner로 동일한 이름을 지정
  • NFS_SERVER : NFS SERVER IP
  • NFS_PATH : NFS Server Root Path
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k apply -f deploy/deployment.yaml 
deployment.apps/nfs-client-provisioner created
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pods 
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-86cd8c4768-nkxvb   1/1     Running   0          11s
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

4. StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  pathPattern: "${.PVC.namespace}/${.PVC.annotations.nfs.io/storage-path}" # waits for nfs.io/storage-path annotation, if not specified will accept as empty string.
  onDelete: delete

Storage Class의 provisioner를 nfs-client-provisioner의 PROVISIONER_NAME과 매핑한다. nfs-client의 StorageClass를 추가할 경우 다음과 같은 parameter를 함께 구성한다.

  • onDelete : delete일 경우 디렉터리 삭제. retain일 경우 디렉토리 유지.
  • archiveOnDelete : false일 경우 디렉토리를 삭제. onDelete존재하는 경우 archiveOnDelete 무시.
  • pathPattern : label, annotation, name, namepsace와 같은 PVC 메타데이터를 통해 디렉토리 경로를 생성하기 위한 템플릿을 지정. 사용방법 - ${.PVC.<metadata>}
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k apply -f deploy/class.yaml 
storageclass.storage.k8s.io/nfs-client created
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get sc
NAME            PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2 (default)   kubernetes.io/aws-ebs                         Delete          WaitForFirstConsumer   false                  47h
nfs-client      k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate              false                  8s
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

위와 같이 storageclass를 추가하고 상태를 확인한다.

5. PersistentVolumeClaim

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi

다음으로 Dynamic Provisioning을 트리거 하기 위한 PersistentVolumeClaim을 생성한다.

[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k apply -f deploy/test-claim.yaml 
persistentvolumeclaim/test-claim created
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
test-claim   Bound    pvc-d5483f0f-eda5-4d7e-bdda-d8febaa39696   1Mi        RWX            nfs-client     9s
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
pvc-d5483f0f-eda5-4d7e-bdda-d8febaa39696   1Mi        RWX            Delete           Bound    default/test-claim   nfs-client              2m
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

위와 같이 pvc 생성 시 자동으로 pv를 생성하고 pvc가 Bound 되는 것을 확인할 수 있다.

6. Pod deploy

이제 Pod를 생성하고, 볼륨의 동작에 대해 확인해 보도록 하자.

kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox:stable
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim

위와 같이 test-pod는 pod 내 mount path인 "/mnt" 하위에 SUCCESS라는 파일을 생성하고 Pod를 완료한다.

[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k apply -f deploy/test-pod.yaml 
pod/test-pod created
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pods
NAME                                      READY   STATUS              RESTARTS   AGE
nfs-client-provisioner-86cd8c4768-nkxvb   1/1     Running             0          115s
test-pod                                  0/1     ContainerCreating   0          5s
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pods
NAME                                      READY   STATUS      RESTARTS   AGE
nfs-client-provisioner-86cd8c4768-nkxvb   1/1     Running     0          119s
test-pod                                  0/1     Completed   0          9s
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

Pod의 상태가 Complete으로 변경되면 아래와 같이 NFS Server를 확인해 보자.

[root@ip-192-168-78-195 default (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# ls -la
total 0
drwxrwxrwx 2 root      root      21 Apr  3 00:03 .
drwxr-xr-x 3 root      root      21 Apr  1 16:48 ..
-rw-r--r-- 1 nfsnobody nfsnobody  0 Apr  3 00:00 SUCCESS
[root@ip-192-168-78-195 default (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

SUCCESS 파일이 생성된 것을 확인할 수 있다.

7. deployment deploy

다음으로 nginx pod를 생성하여 volume을 사용하는 deployment를 배포해 보자.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
          - name: nfs-pvc
            mountPath: "/mnt"
      volumes:
        - name: nfs-pvc
          persistentVolumeClaim:
            claimName: test-claim

해당 deployment는 replicas를 2로 구성하고 각 pod간 데이터가 공유되는지, NFS에는 저장이 되는지 확인해 보자.

[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k get pods 
NAME                                      READY   STATUS    RESTARTS   AGE
my-nginx-95b4dc9cc-hkshf                  1/1     Running   0          37s
my-nginx-95b4dc9cc-jt5ml                  1/1     Running   0          37s
nfs-client-provisioner-86cd8c4768-nkxvb   1/1     Running   0          21m
[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k exec -it my-nginx-95b4dc9cc-hkshf /bin/bash
root@my-nginx-95b4dc9cc-hkshf:/# cd /mnt
root@my-nginx-95b4dc9cc-hkshf:/mnt# touch nfstest
root@my-nginx-95b4dc9cc-hkshf:/mnt# ls -la
total 0
drwxrwxrwx 2 root   root    36 Apr  3 00:20 .
drwxr-xr-x 1 root   root    39 Apr  3 00:18 ..
-rw-r--r-- 1 nobody nogroup  0 Apr  3 00:00 SUCCESS
-rw-r--r-- 1 nobody nogroup  0 Apr  3 00:20 nfstest
root@my-nginx-95b4dc9cc-hkshf:/mnt#

위와 같이 f로 끝나는 nginx pod에 접속하여 "/mnt" 하위에 nfstest 파일을 생성한다.

[root@ip-192-168-78-195 nfs-subdir-external-provisioner (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# k exec -it my-nginx-95b4dc9cc-jt5ml /bin/bash
root@my-nginx-95b4dc9cc-jt5ml:/# cd /mnt
root@my-nginx-95b4dc9cc-jt5ml:/mnt# ls -la
total 0
drwxrwxrwx 2 root   root    36 Apr  3 00:20 .
drwxr-xr-x 1 root   root    39 Apr  3 00:18 ..
-rw-r--r-- 1 nobody nogroup  0 Apr  3 00:00 SUCCESS
-rw-r--r-- 1 nobody nogroup  0 Apr  3 00:20 nfstest
root@my-nginx-95b4dc9cc-jt5ml:/mnt#

다음으로 l로 끝나는 Pod에 연결하여 "/mnt" 경로 하위를 확인한다.

마지막으로 NFS Server를 직접 확인한다.

[root@ip-192-168-78-195 default (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]# ls -la
total 0
drwxrwxrwx 2 root      root      36 Apr  3 00:20 .
drwxr-xr-x 3 root      root      21 Apr  1 16:48 ..
-rw-r--r-- 1 nfsnobody nfsnobody  0 Apr  3 00:20 nfstest
-rw-r--r-- 1 nfsnobody nfsnobody  0 Apr  3 00:00 SUCCESS
[root@ip-192-168-78-195 default (iam-root-account@NRSON-EKS-CLUSTER.ap-northeast-2.eksctl.io:default)]#

위와 같이 NFS 서버에 정상적으로 추가된 것을 확인할 수 있다.


결론

서두에 이야기한 것처럼 Kubernetes와 연결하여 사용되는 수많은 볼륨들은 이제 CSI 표준에 따라 구성되며, 손쉬운 확장과 통합을 지원하게 되었다. 그 확장성의 측면에서 Kubernetes의 부담을 줄이고, 볼륨을 제공하는 Provider 측면에서 인터페이스를 개발하여 제공하게 됨으로써 유연함을 확보할 수 있다.

여전히 Kubernetes에서 직접 연결하는 방식에 대해 제공되고 있지만, Provider를 제공하는 Volume에 대해서는 CSI Provisioner에 의해 구현된 StorageClass, PersistentVolume에 의해 자동으로 프로비저닝을 관리하는 것이 보다 효과적일 것이다.

728x90
반응형