인사말

컨테이너1 기술의 등장으로 기존 가상 머신보다 적은 용량을 가지는 가상 환경을 만들 수 있게 됐고, 가상 환경을 배포하는 시간도 단축되었습니다. 이와 같은 편의성 때문에 최근 클라우드 시스템에서 컨테이너를 많이 사용하고 있는 추세입니다.

이번 포스팅에서 다룰 내용은 쿠버네티스2 환경에서 진행되는데요, 쿠버네티스라는 용어를 오다가다 들어보셨거나 처음 접하신 분들도 있을 거 같습니다. 쿠버네티스가 무엇인지 알아야 포스팅을 이해하는 데 도움이 되기 때문에 간단하게 설명하려 하는데, 그 전에!! 쿠버네티스를 처음 접하시는 분들은 도커3라는 용어도 생소할 거 같다는 생각이 드네요. 그래서 도커를 먼저 간략히 설명하겠습니다.

도커는 위에서 말한 컨테이너를 단일 노드 환경에서 생성, 삭제, 배포 등을 도와주는 도구입니다. 그렇다면 쿠버네티스는 무엇이냐?? 쿠버네티스는 다중 노드 환경에서 컨테이너를 생성, 삭제, 배포, 관리 등을 도와주는 컨테이너 오케스트레이션 중 하나입니다. 예를 들어 컨테이너가 생성될 때 여러 노드 중에 어느 노드에서 실행시켜야 좋을지를 정한다든지, 컨테이너가 문제가 생겼을 때 이를 해결하려고 지속적으로 관리하는 것과 같은 일을 수행합니다.

이번 포스트에서는 쿠버네티스에서 동작하는 컨테이너들이 데이터를 어떻게 관리하는 지 알아보고자 합니다.

기본적으로 컨테이너 내에 있는 데이터는 임시적이며, 컨테이너가 비정상적으로 종료됐을 때 데이터가 사라지거나 파드 내 컨테이너 간 파일을 공유할 때 문제가 생깁니다. 아!! 파드라는 용어도 모르실 수 있으니 설명을 드리자면, 파드는 쿠버네티스에서 생성하고 관리할 수 있는 최소한의 단위이며 파드는 단일 컨테이너일 수 있으며 또한 다중 컨테이너일 수도 있습니다. 컨테이너의 데이터를 임시로 사용하는 것이 아닌 영구적으로 사용해 앞서 말한 문제점을 해결하기 위하여 쿠버네티스에서는 볼륨이라는 개념을 사용합니다. 쿠버네티스에서는 다양한 유형의 볼륨을 지원하며, 파드는 여러 볼륨 유형을 동시에 사용할 수 있습니다.

파드가 사용할 수 있는 볼륨 유형은 다음과 같습니다:

이름 설명
emptyDir 파드가 생성될 때 생성되고 파드가 삭제될 때 삭제되는 임시 볼륨
hostPath 노드의 로컬 볼륨
awsElasticBlockStore 아마존 웹 서비스 EBS 볼륨
azureDisk 마이크로소프트 애져 볼륨
cephfs Ceph4 볼륨
cinder 오픈스택5 Cinder 볼륨
gcePersistentDisk 구글 컴퓨트 엔진 볼륨
glusterfs 글러스터6(오픈 소스 네트워크 파일시스템) 볼륨
iSCSI iSCSI7 볼륨
NFS NFS(네트워크 파일시스템) 볼륨

위의 볼륨 유형 중 emptyDir는 앞서 설명한 것과 같이, 컨테이너 데이터를 임시로 저장하는 볼륨으로 파드가 삭제될 때 데이터가 영구적으로 삭제가 됩니다. 하지만 emptyDir 외 나머지 볼륨들은 파드가 제거되더라도 데이터는 유지되며, 같은 내용을 서로 다른 파드들끼리 공유할 수 있습니다.

 

쿠버네티스 스토리지

