혼자 쿠버네티스 정리하기 위한 글, 오늘은 volume 편

여태 생성했던 Pod들은 stateless container를 사용하고 있었다. 그래서 Pod나 Node가 재기동되면 그 데이터가 사라지는 단점이 있었다.
만약 제공해야하는 서비스에서 이런 경우에도 데이터를 계속 보존해야한다면 kubernetes에서는 Volume을 사용해야한다.

volume

emptyDir

Pod가 실행되는 host의 disk를 Contatiner의 volume으로 할당하는 기능,
Container가 재기동 되어도 데이터는 보존되지만 Pod가 종료되면 emptyDir에 할당했던 volume 내 데이터도 사라진다.
이 점이 앞서 살펴봤던 kubectl drain node 수행 시 emptyDir 사용하는 Pod이 있는 경우 에러가 발생하는 이유인듯

apiVersion: v1 
kind: Pod 
metadata: 
  name: emptydir-test 
  labels:
    app: emptydir-test 
spec:
  containers:
  - name: nginx
    image: nginx 
    volumeMounts:
    - mountPath: /emptydir 
      name: emptydir-volume 
    volumes:
    - name: emptydir-volume 
      emptyDir: {}

=> .spec.contatiners.volumes[] 에서 사용햐려는 볼륨을 먼저 생성한다.
emptydir-volume이라는 이름의 emptyDir을 생성하고 volumeMounts 에서 이 volume을 사용하겠다고 지정 후
( .spec.containers.volumes[].name => .spec.containers.volumeMounts[].name 으로 매핑 )
.mountPath 필드 값으로 container의 /emptydir 디렉토리로 mount

hostPath

Pod이 실행되는 host의 파일이나 디렉토리를 Pod에 mount하는 기능
emptyDir과는 달리 Pod이 재기동되어도 host에 데이터가 남음

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-test
  labels:
    app: hostpath-test
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - mountPath: /hostpath-volume
      name: hostpath-volume
  volumes:
  - name: hostpath-volume
    hostPath:
      path: /Users/nhn/Documents/kubernetes/volume
      type: Directory

PersistentVolume & PersistentVolumeClaim

앞서 봤던 volume은 volume을 Pod에 직접 할당하는 방식이다.
그렇기 때문에 만약 Pod가 사라지면 직접 할당된 volume도 사라지고 volume에 변경사항이 생기면 (ex. voulme type)
Pod의 volume을 일일이 바꿔줘야 하는 불편사항이 있다.

이 구조를 Pod -> PVC -> PV 로 변경하게 되면 Pod는 PVC를 거쳐서 volume을 사용하게 되기 때문에 어떤 스토리지를 사용하는지 신경 쓰지 않아도 되고
PVC 에서 PV만 새로 변경해주면 되기 때문에 상황에 맞게 유연한 사용이 가능하다.

PV ( PersistentVolume )

가상의 volume 객체로 kubernetes cluster에서 Pod와는 별개 된 자원으로서 관리한다.
별도의 생명주기가 있으며 PVC를 통해 PV 할당 요청을 받으면 사용하고 싶은 용량, 읽기/쓰기 모드 등에 맞게 PV를 provisiong 한다.


위는 static provisiong의 방식을 나타내는데 PV와 PVC는 아래와 같은 생명 주기를 갖는다.

  • provisioing : PV를 생성하는 단계로 미리 만들어 놓는 static, PVC로 부터 요청이 있을 때마다 생성하는 dynamic 방식이 있다.
  • binding : Provisioning으로 생성한 PV를 PVC와 연결하는 단계, PVC에서 요청한 스펙의 PV를 매핑한다. PV와 PVC는 1:1 관계
  • using: PVC는 Pod에 연결되고, Pod는 PVC를 volume ,즉 PVC로 인식해서 사용하는 단계
  • reclaiming: 사용이 끝난 PVC는 삭제하고 PV를 초기화 하는 과정
  • pv_test.yaml
apiVersion: v1
kind: PersistentVolume 
metadata: 
  name: pv-test 
  labels:
    storage: prod
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes: 
  - ReadWriteOnce  
  storageClassName: test
  persistentVolumeReclaimPolicy: Delete
  hostPath:
    path: /Users/nhn/Documents/kubernetes/volume/test/pv-test

=> volume plugin=hostPath 방식으로 1GB 짜리 PV 생성

PVC ( PersistentVolumeClaim )

Pod가 자신이 사용하는 스토리지를 신경쓸 필요 없이 PVC를 통해 사용하고 싶은 스펙의 PV를 PVC에 요청하면 PVC가 그 스펙에 맞는 PV를 생성하고 자신과 매핑시킴.
Pod는 결국 PVC를 Volume으로 인식하여 사용하게 됨

  • pvc_test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-test
spec:
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: test
  selector:
    matchLabels:
      storage: prod

=> .spec.resources.requests 필드를 통해 필요로 하는 자원을 요청한다.
.spec.selector 를 이용하여 labels로 PV와 매핑할 수 있음

$ kubectl get pv,pvc
NAME                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
persistentvolume/pv-test   1Gi        RWO            Delete           Bound    default/pvc-test   test                    6m47s

NAME                             STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc-test   Bound    pv-test   1Gi        RWO            test           7s

=> pvc-test PVC가 pv-test PV와 연결된 것을 확인

  • 매핑되는 PV가 없을 때
$ kubectl get pv,pvc
NAME                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
persistentvolume/pv-test   1Gi        RWO            Delete           Available           test                    74s

NAME                             STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc-test   Pending                                      manual         63s

=> PVC 에서 요청한 storageclass를 사용하는 PV가 없어서 PVC가 PV를 매핑하지 못하고 pending 상태임

  • 매핑되는 PV를 생성하고 다시 조회해보자
$ kubectl get pv,pvc
NAME                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
persistentvolume/pv-test   1Gi        RWO            Delete           Bound    default/pvc-test   manual                  2m53s

NAME                             STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc-test   Bound    pv-test   1Gi        RWO            manual         2m42s

PVC를 사용하는 Pod를 생성해보자

---
apiVersion: apps/v1
kind: Deployment
metadata: 
  name: nginx-pvc-test
  labels:
    app: nginx-pvc-test
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      name: nginx 
      labels:
        app: nginx 
    spec:
      containers: 
      - name:  nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: pod-pvc-test
          mountPath: /pv-test
      volumes:
      - name: pod-pvc-test
        persistentVolumeClaim:
          claimName: pvc-test

=> .spec.template.spec.volumes[].name 으로 사용할 volume을 선언하는데
.spec.template.spec.volumes[].persistentVolumeClaim.cclaimName으로 이전에 생성해놓은 pvc-test PVC를 설정한다.
.spec.template.specc.containers.volumeMounts[].name에서 pvc-test volume을 conatiner의 /pv-test에 mount 한다고 설정한다.
그리고 이 pvc-test PVC는 pv-test 와 매핑되는데 pv-test 는 이전에 hostPath로 host의 /Users/nhn/Documents/kubernetes/volume/test/pv-test 디렉토리에 마운트됨

$ kubectl describe pod/nginx-pvc-test-5d55669b9-vm8ml

Volumes:
  pod-pvc-test:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  pvc-test

$ kubectl describe persistentvolumeclaim/pvc-test

Volume:        pv-test
Mounted By:    nginx-pvc-test-5d55669b9-nkq68
               nginx-pvc-test-5d55669b9-vm8ml

$ kubectl describe persistentvolume/pv-test

Source:
    Type:          HostPath (bare host directory volume)
    Path:          /Users/nhn/Documents/kubernetes/volume/test/pv-test