DevOps/Kubernetes

[Kubernetes] Volume: k8s에서 데이터를 Disk에 영구 저장하기

ooeunz 2020. 7. 20. 18:03
반응형

앞선 controller 포스팅의 stateful controller를 다루며 잠시 볼륨에 대해서 짚고 넘어갔었습니다. 잠깐 다시 한번 언급하고 넘어가자면, 쿠버네티스는 기본적으로 상태가 없는 stateless 앱 컨테이너를 사용합니다. 상태가 없다는 것은 컨테이너를 종료하고 새로 실행시켰을 때 컨테어너만의 종속적인 특별한 상태가 없기 때문에 자유롭게 다른 노드로 옮길 수 있다는 것을 뜻합니다. 하지만 그 특성상 현재까지 저장된 데이터 역시 같이 손실된다는 특성이 있는데, 몇몇 서비스는 문제가 생기더라도 데이터를 보존해야 하는 경우가 있습니다. 대표적으로 데이터 파일을 저장하는 젠킨스나 데이터베이스가 있습니다.

 

볼륨은 쿠버네티스 Pod에 종속되는 디스크입니다. 컨테이너 단위가 아니라 Pod 단위이기 때문에 그 Pod에 속해 있는 여러 개의 컨테이너가 공유해서 사용할 수 있습니다. 볼륨을 사용하면 컨테이너를 재시작하더라도 데이터를 유지하기 때문에 안정적으로 서비스를 운영할 수 있습니다.

 

쿠버네티스에서 사용할 수 있는 볼륨 플러그인 종류는 굉장히 많은데, 그중 aws, gce, azure와 같은 클라우드 네이밍이 붙은 볼륨은 클라우드 서비스에서 제공하는 볼륨입니다. 그 외에 이번 포스팅에서는 쿠버네티스 내부 오브젝트인 emptyDir, hostPath, local과 같은 컨테이너가 실행된 노드의 디스크를 볼륨으로 사용하는 옵션에 대해 살펴보겠습니다.

 

※ 자세한 볼륨 리스트는 아래의 url을 참고하기 바랍니다.

 

Volumes

On-disk files in a Container are ephemeral, which presents some problems for non-trivial applications when running in Containers. First, when a Container crashes, kubelet will restart it, but the files will be lost - the Container starts with a clean state

kubernetes.io

 

emptyDir

emptyDir은 파드가 실행되는 호스트이 디스크를 임시로 컨테이너 볼륨에 할당해서 사용하는 방법입니다. 따라서 파드가 사라지면 emptyDir에 할당해서 사용했던 볼륨의 데이터도 함께 사라집니다. 여기서 주의할 점이 파드 내의 컨테이너가 삭제되거나 재시작 되더라도 emptyDir의 생명주기는 파드 단위이기 때문에 파드가 삭제되기 전까지 emptyDir은 삭제되지 않고 계속해서 사용 가능합니다. 생명주기를 헷갈리지 않도록 주의하도록 합니다.

 

emptyDir의 이름은 생성 당시에 디스크에 아무 내용이 없기 때문에 붙은 이름입니다. 주로 임시로 대용량 데이터 저장공간이 필요할 때 사용하게 되고 환경에 따라 로컬 디스크에 저장하거나 네트워크 디스크에 저장하는 것을 변경할 수 있습니다. 또한 emptyDir.medium 필드에 Memory라고 지정을 해주면 emptyDir의 데이터가 물리 디스크가 아닌 메모리에 저장되게 됩니다.

 

hostPath

hostPath는 파드가 실행된 호스트가 파일이나 디렉토리를 파드에 마운트 합니다. emptyDir은 임시 디렉토리를 마운트 하지만 hostPath는 실제 파일이나 디렉토리를 마운트 하기 때문에 파드가 재시작되더라도 데이터가 남습니다.

 

PV / PVC

쿠버네티스는 물리 디스크를 설정하고 이해해야하는 인프라적인 복잡성을 추상화하여 손쉽게 설정할 수 있는 개념을 가지고 있습니다. 이를 통해 인프라 종속적인 부분은 시스템 관리자가 설정하고, 개발자는 이에 대한 이해 없이도 간단하게 사용할 수 있도록 PV(PersistentVolume)PVC(PersistentVolumeClaim) 개념을 사용했습니다. PV는 클러스터 내에서 자원으로 다뤄지는 볼륨 자체를 뜻합니다. 파드와는 별개로 별도의 생명주기를 가지고 있습니다. PVC는 사용자가 PV에 하는 요청입니다. 사용하고 싶은 용량이 얼마인지, 일기/쓰기는 어떤 모드를 사용할 것인지 등을 설정합니다. 즉 쿠버네티스는 볼륨을 파드에 직접 할당하는 방식이 아니라 중간에 PVC를 두어서 파드와 스토리지를 분리했습니다. 이런 구조는 각각의 상황에 맞게 다양한 스토리지를 융통성 있게 사용할 수 있다는 장점이 있습니다. 

 

 

PV / PVC Life Cycle

PV와 PVC는 같은 라이프 사이클을 사용합니다. PV는 생성되면 Available 상태가 됩니다. 이 상태에서 PVC에 바인딩되면 Bound 상태로 바뀌게 되고, PVC가 삭제되면 PV가 삭제되는 것이 아니라 Released 상태가 됩니다. (Available 상태를 제외하곤 사용이 불가능합니다.)

 

물론 할당된 PVC가 파드를 유지하고 있는 동안에도 시스템에서 임의로 삭제할 수 없게 됩니다. 사용 중인 데이터 스토리지를 임의로 삭제하면 치명적인 문제가 발생할 수 있기 때문에 사용 중인 오브젝트는 보호됩니다. (이를 오브젝트 보호 Storage Objcet in Use Protection이라고 합니다.)

