본문 바로가기
IT/Kubernetes

[Kubernetes] Storage - Volume

by 물통꿀꿀이 2019. 4. 21.

이번 포스팅에서는 k8s의 Volume에 대해 알아보려고 한다.


Volume

Volume을 간단히 정의하면 Pod의 Container에서 접근하여 데이터를 저장할 수 있는 디렉토리이다. (참고로 Docker에서도 Volume을 디렉토리로 정의한다.)

Container에서 접근한다는 의미를 좀 더 이해하기 쉽게 하기 위해 아래 그림을 참조한다.

그림 1. Volume


그림 1을 확인해보면 Pod 내의 각 Container가 Volume을 참조하는 것을 확인 할 수 있다. (File Puller는 Volume에 Write, Web Server는 Volume을 Read)

이렇듯 Volume이 존재하는 이유는 다음과 같다.

1) Container 그 자체는 영구적인 리소스가 아니다.

Container는 언제든 문제가 발생할 수 있는 여지가 있어서 Container가 교체되기라도 하면 Container 내부에 저장된 데이터는 사라질 수 밖에 없다. (Container의 시작 시점은 항상 Clean State를 유지하기 때문에)

물론 그렇다고 Volume 또한 영구적이진 않다. 그림 1에서 볼 수 있듯이 Volume은 Pod 안에 속한 개념이므로 일반적으로 Volume의 Lifetime은 Pod와 같다. 때문에 Pod가 멈추면 Volume 또한 멈추게 된다. 


2) Container 간에 데이터를 공유할 필요가 있다.

그림 2. Volumes in Pod


이외에도 그림 2를 살펴보면 알 수 있듯이 Pod에 여러 개의 Volume을 추가 할 수 있다. k8s에서는 지원하는 Volume 타입은 다양하며 Container에서 동시에 여러 개의 Volume을 mount하여 사용 할 수 있다. 


emptyDir

emptyDir은 Pod가 생성될 때 만들어지는 Volume이다. 때문에 해당 Volume은 Pod의 lifetime*과 동일시 할 수 밖에 없다. (즉, Pod가 종료되면 emptyDir의 데이터 또한 사라진다.) 

*Pod에 영향을 받기 때문에 Container에서 문제가 생겨서 Restart와 같은 Recovery 작업이 일어나도 Volume엔 영향을 주지 않는다.


emptyDir은 이름처럼 비어있는 디렉토리로 임시로 데이터를 디스크에 저장해야 할 때 사용된다. 그렇기 때문에 보통 아래와 같은 목적으로 사용된다.

1) Pod의 Container 간에 동일한 파일을 read & write

2) 대규모의 데이터 임시 저장 (데이터의 크기가 Memory의 크기를 넘어서는 경우)

3) Recovery (Container Crash 대비)


apiVersion: v1

kind: Pod

metadata:

  name: counter

spec:

  containers:

  - name: count

    image: busybox

    args:

    - /bin/sh

    - -c

    - >

      i=0;

      while true;

      do

        echo "$i: $(date)" >> /var/log/1.log; // 1.log 파일 쓰기

        echo "$(date) INFO $i" >> /var/log/2.log; // 2.log 파일 쓰기

        i=$((i+1));

        sleep 1;

      done

    volumeMounts:

    - name: varlog

      mountPath: /var/log // varlog 이름의 Volume이 해당 Container의 /var/log에 mount

  - name: count-log-1

    image: busybox

    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']

    volumeMounts:

    - name: varlog

      mountPath: /var/log // varlog 이름의 Volume이 해당 Container의 /var/log에 mount

  - name: count-log-2

    image: busybox

    args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']

    volumeMounts:

    - name: varlog

      mountPath: /var/log // varlog 이름의 Volume이 해당 Container의 /var/log에 mount

  volumes:

  - name: varlog

    emptyDir: {} // varlog 이름을 가지고 있는 emptyDir Volume


위는 emptyDir Volume 타입을 가지는 매니페스트 파일이다. 내부적으로 3개의 Container가 varlog 이름의 Volume을 mount하여 사용하고 있다. 

또한 메인 Container인 Counter가 emptyDir Volume에 다른 형태의 데이터를 Write하며 SideCar (count-log-1, 2) Container에서 각각 다른 데이터를 Read 한다.


그림 3. Pod for Counter


그림 4. Counter-1 Logs

그림 5. Counter-2 Logs


그림 4,5에서 확인 할 수 있듯이 Log 관련 Container에서는 각각 varlog에 서로 다른 파일을 읽고 출력하는 것을 확인 할 수 있다.


추가로 emptyDir의 Volume은 Pod를 호스팅하는 Node의 환경*에 성능이 달라 질 수 있다.

* Node의 환경은 Node의 디스크 유형으로 HDD, SDD 또는 Network Storage 등이 될 수 있다.

특히, 디스크 대신 메모리를 사용할 수 있다.

