[k8s] GKE Ephemeral Storage 분석


Kubernetes에는 데이터를 보관하기 위한 볼륨으로 다양한 방안들이 있지만 이 중 Pod와 생명주기가 동일한 경우

즉, Pod가 죽을때 데이터가 휘발돼도 문제없는 경우 ehemeral-storage(임시 볼륨)를 많이 쓴다.

기본적으로 pod는 볼륨 마운트 설정이 없으면 모든 파일은 임시 볼륨을 사용한다.

볼륨 마운트 설정이 없는 pod에 들어가서 파일들을 만든다면 해당 pod의 워커노드에 용량을 쓴다는 얘기이다.

서비스의 성격에 따라 다르지만, 서비스에서 임시 파일을 만들며, 삭제돼도 상관없다면 임시 볼륨을 써도 된다.




ubuntu.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ubuntu
  template:
    metadata:
      labels:
        app: ubuntu
    spec:
      nodeSelector:
        kubernetes.io/hostname: gke-default-pool-2-ecc8f5c5-j3s3
      containers:
      - name: ubuntu
        image: registry.gitlab.com/gbe0/utility-containers/ubuntu-base:latest
        command:
          - sleep
          - "3600000"

$ kubectl apply -f ubuntu.yaml
$ kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE                                                NOMINATED NODE   READINESS GATES
ubuntu-b4b4c77b9-wrwhh   1/1     Running   0          3m48s   10.16.4.162   gke-default-pool-2-ecc8f5c5-j3s3   <none>           <none>

위의 ubuntu deployment를 간단하게 작성한 후 해당 pod가 특정 worker node에 떴는지 확인해본다.

이렇게 세팅을 한 다음 테스트를 해보려고 한다.

$ kubectl exec -it pod/ubuntu-b4b4c77b9-wrwhh -- /bin/bash

root@ubuntu-b4b4c77b9-wrwhh:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   32G   63G  34% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   32G   63G  34% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

실제 pod에 들어가서 용량을 살펴보면 Worker Node의 Root Disk(100GB)를 사용하는 것을 볼 수 있다.

그러면 만약 Worker Node의 Root Disk 이상을 사용하게 되면 어떤 상황이 벌어질까?

이를 테스트 하기 위해서는 다른 Pod를 하나 띄우면 더 테스트하기가 편하다.

ubuntu-attack.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu-attack
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ubuntu-attack
  template:
    metadata:
      labels:
        app: ubuntu-attack
    spec:
      nodeSelector:
        kubernetes.io/hostname: gke-default-pool-2-ecc8f5c5-j3s3
      containers:
      - name: ubuntu-attack
        image: registry.gitlab.com/gbe0/utility-containers/ubuntu-base:latest
        command:
          - sleep
          - "3600000"

$ kubectl apply -f ubuntu-attack.yaml
$ kubectl get pod -o wide
NAME                             READY   STATUS              RESTARTS   AGE   IP            NODE                                                NOMINATED NODE   READINESS GATES
ubuntu-attack-6b546769bd-8xmbb   0/1     ContainerCreating   0          4s    <none>        gke-dev-apps-cluster-default-pool-2-ecc8f5c5-j3s3   <none>           <none>
ubuntu-b4b4c77b9-wrwhh           1/1     Running             0          16m   10.16.4.162   gke-dev-apps-cluster-default-pool-2-ecc8f5c5-j3s3   <none>           <none>

이렇게 attack이라는 pod를 만들어 주고 여기서 사이즈가 큰 파일들을 만들어봐야겠다.


$ kubectl exec -it pod/ubuntu-attack-6b546769bd-8xmbb  -- /bin/bash

root@ubuntu-attack-6b546769bd-8xmbb:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   33G   63G  34% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   33G   63G  34% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

root@ubuntu-attack-6b546769bd-8xmbb:/# fallocate -l 10G test10.txt

root@ubuntu-attack-6b546769bd-8xmbb:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   43G   53G  45% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   43G   53G  45% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

fallocate 명령어로 간단한게 10G 용량의 더미파일을 생성했을때 정상적으로 만들어진다.

자 그러면 이 상황에서 50GB짜리 파일을 만들어보겠다.

root@ubuntu-attack-6b546769bd-8xmbb:/# fallocate -l 50G test50.txt

root@ubuntu-attack-6b546769bd-8xmbb:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   93G  2.2G  98% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   93G  2.2G  98% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

root@ubuntu-attack-6b546769bd-8xmbb:/# fallocate -l 2G test2.txt

root@ubuntu-attack-6b546769bd-8xmbb:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   87G  8.1G  92% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   87G  8.1G  92% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

용량을 100%로 만들자 마자 Worker Node에서 불필요한 파일들을 삭제하여 8GB를 긴급하게 만들어 냈다.

그 후에는 Pod에 연결이 끊기고 pod들의 상태를 확인해봤다.



$ kubectl get pod
NAME                             READY   STATUS                   RESTARTS   AGE
ubuntu-attack-6b546769bd-8xmbb   0/1     ContainerStatusUnknown   1          9m (<- 기존 pod)
ubuntu-attack-6b546769bd-gkqs9   0/1     Pending                  0          1m52s (<- deployment로 인해 새로 생성된 pod)
ubuntu-b4b4c77b9-wrwhh           1/1     Running                  0          25m