쿠버네티스 사용자 및 관리자에게 스토리지 사용 방법에서부터 제공 방법에 대한 세부 사항을 추상화하는 퍼시스턴트 볼륨(Persistent Volume, 이하 PV), 퍼시스턴트 볼륨 클레임(Persistent Volume Claim, 이하 PVC)이라는 두 가지 API 리소스를 사용합니다.

  • PV

    PV는 관리자가 사용자의 스토리지 요청에 맞게 프로비저닝하여 파드에서 사용할 수 있는 쿠버네티스 클러스터의 스토리지입니다.

  • PVC

    PVC는 사용자가 PV에 요구하는 스펙입니다. 사용하려는 용량, 접근 모드 등을 PV에 요청합니다.

  • Life Cycle

    PV와 PVC는 서로가 밀접한 만큼 라이프사이클을 통해 상태를 구분지을 수 있습니다. 아래 [그림 1]은 PV와 PVC의 라이프사이클 단계를 표현합니다.

     

    Alt_text

    그림 1. PV-PVC Life Cyle

     

    • 프로비저닝(Provisioning)

      PV를 만드는 단계입니다.
      이 단계에서는 PV를 미리 만들고 사용하는 정적(static) 방법과 요청이 있을 때 PV를 만드는 동적(dynamic) 방법이 있습니다.

    • 바인딩(Binding)

      위에서 만든 PV를 PVC와 연결하는 단계입니다. PVC에서 원하는 스토리지 용량과 접근 방법에 대응되는 PV와 연결되는데 대응되는 PV가 없으면 대응되는 PV가 생길 때까지 대기합니다.

      PV와 PVC는 1:1 관계이므로, PVC 하나가 여러 개의 PV에 할당될 순 없습니다.

    • 사용(Using)

      PVC는 파드에 설정되고 파드는 PVC를 볼륨으로 인식하여 사용하는 단계입니다.

    • 회수(Reclaiming)

      사용이 끝난 PVC는 삭제되고 PVC가 사용하던 PV를 초기화하는 단계입니다.

      이 과정에는 유지(Retain), 삭제(Delete), 재사용(Recycle)의 3가지 정책이 있습니다.

      • 유지(Retain) : PVC가 삭제될 때 대응되는 PV 상태는 bound -> Released로 바뀌며 다른 PVC가 연결될 수 없는 상태이며 PV 속 데이터는 유지합니다.

      • 삭제(Delete) : PVC가 삭제될 때 대응되는 PV도 같이 삭제합니다.

      • 재사용(Recycle) : PVC가 삭제될 때 대응되는 PV 상태는 bound -> Pending으로 바뀌며 연결될 다른 PVC를 기다립니다.

 

정적 프로비저닝 실습

  • PVC YAML 파일 작성
    # test_pvc.yaml
      
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: test-pvc
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
      selector:
        matchLabels:
          name: test-pv
    
  • PVC 생성

    [root@master ~]# kubectl create -f test_pvc.yaml
    [root@master ~]# kubectl get pvc
    NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         AGE
    test-pvc        Pending                                             1Gi        RWX                                  1s
    
    
  • PV YAML 파일 작성

    # test_pv.yaml
      
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: test-pv
      labels:
        name: test-pv
    spec:
      capacity:
        storage: 1Gi
      accessModes:
      - ReadWriteMany
      nfs:
        server: 192.168.x.x
        path: /testPath
    
  • PV 생성
    [root@master ~]# kubectl create -f test-pv.yaml
    [root@master ~]# kubectl get pv
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                   STORAGECLASS         REASON   AGE
    test-pv                                    1Gi        RWX            Delete           Bound    default/test-pvc                                       1s
    [root@master ~]# kubectl get pvc
    NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         AGE
    test-pvc        bound    test-pv                                    1Gi        RWX                                 32s
    
    

     

스토리지 클래스(Storage Class)

위에서 PVC를 생성하고 PVC에 맞게 정적 방법으로 PV를 생성하여 PV에 연결하는 걸 실습해봤습니다.

자~ 이제 관리자 입장에서 생각해봅시다.

