이번 포스팅에서는 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
그런데 일반 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 |
댓글