Provisioning

프로비저닝이란 PV를 생성하는 단계를 이야기합니다. 프로비저닝에는 두 가지 방법이 있습니다.

  1. 정적 방법 : PV를 미리 만들어두고 유저로부터 요청이 있을 때 할당해주는 방법.
  2. 동적 방법 : 요청이 있을 때마다 PV를 만들어서 할당해주는 방법.

 

정적 방법 같은 경우에는 사용할 수 있는 스토리지 용량에 제한이 있을 때 유용하게 사용될 수 있습니다. 예를 들어 1TB의 스토리지를 사용해두더라도 미리 만들어둔 PV의 용량이 200GB라면 300GB의 PV를 요청하더라도 실패하게 됩니다.

 

반면 동적으로 프로비저닝을 하게 되는 경우에는 유저가 PVC를 거쳐서 PV를 요청했을 때 생성해 제공해줍니다. 1TB 스토리지가 있다면 1TB 용량 안에서 사용자가 필요할 때 원하는 용량만큼 생성해서 사용할 수 있습니다. PVC는 동적 프로비저닝을 할 때 여러 가지 스토리지 중 원하는 스토리지를 정의하는 스토리지 클래스로 PV를 생성하게 됩니다. 아래는 PV와 PVC를 생성하는 템플릿의 예제입니다.

 

PV 생성 템플릿

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-sample
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  storageClassName: manual
  persistentVolumeReclaimPolicy: Delete
  hostPath:
    path: /tmp/k8s-pv

PV를 생성하는 템플릿입니다. .spec.accessModes필드는 볼륨의 읽기/쓰기 옵션을 설정합니다. 볼륨은 한 번에 하나의 필드만 설정할 수 있으며 동시에 여러 개를 설정할 수 없습니다. 선택할 수 있는 필드 값은 세 가지가 존재합니다.

  • ReadWriteOnce : 노드 하나에만 볼륨을 읽기/쓰기 하도록 마운트 합니다.
  • ReadOnlyMany : 여러 개 노드에서 읽기 전용으로 마운트 할 수 있습니다.
  • ReadWriteMany : 여러 개 노드에서 읽기 쓰기 가능하도록 마운트 할 수 있습니다.

여기서 주의할 점이 볼륨 플로그인 별로 사용 가능한 엑세스모드에 차이가 있기 때문에 반드시 사용할 볼륨에 따른 엑세스 모드를 공식 문서에서 확인하길 추천드립니다.

 

Persistent Volumes

This document describes the current state of persistent volumes in Kubernetes. Familiarity with volumes is suggested. Introduction Managing storage is a distinct problem from managing compute instances. The PersistentVolume subsystem provides an API for us

kubernetes.io

kubectl get pv 명령어로 pv이 정상적으로 생성됐는지 확인해봅니다.

 

PVC 생성 템플릿

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-sample
spec:
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: manual

이번에는 PVC를 생성하는 템플릿입니다. 주요 필드는 PV 생성 템플릿과 유사한데, 눈여겨볼 점은 .spec.resources.requests.storage 필드 값을 PV 용량 이상으로 설정하게 되면 사용할 수 있는 PV가 없으므로 Pending 상태가 됩니다. 그러므로 PV의 용량 안에서 필요한 만큼 스토리지를 할당받도록 합니다.

 

kubectl get pvc 명령어로 pvc가 정상적으로 실행됐는지 확인해봅니다.

 

Pod에서 PVC를 Volume으로 사용하기

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pvc-deploy-app
  labels:
    app: pvc-deploy-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pvc-deploy-app
  template:
    metadata:
      labels:
        app: pvc-deploy-app
    spec:
      containers:
      - name: pvc-deploy-app
        image: arisu1000/simple-container-app:latest
        ports:
        - containerPort: 8080
        imagePullPolicy: Always
        volumeMounts:
        - mountPath: "/tmp"
          name: myvolume
      volumes:
      - name: myvolume
        persistentVolumeClaim:
          claimName: pvc-hostpath

마지막으로 Pod에서 PVC를 볼륨으로 사용하는 템플릿을 알아보도록 하겠습니다.

.spec.template.spec.volumes[].name : 사용할 볼륨을 설정하게 됩니다. 여기서는 myvolume이라고 설정했고 .spec.template.spec.volumes[].persistentVolumeClaim 필드 값으로 위의 템플릿 예제에서 만든 pvc-sample를 PVC로 설정합니다. 이렇게 되면 디플로이먼트의 파드에서 사용할 samplevolume이라는 볼륨을 준비하게 된 것입니다.

 

준비한 볼륨을 실제 컨테이너에 연결하기 위해서는 .spec.template.spec.containers[].volumeMounts[]의 하위 설정에서 다루게 됩니다. .mountPath 필드 값은 컨테이너의 /tmp 디렉토리로 설정했고, .name 필드 값은 마운트할 볼륨의 이름 samplevolume를 설정했습니다.

 

kubectl apply -f <template.yaml> 명령어로 템플릿을 실행시킨 후 kubectl get pods를 하면 라애와 같이 deployment가 실행된 것을 확인할 수 있습니다.

 

 

파드의 이름을 확인한 다음 kubectl port-forward pods/<pod 이름> 8080:8080 명령어를 실행해서 파드에 접근할 수 있는 포트를 열어줍니다. 그런 다음 localhost8080으로 접속해보면 아래와 같이 정상적으로 접근되는 것을 알 수 있습니다.

반응형