공부/Kubernetes

Kubernetes Local PV

토고미 2022. 3. 7. 19:40

개념

rook-ceph 같은 모듈을 사용하지 않고, k8s 클러스터를 구성하는 노드들의 로컬 스토리지를 볼륨으로서 이용하는 방법이 있다.

바로 LocalPV이다.

 

HostPath와 LocalPV를 헷갈릴 수 있는데, 둘의 가장 큰 차이점은 쿠버네티스 스케줄러가 PV가 어느 노드에 속한지를 알고있다는 것이다.

 

HostPath의 경우에는 pod가 재기동 될 때, 다른 노드에 스케줄링이 될수 있기 때문에 정상 동작을 보장할 수 없다.

하지만 LocalPV는 쿠버네티스 스케줄러가 PV의 위치를 고려해서 pod의 스케줄링을 해준다.

 

역으로 생각하면 pod가 특정 노드에 뜨도록 강제할 수도 있다는 것이다.

물론 NodeSelector를 사용하는 방법도 있지만, statefulset 같이 pod를 개별적으로 설정하기 힘든 경우에는 local PV를 사용해서 스케줄링 할 수 있다.

 

다만 LocalPV는 동적 볼륨 프로비저닝이 안된다. 따라서, PV를 직접 만들어주고 PVC를 만들어야 한다.

 

예제

가장 먼저 StorageClass를 만든다.

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

volumeBindingMode: WaitForFirstConsumer의 의미는 PVC를 사용하는 pod가 실제로 생성되기 전까지는 PV의 바인딩과 프로비저닝을 기다린다는 의미이다. 쉽게말하면 동적 프로비저닝이 안되기 때문에 쓰는 옵션이다.

 

 

그 다음으로 PV를 만든다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: togomi-pv-0
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /tmp
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - mynode-1 # 자신의 노드 이름에 맞게 고쳐줘야함

가장 밑에 values 하위에 들어가는 mynode-1을 자신의 실제 k8s 노드 이름으로 바꿔주면 된다.

 

 

이제 PVC를 만든다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-my-redis-0
  namespace: togomi
spec:
  storageClassName: local-storage
  volumeName: togomi-pv-0
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

volumeName을 넣어줘야 원하는 PV와 바운드가 보장이되니 반드시 넣어주자!

 

드디어 볼륨을 쓸 준비가 됐다. 직접 써보도록 하자.

일반적인 pod는 쉬울테니 스킵하고, 대신에 statefulset을 사용해보자.

아래는 redis statefulset 예시이다.

 

위 yaml에서는 0번 PVC만 보여줬지만, 나는 1,2까지 총 세개의 PV와 PVC를 만들어두었다.

 

statefulset은 이름 규칙이 정해져있으니 그 규칙에따라 미리 PVC를 만들어줘야 한다.

그 규칙은 { volumeClaimTemplate 이름 - statefulset - 숫자 } 의 형식이다.

즉 아래와 같은 statefulset은 "local-my-redis-0, local-my-redis-1, local-my-redis-2"의 pvc를 생성할 것이다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  annotations:
    redis.opstreelabs.in: "true"
  labels:
    app: redis-cluster-leader
    redis_setup_type: cluster
    role: leader
  name: my-redis
  namespace: togomi
spec:
  podManagementPolicy: OrderedReady
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: redis-cluster-leader
      redis_setup_type: cluster
      role: leader
  serviceName: redis-cluster-leader
  template:
    metadata:
      labels:
        app: redis-cluster-leader
        redis_setup_type: cluster
        role: leader
    spec:
      containers:
      - env:
        - name: PERSISTENCE_ENABLED
          value: "true"
        - name: REDIS_ADDR
          value: redis://localhost:6379
        - name: REDIS_PASSWORD
          value: "test"
        - name: SERVER_MODE
          value: cluster
        - name: SETUP_MODE
          value: cluster
        image: quay.io/opstree/redis:v6.2.5
        imagePullPolicy: IfNotPresent
        livenessProbe:
          exec:
            command:
            - bash
            - /usr/bin/healthcheck.sh
          failureThreshold: 5
          initialDelaySeconds: 15
          periodSeconds: 15
          successThreshold: 1
          timeoutSeconds: 5
        name: redis-cluster-leader
        readinessProbe:
          exec:
            command:
            - bash
            - /usr/bin/healthcheck.sh
          failureThreshold: 5
          initialDelaySeconds: 15
          periodSeconds: 15
          successThreshold: 1
          timeoutSeconds: 5
        resources:
          limits:
            cpu: 101m
            memory: 128Mi
          requests:
            cpu: 101m
            memory: 128Mi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /data
          name: local
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      terminationGracePeriodSeconds: 30
  updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate
  volumeClaimTemplates:
  - metadata:
      name: local
    spec:
      accessModes:
      - ReadWriteOnce
      storageClassName: "local-storage"
      resources:
        requests:
          storage: 1Gi

 

정상적으로 생성이 다 잘되었다면 아래와 같이, 내가 의도한 노드에 파드가 스케줄링 되는 것을 확인할 수 있다.

이런 방식으로 statefulset의 개별 pod들도 특정 노드에 띄우는 것이 가능하다!