컨테이너와 파드에 CPU 리소스 할당하기

컨테이너와 파드에 CPU 리소스 할당하기

쿠버네티스에서 컨테이너가 쓸 수 있는 CPU는 선언에 따라 정해져요. 이 페이지에서는 컨테이너에 CPU request(요청)와 CPU limit(상한)을 어떻게 지정하는지, 그리고 그 값이 실제 스케줄링과 실행에 어떤 영향을 주는지를 한 단계씩 실습해 볼게요. 컨테이너는 설정된 상한보다 더 많은 CPU를 쓸 수 없고, 시스템에 CPU 여유가 있다면 요청한 만큼은 확실히 배정받아요.

출처: Assign CPU Resources to Containers and Pods — Kubernetes 공식 문서

본문

사전 준비

이 실습을 따라가려면 클러스터에 최소 1 CPU의 여유가 필요해요. 몇몇 단계는 metrics-server 서비스가 클러스터에 떠 있어야 실행할 수 있는데, 이미 돌고 있다면 그 단계는 건너뛰어도 돼요. minikube를 쓰고 있다면 아래 명령으로 metrics-server를 켜요.

minikube addons enable metrics-server

metrics-server(혹은 리소스 메트릭 API인 metrics.k8s.io를 제공하는 다른 프로바이더)가 실행 중인지 확인하려면 다음처럼 확인해요.

kubectl get apiservices

리소스 메트릭 API를 쓸 수 있다면 출력에 metrics.k8s.io가 보일 거예요.

NAME
v1beta1.metrics.k8s.io

네임스페이스 만들기

이 실습에서 만드는 리소스가 나머지 클러스터와 격리되도록 네임스페이스를 하나 만들어 둘게요.

kubectl create namespace cpu-example

CPU request와 CPU limit 지정하기

컨테이너의 리소스 매니페스트에 resources.requests.cpu 필드를 넣으면 CPU request를, resources.limits.cpu를 넣으면 CPU limit을 지정할 수 있어요. 이번 실습에서는 컨테이너 하나를 가진 Pod를 만들 건데, CPU request는 0.5, limit은 1 CPU로 잡아 볼게요.

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

매니페스트의 args 부분은 컨테이너가 시작할 때 전달할 인자를 정해요. -cpus "2" 인자는 컨테이너가 CPU 2개를 쓰려고 시도하라는 뜻이에요. 이제 Pod를 만들고, 잘 떠 있는지 확인해 봐요.

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit.yaml --namespace=cpu-example
kubectl get pod cpu-demo --namespace=cpu-example

Pod의 상세 정보를 보려면 이렇게 조회해요.

kubectl get pod cpu-demo --output=yaml --namespace=cpu-example

출력에서 이 Pod의 컨테이너 하나가 CPU request 500 milliCPU, CPU limit 1 CPU를 갖고 있다는 걸 확인할 수 있어요.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 500m

kubectl top으로 이 Pod의 메트릭을 가져와 볼게요.

kubectl top pod cpu-demo --namespace=cpu-example

이 예제 출력을 보면 Pod가 974 milliCPU를 쓰고 있어요. 앞서 설정한 1 CPU 상한보다는 살짝 아래인 값이죠.

NAME                        CPU(cores)   MEMORY(bytes)
cpu-demo                    974m         <something>

-cpu "2"로 2 CPU를 쓰도록 지정했지만, 컨테이너는 실제로 약 1 CPU밖에 못 쓴다는 걸 기억하세요. 컨테이너가 상한보다 더 많은 CPU를 쓰려고 해서 CPU 사용량이 제한(throttling)되고 있는 거예요.

참고 CPU 사용량이 1.0 아래인 데는 또 다른 이유가 있을 수 있어요. 노드에 충분한 CPU 리소스가 없을 수도 있거든요. 이 실습의 전제 조건이 클러스터에 최소 1 CPU가 필요하다는 것이었죠. 컨테이너가 CPU가 1개뿐인 노드에서 실행된다면, 컨테이너에 지정된 CPU limit이 아무리 커도 1 CPU를 넘어 쓸 수는 없어요.

CPU 단위

CPU 리소스는 CPU 단위로 측정해요. 쿠버네티스에서 1 CPU는 다음과 같아요.

  • AWS vCPU 1개
  • GCP Core 1개
  • Azure vCore 1개
  • 하이퍼스레딩을 지원하는 베어메탈 Intel 프로세서에서 하이퍼스레드 1개

