Dev&Ops/DevOps

Karpenter와 Empty Pod을 활용한 스케일링(2)

zeroneCoder 2023. 10. 24. 11:22


안녕하세요! zerone-code입니다.

지난 Karpenter와 Empty Pod을 활용한 스케일링(1)에 이어 쿠버네티스에서 제공하는 PriorityClass와 빈 파드(Empty Pod), 그리고 카펜터(Karpenter)를 활용한 오버프로비저닝(Overprovisioning)에 대해 공유하고자 합니다. 이번 글에서는 PriorityClass와 빈 파드를 중점적으로 다루고 카펜터가 어떻게 활용되는지 알아보도록 하겠습니다.

궁금한 부분은 댓글로 남겨주시면 아는 만큼 성심성의껏 답변해 드리도록 하겠습니다.

잘 읽으셨다면 많은 공감과 구독 부탁드립니다!


오버프로비저닝이란?

먼저 그림들을 통해 오버프로비저닝이 어떤 개념인지 알아보도록 하겠습니다.

보통 EKS 환경에서 사용자 요청이 늘어나 CPU나 메모리 사용률이 올라가면, HPA(Horizontal Pod Autoscaling)가 파드를 증설합니다. (그림 1) 이때 기존에 존재하는 노드 중 증설되는 파드에 할당해야 할 CPU 혹은 메모리가 부족하다면 파드는 노드에 할당되지 못하고 대기 상태로 노드가 생성될 때까지 기다리게 됩니다. 그리고 어느 정도 시간이 지나면 노드가 프로비저닝 됩니다. 노드가 정상적으로 준비되면, 쿠버네티스 스케줄러는 파드를 새롭게 생성된 노드에 할당합니다. HPA에 의해 증설된 파드는 이 때가 돼서야 비로소 요청을 처리할 수 있습니다. 이렇게 새로운 노드에 증설되는 파드가 정상 동작할 때까지 기다리는 과정에서 갑작스럽게 많은 요청이 들어오게 되는 경우, 기존에 버티고 있던 파드들이 요청을 제대로 소화하지 못해 500 에러가 발생하거나 OOM으로 파드가 죽을수도 있습니다.

그림 1. 오버프로비저닝이 적용되지 않은 경우

만약 요청이 많이 들어오기 전에 필요한 노드 수보다 더 많이 노드를 생성해 놓았다면 어떻게 될까요?

사용자로부터 요청이 많지 않은 경우, 아무런 동작은 하지 않지만, CPU와 메모리가 할당되어야 하는 파드가 몇 개의 노드에 할당되었다고 가정해보겠습니다. 이 상황에서 갑작스럽게 많이 요청이 들어오는 경우, HPA를 통해 증설되는 파드가 그림 1에서 기존에 아무런 동작은 하지 않지만 자원을 점유하고 있는 파드 덕분에 여유분의 노드가 생성되어 있어 해당 노드에 바로 할당이 가능합니다. 이처럼 서비스의 안정성을 위해 필요한 노드 수보다 더 많은 노드를 생성해서 증설되는 파드가 빠르게 할당될 수 있도록 하는 것을 오버프로비저닝(Overprovisioning)이라고 합니다.

그림 2. 오버프로비저닝이 적용된 경우

PriorityClass와 빈 파드

오버프로비저닝에서는 파드의 우선순위를 결정하는 요소인 PriorityClass와 PriorityClass가 적용된 빈 파드가 필요합니다.

워커 노드에 CPU나 메모리가 충분하지 않거나, 사용하려는 포트를 이미 실행 중인 다른 파드가 점유 중인 경우에는 새로운 파드를 배포하지 못하는 상황이 발생할 수 있습니다. 하지만, 새롭게 생성할 파드가 매우 중요한 역할을 하는 애플리케이션이라면, 이러한 제약조건에도 불구하고 반드시 배포되어야 합니다. 이런 상황을 해결하기 위해 사용할 수 있는 것이 바로 PriorityClass입니다.

EKS 클러스터를 제일 처음 생성하고, priorityClass를 조회해 보면 기본적으로 system-cluster-critical과 system-node-critical이라는 2개의 PriorityClass가 높은 value를 가지고 생성되어 있는 것을 확인할 수 있습니다. 이 ProrityClass는 시스템에서 중요한 파드들에 적용되어 높은 우선순위를 가지도록 해줍니다.

