[Infra] 쿠버네티스(kubernetes)(5) 퍼시스턴트 볼륨 스토리지

업데이트:

퍼시스턴트 볼륨 스토리지(persistent volume storage)

참고 링크

1. 볼륨(volume)의 필요성

쿠버네티스를 이용해 배포한 애플리케이션의 데이터를 보존하기 위해서는 내부 혹은 외부의 스토리지 시스템과 연결해 퍼시스턴트 볼륨(Persistent Volume)을 이용해야 합니다. 예를 들어 DB같은 경우 컨테이너 내에서만 데이터가 생성된다면 컨테이너를 내렸을 경우 컨테이너 안에 있던 데이터도 함께 사라집니다. 따라서 데이터를 보존해야하는 앱의 경우 외부의 볼륨과 연결시켜놔야 컨테이너를 내렸다 올렸다 반복해도 데이터가 유지 됩니다. 이때 퍼시스턴트(persistent)단어는 데이터가 유지된다는 사실을 강조하기 위해 사용한 단어입니다.

2. 볼륨 종류

볼륨은 크게 쿠버네티스 내부에 두느냐 외부에 두느냐로 나눌수 있고, 쿠버네티스 내부에 두는 경우 emptyDir 이냐 hostPath로 나눠집니다.

2.1. emptyDir

먼저 emptyDir의 경우 파드가 종료되면 emptyDir이 사라집니다.

그리고 위 그림과 같이 파드 내 컨테이너 들만 공유하기때문에 파드가 다른 경우에는 사용할 수 없습니다. (추가적으로 확인해봐야할 사항은 emptyDir이 노드 내 경로에도 할당하는지는 알아봐함)

2.2. hostPath

두번째로 hostPath인데 hostPath는 노드내에 볼륨을 둠으로써 서로 다른 파드가 떠있다고 할지라도 hostPath를 통해 데이터를 공유할 수 있습니다.

위 그림과 같이 서로다른 파드의 데이터를 hostPath에 저장할수 있습니다. 그러나 파드가 떠있는 노드가 달라질 경우 hostPath를 사용할 수는 없습니다.

2.3. PersistentVolume(PV)

PV는 외부 스토리지 시스템에 볼륨을 둠으로써 서로다른 노드에 떠있는 파드들의 데이터를 관리할 수 있는 방법입니다.

위 그림을 보면 서로 다른 노드에 떠있는 파드 속 컨테이너 데이터를 관리 할 수 있는 것을 알 수 있습니다.

3. PV?, PVC?

PV는 Persistent Volume의 약자로 볼륨 그 자체를 의미합니다. 즉, PV는 파드와는 별개로 존재합니다. PVC는 Persistent Volume Claim의 약자로 PV에 보내는 요청입니다. PVC는 사용하고 싶은 용량, 모드(읽기/쓰기) 설정 등을 적어서 PV에 요청을 보냅니다. 즉, 파드는 PV에 다이렉트로 요청하는 것이 아니라 중간에 PVC를 거쳐서 요청하게 됨으로써 의존성이 줄어들게 됩니다.

4. hostPath 실습

4.0. 준비물

컨테이너를 만들 이미지가 준비되어 있어야합니다. 여기서는 nginx-lipaco, flask-lipaco 이미지가 필요합니당

4.1. yml 파일 생성

hostPath는 앞서 언급한데로 노드 내부에 볼륨을 둠으로써 파드들의 데이터를 관리합니다.

그럼 지금부터 위 그림과 같이 hostPath를 만들고 파드와 연결시키는 실습을 해보겠습니다. 위와 같은 구조를 만들기 위해 우리가 작성해야하는 파일은 pv.ymlpvc.yml파일 입니다. 그리고 볼륨과 파드를 연결시키기 위해 deployment.yml파일도 수정합니다.

그리고 최종적으로는 아래 그림과 같이 작동 됩니다.

그럼 pv.yml 파일부터 작성해보겠습니다. 다음 코드는 pv.yml 파일의 코드입니다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: "data1"
  labels:
    name: pv-data
spec:
  accessModes:
    - "ReadWriteOnce"
  capacity:
    storage: 2Gi
  hostPath:
    path: /tmp/k8s-pv

다음으로는 pvc.yml파일을 작성해보겠습니다. 다음 코드는 pvc.yml파일의 코드입니다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: lipaco-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 2G
  storageClassName: ""
  selector:
    matchLabels:
      name: pv-data

