[Kubernetes] Resource Request & Limit (리소스 요청과 제한)
🚀 Resource Request & Limit
Docker에서 --cpu
, --memory
옵션을 통해 컨테이너가 실제로 사용할 수 있는 리소스 양을 제한했던 것과 같은 기능을 쿠버네티스에서도 할 수 있다.
파드를 생성할 때 별도로 제한을 걸지 않으면 호스트에 있는 모든 CPU, Memory 자원을 독점해서 사용할 수 있다.
그래서 반드시 적절하게 용량을 지정할 필요가 있다.
- 요청 : request
- 제한 : limit
파드에서 요청이나 제한 또는 모두를 설정할 수 있다. 요청과 제한 모두를 설정하는 것을 권장한다.
🚀 Request (요청)
요청이라는 것은 파드가 리소스를 사용할 때 특정 용량을 요청하는 것이다.
ex) cpu : 1개 memory : 1G 사용할 것이다.
요청을 하게되면 스케줄러(정확하게는 컨테이너를 제어하는 kubelet
)이 요청한 만큼을 사용할 수 있도록 보장해준다.
파드가 요청한 양을 모두 사용하던 적게 사용하던 상관없다.
kubelet
은 요청한 만큼의 용량을 확보해서 파드에게 제공한다.
만약 호스트가 가진 memory가 3G라고 하고 1번 파드가 memory를 1G 요청하고 2번 파드도 1G를 요청했다고 해보자kubelet
은 요청한 만큼을 확보시켜 준다.
필요한 다른 프로세스를 실행하는 것은 고려하지 않고 순수하게 파드만 생각해보면 남은 공간은 1G이다.
만약 3번째 파드가 memory를 1.5G 요청한다면 리소스 용량이 남아있지 않아 할당해줄 수 없다.
그러면 파드의 요청은 실행될 수 없다.
즉, 요청이라고 하는 것은 호스트에게 나에게 요청량만큼을 확보해달라고 하는 것이다.
🚀 Limit (제한)
요청과 제한은 같을 수 있다. 또는 요청보다 제한이 더 클 수 있다.
제한이라고 하는 것은 예를 들자면 파드는 memory 1G를 요청했고 2G로 제한을 걸었을 때
최소한 memory 3G 중에서 1G는 확보해서 무조건 할당해주고 최대 2G까지 늘려서 쓸 수 있다는 것이다.
우선 기본적으로 파드가 start할 때는 1G만큼을 무조건 확보해서 제공한다.
하지만 (제한 - 요청)만큼의 용량은 미리 확보해놓지 않는다.
최대 2G까지 사용할 수 있다는 것이지 실제로 그 용량을 사용할 수 있는가의 여부는 다른 파드의 요청량과 연관이 있다.
vagrant@k8s-node1 ~ kubectl describe pod
QoS Class: BestEffort
QoS(Quality of Service) Class
BestEffort
: 가장 나쁨Burstable
Guaranteed
: 가장 좋음
- 요청/제한이 설정되어 있지 않으면 :
BestEffort
- 요청 < 제한 :
Burstable
- 요청 = 제한 :
Guranteed
만약 특정 호스트에 파드가 여러개 떠있다면 cpu, memory 자원을 경합하게 될 것이다.
경합시 우선순위는 Guranteed
가 제일 높다. 그다음이 Burstable
이며 BestEffort
가 가장 마지막이다.
그래서 BestEffort
는 실제로 리소스를 아예 할당 못받는 경우도 있다.
더 심하게는 RS이 있을 때 삭제되는 제일 첫번째 대상이 BestEffort
이다.
리소스가 더 이상없어서 누군가가 죽어야 한다면 그것이 BestEffort
가 된다.
이름 그대로 Guranteed
는 항상 실행하는 것을 보장한다.Guranteed
로 설정되어 있는 것이 너무 많으면 가장 최근에 생성된 것은 또 안될 수도 있다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl explain pod.spec.containers.resources
- request : 용량을 지정한다. map 형식을 사용한다.
- cpu
- memory
- limits
- cpu
- memory
CPU 요청 & 제한 : milicore(m)
단위 사용
ex) 1500m -> cpu 1.5개, 1000m -> cpu 1개 (정확히는 core 1개)
ex) 1.5, .0.1
Memory 요청 & 제한 : M, G, T, Mi, Gi, Ti
myweb-reqlit.yaml
apiVersion: v1
kind: Pod
metadata:
name: myweb-reqlit
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
resources:
requests:
cpu: 200m
memory: 200M
limits:
cpu: 200m
memory: 200M
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl get po
NAME READY STATUS RESTARTS
myweb-reqlit 1/1 Running 0 4s
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl describe pod myweb-reqlit
QoS Class: Guaranteed
값이 같으면 Guaranteed
이다.
resources:
requests:
cpu: 100m
memory: 100M
limits:
cpu: 200m
memory: 200M
limits
가 더 큰 경우 기본적으로 requests
와 limits
값은 수정될 수 없다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl replace -f myweb-reqlim.yaml --force
pod "myweb-reqlit" deleted
pod/myweb-reqlit replaced
--force
옵션을 붙이면 가능한데 기존의 것을 삭제하고 새로 만든다.
📌 만약 특정 필드를 수정하고 싶은데 수정이 안될 때
kubectl explain pod.spec.containers.resources
KIND: Pod
VERSION: v1
RESOURCE: resources <Object>
DESCRIPTION:
Compute Resources required by this container. Cannot be updated.
Cannot be updated.
라고 되어있으면 기본적으로 수정할 수 없다.--force
옵션을 붙이면 삭제했다가 다시 만들며 엄밀히 update
는 아니다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl describe pod myweb-reqlit
QoS Class: Burstable
Burstable
는 requests
한 만큼 자유자재로 사용하다가 최대치는 limits
까지 올라갈 수 있음을 의미한다.
물론 다른 파드들이 requests
로 확보하고 있는 경우에는 못 쓸수도 있다.
더 이상 할당할 수 있는 양이 없으면 Burst
할 수 없다.
apiVersion: v1
kind: Pod
metadata:
name: myweb-reqlit
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
resources:
requests:
memory: 100M
limits:
memory: 200M
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl describe pod myweb-reqlit
QoS Class: Burstable
cpu 값을 지워도 Burstable
이다.
cpu와 memory 둘 중에 하나라도 세팅되어 있으면 Burstable
로 작동한다.
apiVersion: v1
kind: Pod
metadata:
name: myweb-reqlit
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl describe pod myweb-reqlit
QoS Class: BestEffort
requests
, limits
어떠한 설정도 가지지 않으면 BestEffort
로 작동한다.
나중에 리소스에 대한 경합이 있을 때 리소스를 아예 받지 못하거나 복제본 컨트롤러에서 아예 삭제할 수 있다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
node1 151m 8% 1922Mi 59%
node2 100m 5% 1291Mi 50%
node3 95m 5% 1375Mi 53%
top
명령을 사용하면 각 노드가 사용하는 리소스 양을 볼 수 있다.
나는 VM을 생성할 때 cpu : 2개, node1의 memory : 4G node2, 3의 memory : 3G로 세팅했다.
cpu가 2개라는 것은 2000m를 의미하며 현재 node1이 사용하는 양인 151m가 약 7% 정도여야 하지만
메모리에서 커널이 확보해서 사용하는 영역을 제외하고 나머지 영역에 대해서만 계산하기 때문에
전체 용량 중 8%를 말하는 것은 아니다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl get po -A -o wide | grep node1 | wc -l
12
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl get po -A -o wide | grep node2 | wc -l
12
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl get po -A -o wide | grep node3 | wc -l
11
노드마다 떠 있는 파드 개수를 확인할 수 있다.
파드의 개수가 많을 수록 리소스를 더 많이 사용하는 경향을 띈다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl describe node node1
...
Capacity:
cpu: 2
ephemeral-storage: 40593612Ki
hugepages-2Mi: 0
memory: 3927804Ki
pods: 110 # 노드 당 파드 개수는 110개로 제한
Allocatable:
cpu: 1800m
ephemeral-storage: 37411072758
hugepages-2Mi: 0
memory: 3301116Ki
pods: 110
Non-terminated Pods: (12 in total)
Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits Age
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 1420m (78%) 400m (22%)
memory 835859200 (24%) 1066231040 (31%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
describe
로 노드 정보를 확인하면 Capacity
와 Allocatable
: 할당할 수 있는 양,
Non-terminated Pods
: 실행 중인 파드들
각 파드의 NS와 이름 및 각 파드들의 요청과 제한량을 확인할 수 있다.
0으로 세팅되어 있는 것은 요청과 제한량이 세팅되어 있지 않은 것이며 BestEffort
타입이다.
Allocated resources
는 총 cpu의 요청, 제한량 memory의 요청, 제한량을 볼 수 있다.
Requests
는 실제로 사용하는 양과 상관없이 미리 확보해놓은 양이므로 스케줄러에의해 할당할 수 없는 영역이다.
ephemeral-storage
: emptyDir
, configMap
, Secret
와 같은 임시 스토리지 공간을 의미한다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl top pods -A
NAMESPACE NAME CPU(cores) MEMORY(bytes)
-A
옵션을 붙이면 모든 파드들의 cpu, memory 사용량을 볼 수 있다.
kubectl top
명령으로 사용량을 볼 수 있는 이유는 add-on
인 metrics-server
에 의한 것이다.
해당 add-on
이 없을 때 kubectl top
명령을 실행하면 오류가 발생한다.
myweb-big.yaml
apiVersion: v1
kind: Pod
metadata:
name: myweb-reqlit
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
resources:
limit bnhs:
cpu: 3000m
memory: 4000M
너무 큰 리소스를 요구하여 실행할 수 없는 리소스
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl describe pod myweb-big
Containers:
myweb:
Image: ghcr.io/c1t1d0s7/go-myweb
Port: <none>
Host Port: <none>
Limits:
cpu: 3
memory: 4G
Requests:
cpu: 3
memory: 4G
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2s default-scheduler 0/3 nodes are available: 3 Insufficient cpu, 3 Insufficient memory.
limits
만 설정하면 limits
양과 똑같은 requests
가 자동으로 설정된다.
하지만 requests
만 설정하는 경우 limits
는 설정되지 않는다.
Events
를 보면 FailedScheduling
이다.
3개의 노드 중에서 아무것도 가능하지 않다. 즉, 스케줄링 가능한 노드가 하나도 없다는 것이다.
3개의 노드 모두 cpu, memory가 부족하다. 호스트가 가진 양보다 더 많기 때문에 당연히 할당할 수 없다.
vagrant@k8s-node1 ~/autoscaling/reqlim kubectl get po
NAME READY STATUS RESTARTS AGE
myweb-big 0/1 Pending 0 2m15s
Pending
상태임을 확인할 수 있다.
파드의 라이프 사이클에서 Pending
라는 의미는
- 이미지를 받고 있거나
- 볼륨을 연결하는 중이거나
- 스케줄링 되기 전
순서대로 보면 스케줄링되고 볼륨을 찾았을 때 볼륨이 있어야 하며 그다음 이미지를 받는다.
Pending
상태가 너무 오랫동안 지속되면 다음 세가지 중에 하나를 의심해봐야 한다.