사용자가 파드에 사용할 PVC를 생성했습니다. 이 때 관리자는 사용자가 PVC를 사용할 수 있도록 대응되는 PV를 만드는데, 사용자가 적으면 대응되는 PV를 만드는 건 어렵지 않습니다. 하지만 사용자가 많아 PVC 요청이 많고 시간이 제각각이라면 PVC에 해당되는 PV를 하나하나 만드는 것도 힘들 뿐 아니라, 24시간 항시 PVC 요청을 확인하고 만들 수도 없습니다. 또한 사용자들도 파드를 사용하기 위해 PV가 만들어지길 기다려야 하고, 그 시간이 길어지면 불만이 생길 겁니다. 위와 같은 상황을 위해 쿠버네티스는 동적 프로비저닝을 제공합니다!!

파드가 볼륨을 사용하기 위한 단계는 아래와 같습니다.

Alt_text

그림 2. POD Volume

PVC 생성 -> PV 확인 및 생성 -> PVC-PV bound 확인 -> 파드에 PVC 할당

동적 프로비저닝은 관리자 개입없이 PV를 생성합니다. 쿠버네티스에서 동적 프로비저닝은 스토리지 클래스를 생성하는 것으로 사용할 수 있습니다. 스토리지 클래스에 명시되어 있는 프로비저너가 PVC 상태 모니터링, PV 생성을 담당합니다. 스토리지 클래스에서 사용하는 프로비저너는 CSI 드라이버로 파일 시스템에 따라 다르며 쿠버네티스 인트리에 있는 CSI 드라이버를 사용하거나 외부의 CSI 드라이버를 사용할 수 있습니다.

 

CSI(Container Storage Instance) 드라이버

 

Alt_text

그림 3. CSI Plugin

 

  • 쿠버네티스 CSI 플러그인

    쿠버네티스 CSI은 기본적으로 프로비저너(Provisioner), 어태쳐(Attacher), 컨트롤러(Controller), 노드서버(NodeServer)로 이루어져 있습니다.

    • 프로비저너(Provisioner)

      프로비저너는 클러스터에 PVC가 생성되는 걸 모니터링하고 있다가 PVC가 생성되면 PV를 생성하는 걸 담당합니다.

    • 어태쳐(Attacher)

      어태처는 파드가 PVC를 사용하려할 때 해당 컨테이너에서 PV를 마운트하는 걸 담당합니다.

    • 컨트롤러(Controller)

      컨트롤러는 쿠버네티스 컨테이너에서 사용할 볼륨을 스토리지 서버에서 생성 및 삭제하는 걸 담당합니다.

    • 노드서버(NodeServer)

      노드서버는 파드가 배포될 노드에서 스토리지 볼륨에 마운트할 수 있게 환경을 만드는 걸 담당합니다.

 

SPDK CSI 드라이버

많은 CSI 드라이버 중 저번에 다뤄서 조금은 친숙한 SPDK에서 제공하는 SPDK CSI 드라이버를 알아보겠습니다.

잠깐, SPDK가 기억이 안 나시는 분들이 있으실 수 있으니 간단하게 설명하고 CSI 드라이버로 들어가 볼게요~

SPDK는 고성능 스토리지 애플리케이션을 개발하기 위해 드라이버, 라이브러리, 애플리케이션을 모아 놓은 오픈소스 개발 키드입니다. 최신 저 지연 플래시 스토리지 미디어, 특히 NVMe SSD의 본 성능을 최대한 끌어내기 위해 디자인되었습니다.

보다 자세한 내용은 블로그 예전 내용을 참고하시면 되겠습니다.

 

Alt_text

그림 4. SPDK CSI 드라이버 동작 흐름

 

[그림 4]에서 보는 것처럼 SPDK 컨트롤러는 PVC에 대응되는 PV를 만든 후 SPDK 스토리지 서버에서 동작하고 있는 HTTP Proxy 서버에 요청을 보냅니다.

PVC가 요청한 디스크 용량을 가지고 있는 Logical Volume Store를 선택한 후 요청한 디스크 용량을 가지는 Logical Volume을 만듭니다. 만들어진 Logical Volume을 NVMe-oF Target을 만드는 것으로 의 일이 끝납니다.