마지막으로 볼륨과 연결하기 위해 기존에 있던 deployment.yml파일을 다음과 같이 수정하겠습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      volumes:
        - name: hostpath-pv
          persistentVolumeClaim:
            claimName: lipaco-pvc
      containers:
        - name: nginx-lipaco
          image: living_paradise_nginx
          imagePullPolicy: Never
          ports:
            - containerPort: 8004
        - name: flask-lipaco
          image: living_paradise_predict_product_demand
          imagePullPolicy: Never
          volumeMounts:
            - name: hostpath-pv
              mountPath: /mnt
          ports:
            - containerPort: 5004

위 코드에서 수정 된 부분은 다음과 같습니다.

4.2. pv.yml 작동 시킴

위에서 작성한 파일을 동작시켜보겠습니다.

가장 먼저 pv.yml을 작동시킵니다.

$ kubectl apply -f pv.yml
persistentvolume/data1 created

pv가 제대로 생성되었는지 확인해보겠습니다. 다음 결과를 보면 제대로 생성된 것을 알 수 있습니다.

$ kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
data1   2Gi        RWO            Retain           Bound    default/lipaco-pvc                           9s

4.3. pvc.yml 작동 시킴

다음으로는 pvc.yml을 작동시킵니다.

$ kubectl apply -f pvc.yml
persistentvolumeclaim/lipaco-pvc created

그리고 pvc가 제대로 생성되었는지 확인해보겠습니다.

$ kubectl get pvc
NAME         STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
lipaco-pvc   Bound    data1    2Gi        RWO                           9s

참고로 다음 커맨드와 같이 pv와 pvc를 한번에 확인할 수 있습니다.

$ kubectl get pv,pvc
NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
persistentvolume/data1   2Gi        RWO            Retain           Bound    default/lipaco-pvc                           29h

NAME                               STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/lipaco-pvc   Bound    data1    2Gi        RWO                           29h

4.4. deployment.yml 작동 시킴

다음으로는 deployment.yml을 실행시켜 파드를 올리고 볼륨과 연결시킵니다.

$ kubectl apply -f deployment.yml
deployment.apps/web-deploy created

파드가 잘 돌아가는지 확인해봅시다.

$ kubectl get pod
NAME                          READY   STATUS    RESTARTS   AGE
web-deploy-6c594786ff-cnb7k   2/2     Running   0          4s
web-deploy-6c594786ff-l297d   2/2     Running   0          4s
web-deploy-6c594786ff-rw4mq   2/2     Running   0          4s

4.5. service.yml 작동 시킴

마지막으로 service.yml을 실행해 로드밸런서를 작동시킵니다.

$ kubectl apply -f service.yml
service/web-service created

다음 코드로 서비스가 잘 작동하는지 확인해보겠습니다.

$ kubectl get service
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes    ClusterIP      10.96.0.1       <none>        443/TCP        49d
web-service   LoadBalancer   10.97.129.167   <pending>     80:30004/TCP   4s

그리고 다음과 같이 접속을 해보면 잘 되는 것을 볼 수 있습니다.

부록1. 작동 정지 시키는 법

작동 중인 친구들을 정지 시키고 싶다면 다음과 같이 입력합시다. (마지막 pv.yml은 입력안해도 될듯)

$ kubectl delete -f service.yml
$ kubectl delete -f deployment.yml
$ kubectl delete -f pvc.yml
$ kubectl delete -f pv.yml

부록2. pv 삭제하고 싶을때(pv terminating 문제 해결)

pv를 삭제하기 위해 다음 커맨드를 실행했습니다.

$ kubectl delete -f pv.yml
persistentvolume "data1" deleted

그런데 종료가 되지 않는 것이었습니다. 이게뭐지 싶어서 확인을 해보니 다음 코드와 같이 Terminating 이라는 상태만 뜨고 종료되지 않았습니다.

$ kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS        CLAIM                STORAGECLASS   REASON   AGE
data1   2Gi        RWO            Retain           Terminating   default/lipaco-pvc                           5d22h

저는 pv를 완전히 삭제하고 싶었는데 이는 다음과 같은 명령어를 사용하면 됩니다.

$ kubectl patch pv <pv_name> -p '{"metadata": {"finalizers": null}}'
$ kubectl delete pv <pv_name> --grace-period=0 --force

제가 만든 pv의 이름은 data1 이므로 다음과 같이 입력합니다.

$ kubectl patch pv data1 -p '{"metadata": {"finalizers": null}}'
persistentvolume/data1 patched
$ kubectl delete pv data1 --grace-period=0 --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
Error from server (NotFound): persistentvolumes "data1" not found

그리고 나서 pv를 확인하면 사라진 것을 볼수 있습니다.

$ kubectl get pv
No resources found

부록3. 파드 속 컨테이너 확인법

$ Kubectl get pods.
$ Kubectl describe pod.
$ Kubectl logs [-f] POD [-c CONTAINER]
$ Kubectl top pod POD_NAME --containers.