Kubernetes에는 데이터를 보관하기 위한 볼륨으로 다양한 방안들이 있지만 이 중 Pod와 생명주기가 동일한 경우
즉, Pod가 죽을때 데이터가 휘발돼도 문제없는 경우 ehemeral-storage(임시 볼륨)를 많이 쓴다.
기본적으로 pod는 볼륨 마운트 설정이 없으면 모든 파일은 임시 볼륨을 사용한다.
볼륨 마운트 설정이 없는 pod에 들어가서 파일들을 만든다면 해당 pod의 워커노드에 용량을 쓴다는 얘기이다.
서비스의 성격에 따라 다르지만, 서비스에서 임시 파일을 만들며, 삭제돼도 상관없다면 임시 볼륨을 써도 된다.
Ephemeral Storage by ubuntu
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을 설정하는게 좋아보인다.