이후 만들어진 PVC를 사용하는 파드가 생성되거나 대기상태일 때 파드가 배포되는 워커 노드의 SPDK-CSI Node Driver가 전에 만든 NVMe-oF Target을 연결한 뒤 연결된 NVMe를 워커 노드 볼륨에 마운트하고 해당 볼륨을 파드에 마운트 시킵니다.

 

동적 프로비저닝 실습

SPDK 스토리지 서버 설정

SPDK 스토리지는 SPDK 설치 및 NVMe의 커널 오너쉽은 해제된 상태입니다. SPDK 기본 설정은 링크를 참고하시면 되겠습니다.

  • SPDK 실행
  [root@spdk spdk]# build/bin/nvmf_tgt
  [2022-05-12 15:48:15.906122] Starting SPDK v22.05-pre git sha1 68184a503 / DPDK 21.11.0 initialization...
  [2022-05-12 15:48:15.906269] [ DPDK EAL parameters: [2022-05-12 15:48:15.906319] nvmf [2022-05-12 15:48:15.906346] --no-shconf [2022-05-12 15:48:15.906367] -c 0x1 [2022-05-12 15:48:15.906390] --huge-unlink [2022-05-12 15:48:15.906407] --log-level=lib.eal:6 [2022-05-12 15:48:15.906424] --log-level=lib.cryptodev:5 [2022-05-12 15:48:15.906443] --log-level=user1:6 [2022-05-12 15:48:15.906458] --iova-mode=pa [2022-05-12 15:48:15.906474] --base-virtaddr=0x200000000000 [2022-05-12 15:48:15.906487] --match-allocations [2022-05-12 15:48:15.906503] --file-prefix=spdk_pid28416 [2022-05-12 15:48:15.906520] ]
  EAL: No free 2048 kB hugepages reported on node 1
  EAL: No available 1048576 kB hugepages reported
  TELEMETRY: No legacy callbacks, legacy socket not created
  [2022-05-12 15:48:15.974793] app.c: 603:spdk_app_start: *NOTICE*: Total cores available: 1
  [2022-05-12 15:48:16.134453] reactor.c: 947:reactor_run: *NOTICE*: Reactor started on core 0
  [2022-05-12 15:48:16.134551] accel_engine.c: 842:sw_accel_engine_init: *NOTICE*: Accel framework software engine initialized.
  • Logical Volume Store 생성

    연결되어 있는 NVMe 중 Logical Volume Store로 사용할 NVMe를 선택하여 SPDK에 등록한 뒤 Logical Volume Store를 생성합니다.

  [root@spdk spdk]# lspci | grep SSD
  10000:82:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10001:81:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10001:83:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10001:84:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10002:02:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10003:01:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10003:03:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  10003:04:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05)
  [root@spdk spdk]# sudo scripts/rpc.py bdev_nvme_attach_controller -b NVMe0 -t PCIe -a 10000:82:00.0
  NVMe0n1
  [root@spdk spdk]# sudo scripts/rpc.py bdev_lvol_create_lvstore NVMe0n1 lvs_1
  76d6ec0d-a13f-4bc7-a248-00e4b9f87dcd
  • SPDK HTTP Proxy 시작
  # rpc_http_proxy.py [spdk-server-ip] [port] [username] [password]
  [root@spdk spdk]# sudo scripts/rpc_http_proxy.py 192.168.10.118 9009 root gluesys
  Started RPC http proxy server

 

