시작하며

Docker에서도 컨테이너의 데이터를 영속적으로 저장하기 위해 호스트 영역을 공유하는 방식을 사용한다. 쿠버네티스도 동일하게 호스트에 위치한 디렉터리를 각 포드와 공유함으로써 데이터를 보존할 수 있다. 하지만 쿠버네티스는 클러스터 환경이기 때문에, 운영상의 이유(장애 등)로 워커 노드 중 다른 노드로 포드가 재배정되면 기존 호스트의 저장공간에 접근할 수 없게 된다. 따라서 어느 노드에서도 접근 가능한 Persistent Volume을 사용한다.

볼륨 타입별 개념과 사용법

Type 1. Local Volume — hostPath와 emptyDir

hostPath

hostPath는 워커 노드의 호스트와 볼륨을 공유하기 위해 사용한다. CAdvisor와 같은 모니터링 툴에서 주로 사용하며, 일반적인 애플리케이션에는 권장되지 않는다.

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
    - name: my-container
      image: busybox
      args: [ "tail", "-f", "/dev/null" ]
      volumeMounts:
        - name: my-hostpath-volume
          mountPath: /etc/data # 포드의 /etc/data를 호스트의 /tmp에 연결
  volumes:
    - name: my-hostpath-volume
      hostPath:
        path: /tmp 
kubectl apply -f hostpath-pod.yaml 
kubectl exec -it hostpath-pod -- touch /etc/data/mydata

emptyDir

emptyDir는 포드가 실행되는 도중에만 필요한 휘발성 데이터를 각 컨테이너가 함께 사용할 수 있도록 볼륨을 공유할 때 사용한다. 포드가 삭제되면 emptyDir에 저장된 데이터도 함께 삭제된다.

한 컨테이너가 파일을 관리하고, 다른 컨테이너가 그 파일을 사용하는 경우에 유용하게 쓸 수 있다.

apiVersion: v1
kind: Pod
metadata: 
  name: emptydir-pod
spec:
  containers:
    - name: content-creator
      image: alicek106/alpine-wget:latest
      args: ["tail", "-f", "/dev/null"]
      volumeMounts:
      - name: my-emptydir-volume
        mountPath: /data # 컨테이너가 파일을 생성하는 영역
  
    - name: apache-webserver
      image: httpd:2
      volumeMounts:
      - name: my-emptydir-volume
        mountPath: /usr/local/apache2/htdocs/ # 아파치 웹서버에서 접근
 
  volumes:
    - name: my-emptydir-volume
      emptyDir: {}
kubectl apply -f emptydir-pod.yaml
 
kubectl exec -it emptydir-pod -c content-creator sh 
/ # echo Hello, Kubernetes! >> /data/test.html
/ # exit
 
kubectl describe pod emptydir-pod | grep IP
 
kubectl run -i tty --rm debug \
  --image=alicek106/ubuntu:curl --restart=Never -- curl 172.17.0.8/test.html
  Hello, Kubernetes!

Type 2. 네트워크 볼륨

쿠버네티스는 네트워크 볼륨의 위치에 상관없이, 네트워크로 접근만 가능하다면 어느 곳에서든지 포드에 마운트할 수 있다. 네트워크 볼륨을 선택하는 기준은 다음과 같다.

  1. 데이터 읽고 쓰는 속도
  2. 마운트 방식 (1:1 또는 1:N)
  3. 구축 비용

NFS

NFS(Network File System)는 하나의 서버만으로 간편하게 사용할 수 있으며, NFS를 마치 로컬 스토리지처럼 사용할 수 있다는 장점이 있다.

  • NFS 서버: 영속적인 데이터가 실제로 저장되는 네트워크 스토리지 서버
  • NFS 클라이언트: NFS 서버에 마운트해 스토리지에 파일을 읽고 쓰는 역할

Type 3. PV, PVC를 이용한 볼륨 관리

PV, PVC는 왜 사용하나?

NFS와 같이 쿠버네티스에서 지원하는 대부분의 볼륨 타입은 포드나 디플로이먼트 YAML 파일에서 직접 정의해 사용할 수 있다. 하지만 이 방식을 사용하면 특정 볼륨 타입(NFS 등)에 종속되어 다른 볼륨으로 교체가 어렵다. 이른바 결합도가 높아지는 상황이 된다.

이를 해결하기 위해 PV와 PVC를 사용한다.

PV(Persistent Volume)PVC(Persistent Volume Claim)는 포드(YAML)가 세부 사항을 몰라도 볼륨을 사용할 수 있도록 추상화해주는 역할을 담당한다.

Pod <-> PVC(개발자) <-> PV(인프라관리자) <-> 네트워크 볼륨

PV, PVC 사용하기 (AWS EBS 활용)

핵심은 외부 네트워크 볼륨을 쿠버네티스에 등록하고, 개발자 입장에서 PVC를 사용해보는 것이다.

Step 1) AWS에서 EBS(Elastic Block Store) 생성

EBS의 가용 영역과 리전은 쿠버네티스 워커 노드와 동일한 곳에 있어야 한다.

export VOLUME_ID=$(aws ec2 create-volume --size 5 \
--region ap-northeast-2 \
--availability-zone ap-northeast-2a \ 
--volume-type gp2 \
--tag-specifications \
'ResourceType=volume,Tags=[{Key=KubernetesCluster,Value=mycluster.k8s.local}]' \
| jq '.VolumeId' -r)
 
echo VOLUME_ID

Step 2) EBS 볼륨으로 쿠버네티스 PV(Persistent Volume) 생성

apiVersion: v1
kind: PersistentVolume
metadata:
  name: ebs-pv
spec:
  capacity:
    storage: 5Gi # 볼륨의 크기는 5G
  accessModes:
    - ReadWriteOnce # 하나의 포드(인스턴스)에 의해서만 마운트 가능
  awsElasticBlockStore: # EBS 마운트를 위한 항목 정의
    fsType: ext4
    volumeID: <VOLUME_ID>
cat ebs-pv.yaml | sed "s/<VOLUME_ID>/$VOLUME_ID/g" | kubectl apply -f -
kubectl get pv # 생성된 pv 확인

Step 3) 개발자 입장(사용자)에서 PVC 생성 후 포드에 사용 명시

PVC의 accessMode와 resources는 볼륨의 요구사항이며, 해당 조건을 만족하는 PV와 연결(bind)된다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-ebs-pvc # 1. pvc 생성
spec:
  storageClassName: " "
  accessModes:
    - ReadWriteOnce # 2-1. RWO인 PV와 연결
  resources:
    requests:
      storage: 5Gi # 2-2. 볼륨 크기가 최소 5G인 PV와 연결
---
apiVersion: v1
kind: Pod
metadata:
  name: ebs-mount-container
spec:
  containers:
    - name: ebs-mount-container
      image: busybox
      args: ["tail", "-f", "/dev/null"]
      volumeMounts:
      - name: ebs-volume
        mountPath: /mnt
  volumes:
  - name: ebs-volume
    persistentVolumeClaim:
      claimName: my-ebs-pvc # 3. my-ebs-pvc라는 이름의 pvc를 사용

정리하며

쿠버네티스에서 데이터 영속성을 보장하려면 볼륨 전략을 명확히 세워야 한다. hostPath와 emptyDir은 단순하지만 클러스터 환경에 적합하지 않고, 네트워크 볼륨(NFS, EBS 등)은 어느 노드에서도 접근이 가능하다. PV와 PVC를 통해 인프라 관리자와 개발자의 역할을 분리하면 볼륨 구현 세부사항과 애플리케이션의 결합도를 낮출 수 있다.