본문 바로가기

OpenSource/kubernetes

[Kubernetes] Statefulset에서 Local Persistent Volume 사용하기

쿠버네티스에 Mysql이나 Mongodb 같은 DB를 설치할때 statefulset을 이용하여 저장소를 구성할수 있다.
이때 Statefulset에서 사용하는 저장소를 NFS같은 네트워크 PV(Persistent Volume)을 설정하여 저장소를 구축할수 있지만, 좀더 나은 성능을 위해 쿠버네티스 노드의 로컬디스크를 이용하여 PV를 구성할 수도 있다.

오늘은 Local Persistent Volume을 구성하는 방법을 정리해보고자 한다.

1. Local Storage Class 생성

노드의 디스크를 Statefulset에서 바로 사용하려면 Local Storage Class를 생성해야 한다. 아래와 같은 yaml을 작성하여 Local Storage Class를 생성할 수 있다.

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

이후 아래의 명령을 수행하여 Local Storage Class를 생성한다.

$ kubectl create -f ./local-storage-class.yaml

2. 쿠버네티스 노드에 디렉토리 생성

노드의 디스크에 직접 엑세스 하기 위해 미리 디렉토리를 만들어 둬야 한다. Statefulset이 생성될 노드에 모두 동일한 경로의 디렉토리를 생성한다.
필자는 /data/vol1 이라는 디렉토리를 생성하여 vol1 디렉토리에 Statefulset 데이터가 저장되도록 했다.

$ sudo mkdir -p /data/vol1
$ sudo chown -R 유저:그룹 /data/vol1
$ chmod 755 /data/vol1

3. Statefulset replicas 갯수 만큼 Persistent volume 생성

로컬 디스크를 이용하려면 Statefulset의 replicas 갯수 만큼 미리 PV를 생성해 둬야 한다. 만약 한개의 PV만 만들어 두었다면 한개의 Statefulset만 구동이 되고, 나머지 Statefulset은 pending 상태에서 진행이 되지 않는다.

이때 중요한것은 PV를 생성할때 어떠한 PVC와 연동될 수 있는지 지정해야 하며, 이는 네이밍룰에 의해 Persistent Volume Claim(PVC) 이름을 예측하고, PV의 ClaimRef 를 이용하여 예측되는 PVC 이름을 지정하여 PV와 PVC를 연동할 수 있다.

PVC 의 네이밍 룰은 아래와 같다.

VC_TEMPLATE_NAME-STATEFULESET_NAME-**REPLICA-INDEX

**

예를 들어 아래와 같은 Statefulset Manifest 파일이 있다고 가정해보자.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx_sf            ## Statefulset Name
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: data0
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: data0                        ## Volume Claim(VC) Name
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 100Gi

이때 Statefulset의 이름은 nginx-sf 이며, Volume Claim(VC) 이름은 data0이 된다.

이후 volumeClaimTemplates 에 의해 자동으로 생성되는 PVC 이름은

data0-nginx_sf-0
data0-nginx_sf-1

이렇게 두개의 PVC가 자동으로 생성된다.

유추된 PVC 이름을 가지고 Persistent Volume을 생성하면된다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-0                         ## 이름이 중복되지 않게 마지막에 인덱스를 붙인다.
spec:
  capacity:
    storage: 100Gi
  accessModes:
  - ReadWriteOnce
  claimRef:                                ## claimRef를 통해 PVC를 지정한다.
    namespace: nginx
    name: data0-nginx_sf-0                 ## 바로 전 단계에서 생성될 것이라고 예상되는 PVC이름을 입력한다.
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /data/vol1                        ## 2. 쿠버네티스 노드에 디렉토리 생성 의 단계에서 생성한 로컬 디렉토리를 지정한다.
#  nodeAffinity:                                ## 이건 옵션이라 따로 지정하지 않아도 된다. 만약 Statefulset이 특정 노드에만 실행하도록 nodeAffinity를 설정했다면, 그것에 맞춰 PV 설정에도 추가한다.
#    required:
#      nodeSelectorTerms:
#      - matchExpressions:
#        - key: node-group
#          operator: In
#          values:
#          - master
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-1                                ## 첫번째 PV와 중복되지 않도록 index를 붙인다.            
spec:
  capacity:
    storage: 100Gi
  accessModes:
  - ReadWriteOnce
  claimRef:
    namespace: nginx
    name: data0-nginx_sf-1                        ## 두번째 PVC를 입력 
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /data/vol1
#  nodeAffinity:
#    required:
#      nodeSelectorTerms:
#      - matchExpressions:
#        - key: node-group
#          operator: In
#          values:
#          - master

예제에는 Replicas 갯수만큼 PV를 지정했지만 Helm과 같은 툴을 이용하여 반복작업을 하지 않도록 하자.

4. 실행

먼저 PV를 생성한후 Statefulset을 실행하자.

## pv 생성
$ kubectl apply -f ./pv.yaml

## Statefulset 생성
$ kubectl apply -f ./sf.yaml

5. 삭제

재설치를 한다거나, 더이상 필요가 없어질 경우 아래와 같은 방식으로 모든 데이터를 깨끗히 지울수 있다.

단 PV, PVC를 삭제한다고 실제 로컬 디스크의 파일이 지워지지는 않기 때문에 모든 데이터를 삭제하고자 한다면 실제 노드의 디렉토리(이 예제에서는 /data/vol1) 도 함께 삭제해 주어야한다.

## PVC 먼저 삭제후, PV를 삭제해야 한다.
## 필자는 PV, PVC 모두 테스트로 만든 상태임으로 --all을 이용해 모든 PV, PVC를 삭제한다.
## 만약 이미 다른 PV, PVC를 사용한다면 --all 옵션을 사용하지 말고 삭제해야한다.
$ kubectl delete pvc --all
$ kubectl delete pv --all

## 이후 노드의 디렉토리를 실제로 삭제해줘야 한다.

 

[참고]