그림 3. EKS Default PriorityClass(system-cluster-critical, system-node-critical)

시스템에서 제공하는 PriorityClass 외에 사용자가 PriorityClass를 생성하여 원하는 파드에 지정할 수 있습니다.

위의 YAML 설정파일을 보면 value에 지정된 값에 따라서 파드에 우선순위를 설정할 수 있습니다. 새로 배포되어야 하는 파드의 우선 순위가 높은데 리소스 부족 등 다양한 이슈로 배포할 수 없다면 해당 파드보다 낮은 우선순위를 가지고 있는 파드를 기존의 노드에서 축출(Eviction)하고 우선 순위가 높은 새로운 파드가 배포됩니다. 이러한 동작 원리를 이용하면, 우선순위가 낮은 빈 파드를 만들어 CPU와 메모리만 점유하도록 설정하면, 우선순위가 높은 서비스 파드가 들어왔을 때 밀어내도록 만들 수 있습니다.

그림 4. 별도의 PriorityClass가 적용되지 않은 파드

 

카펜터를 활용한 오버프로비저닝

이제 위에서 설명한 PriorityClass와 빈 파드, 그리고 카펜터를 활용해서 어떻게 오버프로비저닝을 적용하는지 그림 5를 통해 순서대로 알아보도록 하겠습니다.

그림 5. 카펜터를 통한 오버프로비저닝 동작 과정
  1. 1번 노드에는 높은 우선순위를 가진 파드(Nginx-1)가 할당돼 있고 2번 노드에는 낮은 우선순위를 가진 파드(빈 파드)가 할당돼 있습니다.
  2. 높은 우선순위를 가진 파드(Nginx-2)를 할당할 노드가 없습니다.
  3. 낮은 우선순위를 가진 파드(빈 파드)는 축출됩니다.
  4. 높은 우선순위를 가진 파드(Ningx-2)는 낮은 우선순위를 가진 파드가 있던 2번 노드에 할당됩니다.
  5. 카펜터는 낮은 우선순위를 가진 파드(빈 파드)를 할당하기 위해 새로운 노드를 증설합니다.
  6. 새롭게 증설된 노드에 낮은 우선순위를 가진 파드(빈 파드)가 할당됩니다.

위의 과정을 실제 EKS 환경에서 적용한 예를 한번 살펴보도록 하겠습니다.

그림 6를 보면 12대의 워커 노드가 있는 클러스터가 있습니다. 이 워커 노드들에는 aws-node나 kube-proxy, argoCD와 같은 여러 파드들이 떠 있습니다. 여기에 그림 7과 같이 other라는 네임스페이스를 생성하고 Priority가 -1인 PriorityClass를 빈 파드에 적용했습니다. 빈 파드는 10.102.108.161이라는 주소를 가진 노드에 할당된 것을 확인할 수 있습니다.

그림 6. EKS에 구성된 노드들
그림 7. PriorityClass가 적용된 빈 파드

 

그 다음 서비스 파드를 가정하기 위해 별도의 PriorityClass가 적용되지 않은 nginx 파드를 같은 클러스터에 배포해 줍니다. 그림 8에서 볼 수 있듯이 nginx 파드들이 할당될 자원이 충분하기 때문에 빈 파드가 바로 축출되지 않고, nginx도 정상적으로 배포되는 것을 확인할 수 있습니다.

이 상황에서 nginx 파드에 부하를 줘서 새로운 파드가 증설되도록 합니다. 새롭게 증설되어야 할 파드에 할당될 CPU나 메모리가 부족하기 때문에 우선순위가 낮은 빈 파드를 축출시키고 해당 노드에 할당되게 됩니다. 그리고 빈 파드는 대기 상태가 되기 때문에 카펜터는 이를 감지해서 새로운 노드를 증설하게 되고 그림 10와 같이 10.102.111로 시작되는 주소를 가진 노드에 새롭게 할당된 것을 확인할 수 있습니다.

그림 8. 서비스용 nginx 파드
그림 9. 새로운 노드에 뜨고 있는 빈 파드
그림 10. 새로운 노드에 정상적으로 할당된 빈 파드