DevOps/Kubernetes

[Kubernetes] Resource Request & Limit (리소스 요청과 제한)

TTOII 2022. 6. 4. 12:21
728x90

🚀 Resource Request & Limit

파드 및 컨테이너 리소스 관리 | Kubernetes

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

  1. BestEffort : 가장 나쁨
  2. Burstable 
  3. 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가 더 큰 경우 기본적으로 requestslimits값은 수정될 수 없다.

 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

Burstablerequests한 만큼 자유자재로 사용하다가 최대치는 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로 노드 정보를 확인하면 CapacityAllocatable : 할당할 수 있는 양,

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-onmetrics-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 상태가 너무 오랫동안 지속되면 다음 세가지 중에 하나를 의심해봐야 한다.

728x90