volumes:

 - name: html

    emptyDir:

     medium: Memory // emptyDir의 파일은 메모리에 저장됨


위와 같이 매니페스트 파일을 수정하면 메모리를 사용하는 tmpfs를 mount하여 사용할 수 있다. 하지만 tmpfs는 Node를 사용하는 것이기 때문에 Node가 재부팅이라도 되면 저장하고 있는 파일이 다 사라진다. (Node의 메모리에 따라 사용할 수 있는 한계치도 다를 수 있다.)


hostPath

hostPath Volume은 Pod를 호스팅하고 있는 Node의 디렉토리나 파일을 mount 한다. 

그림 6. hostPath


그림 6에서 볼 수 있듯이, Pod가 속한 Node의 파일 또는 디렉토리를 mount 한다. 그런데 Pod 또는 Container가 아닌 Node에 접근해야 하기 때문에 hostPath는 보통 아래의 용도로 사용한다.

1) Docker internals에 접근이 필요할 때 : /var/lib/docker

2) cAdvisor를 사용 할 때 : /sys


이렇게 다양한 용도로 사용되는 hostPath Volume의 매니페스트를 정의하는 방법은 아래와 같다.

apiVersion: v1

kind: Pod

metadata:

  name: test-pd

spec:

  containers:

  - image: k8s.gcr.io/test-webserver

    name: test-container

    volumeMounts:

    - mountPath: /test-pd

      name: test-volume

  volumes:

  - name: test-volume

    hostPath:

      # directory location on host

      path: /data

      # this field is optional

      type: Directory 

그런데 위의 정의에도 볼 수 있듯이 hostPath의 type을 정할 수 있다. (물론 optional 이지만) 공식 문서에서 제공하는 type에 들어 갈 수 있는 값을 보면 아래와 같다.

그림 7. hostPath Type


여러 종류가 존재하지만 일반적으로는 type을 사용하지 않는다. (Default 값을 가지고 있기 때문에) 즉, 각각의 Container에서 Node의 hostPath를 mount 할 때 체크하지 않고 그냥 사용하겠다는 것이다. (물론 이전 버전과의 호환성 때문에 Default 값이 유지되지만 결국 mount 및 read & write에 발생하는 문제는 고스란히 Container의 몫으로...)

그런데 type을 사용함에 있어서는 많은 주의가 필요하다. 특히 hostPath에 파일 또는 디렉토리의 write는 오로지 root에 의해서만 가능하다. 때문에 Container가 hostPath Volume에 write를 하기 위해서는 root 이거나 root에 상응하는 권한을 가지고 있어야 한다. 


그렇다면 실제 hostPath를 사용하고 있는 것들을 살펴보면,

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

  name: monitoring-influxdb

  namespace: kube-system

spec:

  replicas: 1

  template:

    metadata:

      labels:

        task: monitoring

        k8s-app: influxdb

    spec:

      containers:

      name: influxdb

        image: k8s.gcr.io/heapster-influxdb-amd64:v1.5.2

        volumeMounts:

        mountPath: /data

          name: influxdb-storage

      volumes:

      name: influxdb-storage

        hostPath:

          path: /data/shared/heapster/influxdb/data  

위의 매니페스트 파일은 이전 포스팅에서 언급했던 influxDB에 대한 것으로 hostPath를 사용하는 것을 확인 할 수 있다. 또한 k8s의 Master에서 동작하는 여러 Component에서도 hostPath를 사용한다.

그림 8. kube-controller-manager hostPath


그림 8은 controller-manager의 Pod 내부 정의로 역시 hostPath를 사용하는 것을 확인 할 수 있다. 그런데 좀 더 면밀히 살펴보면 hostPath를 Cert, Config에 대해서만 사용하고 있다. 이는 해당 Node가 Master Node의 역할을 가지고 있으므로 Node에서 주요 파일을 Global 같이 관리하고 있다. (즉, 모든 Master Node의 Pod에서 주요 파일에 접근해야 하기 때문에)

그런데 일반 Worker Node에서 hostPath Volume을 사용할 때는 주의를 기울여야 한다. 말 그대로 hostPath Volume을 Node의 특정 경로를 mount하는 것이기 때문에 만일 Pod가 다른 Node에서 생성된다면 mount를 할 수가 없다. (Pod는 항상 같은 Node에서만 생성되는 것이 아니기 때문에)


Reference

https://kubernetes.io/docs/concepts/storage/volumes/

https://www.slideshare.net/Byungwook/kubernetes-4-volume-amp-stateful-set


'IT > Kubernetes' 카테고리의 다른 글

[Kubernetes] Storage - StorageClass  (0) 2019.04.21
[Kubernetes] Storage - PersistentVolume  (0) 2019.04.21
[Kubernetes] Logging  (0) 2019.04.10
[Kubernetes] Monitoring  (0) 2019.04.10
[Kubernetes] Job & CronJob  (1) 2019.04.06

댓글