워커 노드 설정

  • 워커 노드 전부 SPDK CSI 이미지를 생성, nvme-cli 모듈 설치
  [root@worker1 ~]# docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock golang:1.14 bash -c "apt update && apt install -y make git docker.io && git clone https://review.spdk.io/gerrit/spdk/spdk-csi && cd spdk-csi && make image"
  ...
  ...
  === running docker build
  Sending build context to Docker daemon  16.18MB
  Step 1/6 : FROM alpine:3.8
   ---> c8bccc0af957
  Step 2/6 : LABEL maintainers "SPDK-CSI Authors"
   ---> Running in 1427a677fa66
   ---> ca9dbdd54a02
  Removing intermediate container 1427a677fa66
  Step 3/6 : LABEL description "SPDK-CSI Plugin"
   ---> Running in d1ba6e722919
   ---> 0ef066ecb079
  Removing intermediate container d1ba6e722919
  Step 4/6 : COPY spdkcsi /usr/local/bin/spdkcsi
   ---> 1293ad3ef3cd
  Removing intermediate container 1fd591bb9c07
  Step 5/6 : RUN apk add nvme-cli open-iscsi e2fsprogs xfsprogs blkid
   ---> Running in a633714753e3

  fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
  fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
  (1/11) Installing libuuid (2.32-r0)
  (2/11) Installing libblkid (2.32-r0)
  (3/11) Installing blkid (2.32-r0)
  (4/11) Installing libcom_err (1.44.2-r2)
  (5/11) Installing e2fsprogs-libs (1.44.2-r2)
  (6/11) Installing e2fsprogs (1.44.2-r2)
  (7/11) Installing nvme-cli (1.4-r0)
  (8/11) Installing open-isns-lib (0.97-r3)
  (9/11) Installing libmount (2.32-r0)
  (10/11) Installing open-iscsi (2.0.874-r0)
  (11/11) Installing xfsprogs (4.16.1-r1)
  Executing busybox-1.28.4-r3.trigger
  OK: 9 MiB in 24 packages
   ---> d55f7c1fb00a
  Removing intermediate container a633714753e3
  Step 6/6 : ENTRYPOINT /usr/local/bin/spdkcsi
   ---> Running in 6048c9d80cb2
   ---> c2f69de4cc2c
  Removing intermediate container 6048c9d80cb2
  Successfully built c2f69de4cc2c
  [root@worker1 ~]# docker images | grep spdk
  spdkcsi/spdkcsi                                    canary              c2f69de4cc2c        49 seconds ago      26.2 MB
  [root@worker1 ~]# yum install -y nvme-cli
  [root@worker1 ~]# modprobe nvme-tcp

 