$ kubectl describe pod ubuntu-attack-6b546769bd-8xmbb
  Warning  Evicted    53s    kubelet                                The node was low on resource: ephemeral-storage. Threshold quantity: 10120387530, available: 8409784Ki. Container ubuntu-attack was using 65011800Ki, request is 0, has larger consumption of ephemeral-storage.
  Normal   Killing    53s    kubelet                                Stopping container ubuntu-attack

여기서 기존 pod는 노드의 임시용량이 적기에 임시용량을 많이 쓰고 있는 pod부터 Evicted시킨 것으로 보인다.

그렇다면 왜 새로 생성된 pod는 Pending 상태일까?



$ kubectl describe pod/ubuntu-attack-6b546769bd-gkqs9
  Normal   NotTriggerScaleUp  36s                cluster-autoscaler                     pod didn't trigger scale-up (it wouldn't fit if a new node is added): 1 node(s) had untolerated taint {pool: ops-node-pool}, 3 node(s) didn't match Pod's node affinity/selector
  Warning  FailedScheduling   35s (x2 over 36s)  gke.io/optimize-utilization-scheduler  0/12 nodes are available: 1 node(s) had untolerated taint {node.kubernetes.io/disk-pressure: }, 1 node(s) had untolerated taint {pool: test-node-pool}, 10 node(s) didn't match Pod's node affinity/selector. preemption: 0/12 nodes are available: 12 Preemption is not helpful for scheduling.

지금 모든 pod들은 특정 워커노드에만 뜨게 하였는데, 임시용량이 순간 꽉찬 워커노드는 disk-pressure 상태로 들어가기에 새로운 pod들 배포하지 않게 된다.

그렇다면 해당 워커노드에 기동되어있던 다른 pod들은 어떨까?




$ kubectl get pod
NAME                             READY   STATUS                   RESTARTS   AGE
ubuntu-attack-6b546769bd-8xmbb   0/1     ContainerStatusUnknown   1          11m
ubuntu-attack-6b546769bd-gkqs9   0/1     Pending                  0          3m52s
ubuntu-b4b4c77b9-wrwhh           1/1     Running                  0          27m


$ kubectl exec -it pod/ubuntu-b4b4c77b9-wrwhh -- /bin/bash
root@ubuntu-b4b4c77b9-wrwhh:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   25G   70G  27% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   25G   70G  27% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

root@ubuntu-b4b4c77b9-wrwhh:/# fallocate -l 10G test1

root@ubuntu-b4b4c77b9-wrwhh:/# df -Th
Filesystem     Type     Size  Used Avail Use% Mounted on
overlay        overlay   95G   33G   62G  35% /
tmpfs          tmpfs     64M     0   64M   0% /dev
/dev/sda1      ext4      95G   33G   62G  35% /etc/hosts
shm            tmpfs     64M     0   64M   0% /dev/shm
tmpfs          tmpfs     28G   12K   28G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs          tmpfs     16G     0   16G   0% /proc/acpi
tmpfs          tmpfs     16G     0   16G   0% /proc/scsi
tmpfs          tmpfs     16G     0   16G   0% /sys/firmware

일단 문제가 되던 Pod가 Evicted 됐기에 워커 노드의 임시 용량은 어느정도 정리가 된 것으로 보인다.

이 경우에 다른 Pod들은 접속 및 파일 쓰기까지는 문제가 없지만 어플리케이션 자체에 영향이 있는지는 보아야한다.


$ kubectl get pod
NAME                             READY   STATUS                   RESTARTS   AGE
ubuntu-attack-6b546769bd-8xmbb   0/1     ContainerStatusUnknown   1          23m
ubuntu-attack-6b546769bd-gkqs9   1/1     Running                  0          15m
ubuntu-b4b4c77b9-wrwhh           1/1     Running                  0          39m

한 5분정도 지나면 disk-pressure가 끝나는지 Pending된 pod가 정상적으로 Running이 되는 것을 볼 수 있다.

Pod가 Evicted되거나 ContainerStatusUnknown면 이들은 수동으로 지워줘야한다.




pod는 아무런 설정을 안할 경우 워커노드의 임시볼륨(ephemeral-storage)을 사용하며, 한계까지 사용한다면 해당 pod는 Evicted 된다.

그 후에 워커노드는 Disk Pressure 상태가 되며, 추가적인 pod의 배포를 금지하지만 기존에 떠있던 pod에는 영향이 없어보인다.(추측)

5분정도의 시간이 지나면 Disk Pressure 상태가 풀리며, pod의 배포가 정상적으로 되고, Evicted된 pod의 잔재는 수동으로 삭제해야한다.

지금까지 했던 테스트는 1개의 pod에 1개의 Container만 기동된다는 가정이였고, 만약 1개의 pod에 다수의 서비스 Containter가 기동되고, 이들이 서로 공유할 수 있는 볼륨을 필요로 한다면 emptyDir를 사용하는게 맞지 않을까 싶다.

이 모든건 휘발성 데이터인거를 전제로 하며, 워커노드의 Disk Pressure 상태도 그렇게 좋은 상태는 아니기에 ephemeral-storage 볼륨의 limit을 설정하는게 좋아보인다.

Leave a Comment