소수 값도 허용돼요. 0.5 CPU를 요청한 컨테이너는 1 CPU를 요청한 컨테이너보다 정확히 절반만 보장받아요. 접미사 m은 milli를 뜻하는데, 100m CPU와 100 milliCPU, 0.1 CPU는 모두 같은 값이에요. 1m보다 더 정밀한 값은 허용되지 않아요.

CPU는 항상 상대적인 양이 아니라 절대적인 양으로 요청돼요. 0.1은 싱글코어든 듀얼코어든 48코어 머신이든 항상 같은 CPU 양이에요.

kubectl delete pod cpu-demo --namespace=cpu-example

노드 용량보다 큰 CPU request 지정하기

CPU request와 limit은 컨테이너에 붙는 값이지만, Pod 단위로 생각하면 편해요. Pod의 CPU request는 그 Pod 안 모든 컨테이너의 CPU request 합이고, CPU limit도 마찬가지로 모든 컨테이너의 CPU limit 합이에요.

Pod 스케줄링은 request를 기준으로 해요. Pod는 노드가 그 Pod의 CPU request를 충족할 만큼 CPU 여유가 있을 때만 그 노드에 스케줄링돼요. 이번에는 클러스터 어느 노드의 용량도 초과하는 CPU request를 가진 Pod를 만들어 볼게요. 컨테이너 하나가 100 CPU를 요청하는데, 이 값은 보통 클러스터의 어떤 노드 용량도 넘어서기 쉬워요.

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo-2
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr-2
    image: vish/stress
    resources:
      limits:
        cpu: "100"
      requests:
        cpu: "100"
    args:
    - -cpus
    - "2"
kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit-2.yaml --namespace=cpu-example

Pod 상태를 확인해 볼게요.

kubectl get pod cpu-demo-2 --namespace=cpu-example

출력을 보면 Pod 상태가 Pending이에요. 즉 아직 어떤 노드에도 스케줄링되지 않았고, 앞으로도 Pending 상태로 남게 돼요.

NAME         READY     STATUS    RESTARTS   AGE
cpu-demo-2   0/1       Pending   0          7m

이벤트를 포함한 Pod 상세 정보를 보면, 노드의 CPU 리소스가 충분하지 않아 컨테이너를 스케줄링할 수 없다는 메시지가 나와요.

kubectl describe pod cpu-demo-2 --namespace=cpu-example
Events:
  Reason                        Message
  ------                        -------
  FailedScheduling      No nodes are available that match all of the following predicates:: Insufficient cpu (3).
kubectl delete pod cpu-demo-2 --namespace=cpu-example

CPU limit을 지정하지 않으면

컨테이너에 CPU limit을 지정하지 않으면 다음 두 가지 상황 중 하나가 적용돼요.

  • 컨테이너가 쓸 수 있는 CPU 리소스에 상한이 없어져요. 실행 중인 노드의 모든 CPU 리소스를 다 쓸 수도 있죠.
  • 컨테이너가 기본 CPU limit이 있는 네임스페이스에서 실행된다면, 자동으로 그 기본 limit이 지정돼요. 클러스터 관리자는 LimitRange로 CPU limit의 기본값을 지정할 수 있어요.

CPU limit은 지정했지만 CPU request는 지정하지 않으면

컨테이너에 CPU limit을 지정했지만 request는 지정하지 않으면, 쿠버네티스가 자동으로 limit과 같은 값의 CPU request를 할당해요. 마찬가지로 컨테이너가 memory limit을 지정하면서 memory request를 지정하지 않으면, 쿠버네티스가 limit과 같은 memory request를 자동으로 할당하죠.

CPU request와 limit이 필요한 이유

클러스터에서 실행하는 컨테이너들의 CPU request와 limit을 잘 설정하면, 노드에 있는 CPU 리소스를 효율적으로 쓸 수 있어요. Pod의 CPU request를 낮게 유지하면 스케줄링될 가능성이 높아지고, CPU limit을 request보다 크게 잡으면 두 가지를 동시에 얻을 수 있어요.

  • Pod가 여유 있는 CPU 리소스를 활용하는 폭주(burst) 구간을 가질 수 있어요.
  • 폭주 중 쓸 수 있는 CPU 양이 합리적인 수준으로 제한돼요.

정리하기

kubectl delete namespace cpu-example

더 알아보기