쿠버네티스 마스터 노드 설정

  • SPDK CSI 배포 파일 다운
  [root@master ~]# git clone https://review.spdk.io/gerrit/spdk/spdk-csi
  [root@master ~]# cd spdk-csi/deploy/kubernetes/
  [root@master kubernetes]# ls
  config-map.yaml  controller-rbac.yaml  controller.yaml  deploy.sh  ex_pod.yaml  node-rbac.yaml  node.yaml  secret.yaml  snapshotclass.yaml  snapshot.yaml  storageclass.yaml  test_pod.yaml  testpod.yaml  test_pvc.yaml

 

  • Config YAML 파일 수정
  # SPDX-License-Identifier: Apache-2.0
  # Copyright (c) Arm Limited and Contributors
  # Copyright (c) Intel Corporation
  ---
  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: spdkcsi-cm
  data:
    # rpcURL: spdk json rpc target - http://[SPDK Server IP]:[SPDK http proxy port]
    # targetType: nvme-rdma, nvme-tcp, iscsi
    # targetAddr: target service IP
    config.json: |-
      {
        "nodes": [
          {
            "name": "spdk-1",
            "rpcURL": "http://192.168.10.118:9009",  
            "targetType": "nvme-tcp",
            "targetAddr": "192.168.10.118" # SPDK Server IP
          }
        ]
      }

 

  • Secret YAML 수정
  # SPDX-License-Identifier: Apache-2.0
  # Copyright (c) Arm Limited and Contributors
  # Copyright (c) Intel Corporation
  ---
  apiVersion: v1
  kind: Secret
  metadata:
    name: spdkcsi-secret
  stringData:
    # Specify node specific token with item "name" matches ConfigMap.
    # {
    #   "name": "spdk-testnode",
    #   "username": "myuser",
    #   "password": "mypass"
    # }
    secret.json: |-
      {
        "rpcTokens": [
          {
            "name": "spdk-1",
            "username": "root",
            "password": "gluesys"
          }
        ]
      }

 

  • SPDK CSI 드라이버 배포
  [root@master kubernetes]# ./deploy.sh
  === kubectl apply -f config-map.yaml
  configmap/spdkcsi-cm created
  === kubectl apply -f secret.yaml
  secret/spdkcsi-secret created
  === kubectl apply -f controller-rbac.yaml
  serviceaccount/spdkcsi-controller-sa created
  clusterrole.rbac.authorization.k8s.io/spdkcsi-provisioner-role created
  clusterrolebinding.rbac.authorization.k8s.io/spdkcsi-provisioner-binding created
  clusterrole.rbac.authorization.k8s.io/spdkcsi-attacher-role created
  clusterrolebinding.rbac.authorization.k8s.io/spdkcsi-attacher-binding created
  === kubectl apply -f node-rbac.yaml
  serviceaccount/spdkcsi-node-sa created
  === kubectl apply -f controller.yaml
  statefulset.apps/spdkcsi-controller created
  === kubectl apply -f node.yaml
  daemonset.apps/spdkcsi-node created
  === kubectl apply -f storageclass.yaml
  storageclass.storage.k8s.io/spdkcsi-sc created
  === kubectl apply -f snapshotclass.yaml
  error: unable to recognize "snapshotclass.yaml": no matches for kind "VolumeSnapshotClass" in version "snapshot.storage.k8s.io/v1beta1"
  [root@master kubernetes]# kubectl get pods
  NAME                   READY   STATUS    RESTARTS   AGE
  spdkcsi-controller-0   4/4     Running   0          60s
  spdkcsi-node-jqz6w     2/2     Running   0          60s
  spdkcsi-node-x2n72     2/2     Running   0          60s

 

  • PVC & PV 생성 및 확인

    [root@master kubernetes]# kubectl create -f testpod.yaml
    persistentvolumeclaim/spdkcsi-pvc created
    pod/spdkcsi-test created
    [root@master kubernetes]# kubectl get pv
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
    pvc-645738ac-e166-426d-9ae6-6bca7f137711   256Mi      RWO            Delete           Bound    default/spdkcsi-pvc   spdkcsi-sc              30s
    [root@master kubernetes]# kubectl get pvc
    NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    spdkcsi-pvc   Bound    pvc-645738ac-e166-426d-9ae6-6bca7f137711   256Mi      RWO            spdkcsi-sc     32s
    [root@master kubernetes]# kubectl get pods -o wide
    NAME                   READY   STATUS    RESTARTS   AGE     IP              NODE      NOMINATED NODE   READINESS GATES
    spdkcsi-controller-0   4/4     Running   0          5m10s   192.168.10.82   worker1   <none>           <none>
    spdkcsi-node-jqz6w     2/2     Running   0          5m10s   192.168.10.82   worker1   <none>           <none>
    spdkcsi-node-x2n72     2/2     Running   0          5m10s   192.168.10.81   master    <none>           <none>
    spdkcsi-test           1/1     Running   0          52s     10.36.0.2       worker1   <none>           <none>
    [root@master kubernetes]# kubectl exec -it spdkcsi-test -- mount | grep spdk
    /dev/disk/by-id/nvme-2a208f5b-a9fd-4dd7-933c-99783e3ddbfb_spdkcsi-sn on /spdkvol type xfs (rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
    #Worker Node
    [root@worker1 ~]# nvme list
    Node             SN                   Model                                    Namespace Usage                      Format           FW Rev
    ---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
    /dev/nvme0n1     spdkcsi-sn           2a208f5b-a9fd-4dd7-933c-99783e3ddbfb     1         268.44  MB / 268.44  MB    512   B +  0 B   22.05
    

     

SPDK 스토리지 서버 확인

  [root@spdk spdk]# sudo scripts/rpc.py bdev_get_bdevs
  [
    {
      "name": "NVMe0n1",
      "aliases": [
        "19eab925-666a-48fe-b633-15310bd007dd"
      ],
      "product_name": "NVMe disk",
      "block_size": 512,
      "num_blocks": 3125627568,
      "uuid": "19eab925-666a-48fe-b633-15310bd007dd",
      "assigned_rate_limits": {
        "rw_ios_per_sec": 0,
        "rw_mbytes_per_sec": 0,
        "r_mbytes_per_sec": 0,
        "w_mbytes_per_sec": 0
      },
      "claimed": true,
      "zoned": false,
      "supported_io_types": {
        "read": true,
        "write": true,
        "unmap": true,
        "write_zeroes": true,
        "flush": true,
        "reset": true,
        "nvme_admin": true,
        "nvme_io": true
      },
      "driver_specific": {
        "nvme": [
          {
            "pci_address": "10000:82:00.0",
            "trid": {
              "trtype": "PCIe",
              "traddr": "10000:82:00.0"
            },
            "ctrlr_data": {
              "cntlid": 3,
              "vendor_id": "0x1c58",
              "model_number": "HUSPR3216ADP301",
              "serial_number": "STM0001B4236",
              "firmware_revision": "KMGNP120",
              "oacs": {
                "security": 0,
                "format": 1,
                "firmware": 1,
                "ns_manage": 0
              },
              "multi_ctrlr": false,
              "ana_reporting": false
            },
            "vs": {
              "nvme_version": "1.1"
            },
            "ns_data": {
              "id": 1,
              "can_share": false
            }
          }
        ],
        "mp_policy": "active_passive"
      }
    },
    {
      "name": "2a208f5b-a9fd-4dd7-933c-99783e3ddbfb",
      "aliases": [
        "lvs_1/csi-eb26746f-7554-481b-8e24-957730123992"
      ],
      "product_name": "Logical Volume",
      "block_size": 512,
      "num_blocks": 524288,
      "uuid": "2a208f5b-a9fd-4dd7-933c-99783e3ddbfb",
      "assigned_rate_limits": {
        "rw_ios_per_sec": 0,
        "rw_mbytes_per_sec": 0,
        "r_mbytes_per_sec": 0,
        "w_mbytes_per_sec": 0
      },
      "claimed": true,
      "zoned": false,
      "supported_io_types": {
        "read": true,
        "write": true,
        "unmap": true,
        "write_zeroes": true,
        "flush": false,
        "reset": true,
        "nvme_admin": false,
        "nvme_io": false
      },
      "driver_specific": {
        "lvol": {
          "lvol_store_uuid": "76d6ec0d-a13f-4bc7-a248-00e4b9f87dcd",
          "base_bdev": "NVMe0n1",
          "thin_provision": true,
          "snapshot": false,
          "clone": false
        }
      }
    }
  ]

 

마치며

이번 포스팅에서는 쿠버네티스에서 파드의 데이터를 영구적으로 보관하기 위한 PV, PVC 개념과 정적으로 생성하는 방법과 동적으로 생성할 때 필요한 CSI 드라이버 중 SPDK CSI 드라이버를 알아봤습니다. 다음 시간엔 CSI 드라이버를 직접 만드는 시간을 가져보겠습니다.

 

참고

  • 쿠버네티스 PV, PVC - https://kubernetes.io/ko/docs/concepts/storage/persistent-volumes/
  • 쿠버네티스 CSI 드라이버 - https://kubernetes-csi.github.io/docs/drivers.html
  • 쿠버네티스 스토리지 클래스 - https://kubernetes.io/ko/docs/concepts/storage/storage-classes/
  • SPDK CSI 드라이버 - https://github.com/spdk/spdk-csi

각주

  1. 컨테이너 : https://www.redhat.com/ko/topics/containers/containers-vs-vms 

  2. 쿠버네티스 : https://kubernetes.io/ko/docs/concepts/overview/what-is-kubernetes/ 

  3. 도커 : https://www.docker.com/ 

  4. Ceph : https://docs.ceph.com/en/quincy/start/intro/ 

  5. 오픈스택 : https://www.redhat.com/ko/openstack-35971 

  6. GlusterFS : https://docs.gluster.org/en/latest 

  7. iSCSI : https://ko.wikipedia.org/wiki/ISCSI