개요
EKS의 HPA를 찾아보는 과정에서 Karpenter라는 키워드를 많이 듣기도 했고, 여러 세션에서 Karpenter에 대한 설명도 조금씩은 들었었다. 그래서 정리해보고자 한다.
기존의 Autoscaling
기존에 EKS에서 오토 스케일링을 위해서는 Cluster Autoscaler(CA) 를 이용한 것으로 보인다.
Cluster Autoscaler(CA)란?
Cluster Autoscaler(CA)란 Kubernetes 클러스터가 워크로드에 따라 용량을 자동으로 조절하는 기능을 하는 도구이다. 개발자가 Pod를 많이 배포하거나 HPA 과정에서 새로 생성된 Pod가 Node의 리소스 부족으로 인해 Pending 상태에 빠지게 되면 Node를 Scale Out 해 Pod를 배치하는 방식이다.
CA의 작동방식은 아래와 같다.
- HPA에 의해 Pod의 수평적 확장이 한계가 되면 Pod는 Node를 배정받지 못하고 Pending 상태에 빠진다.
- CA는 Pod의 상태를 관찰하다가 지속해서 할당에 실패하면 Node Group의 ASG의 Desired Capacity 값을 수정해서 Worker Node 개수를 증가하도록 설정한다.
- 이를 인지한 ASG가 새로운 Node를 추가한다.
- 새로운 Node가 추가된 후 여유 공간에 Kube-scheduler가 Pod를 새 Node에 할당한다.
그런데 CA는 몇 가지 문제가 있다.
- 동기화 문제가 있을 수 있다. ASG는 EC2 인스턴스를 조절하는 것이고, EKS는 EC2 인스턴스로 구성된 Node를 다뤄 관리 주체가 다르고 동기화가 되지 않아서 EKS에서 특정 노드를 삭제 했지만 EC2 인스턴스는 남아있을 수도 있다.
- ASG에 의존적이다. EKS의 Node를 생성하는 것이지만, ASG의 EC2 Auto Scaling에 의존해야 한다. 그래서 관리자 입장에서 ASG를 따로 관리해줘야 하는 관리 오버헤드가 증가한다.
- 스케일링되는 속도가 느리다. 클러스터의 크기와 복잡성에 따라 수 분 정도 걸릴 수 있다. 워크로드 수요의 급격환 변화에 대응하는 데 지연이 발생할 수 있다.
- 요청 기반 조정으로 인해 활용도가 낮아진다. 실제 사용량이 아닌 클러스터에서 실행 중인 Pod의 리소스 요청을 기반으로 클러스터를 조정하기 때문에 과도하게 프로비저닝되거나 활용도가 낮아질 수 있다.
Karpenter를 활용한 방안
CA의 단점을 보완하기 위해 사용하게 된게 Karpenter이다.
Karpenter의 작동방식
- HPA에 의해 Pod의 수평적 확장이 한계가 되면 Pod는 적절한 Node를 배정받지 못하고 Pending 상태에 빠진다.
- Karpenter는 지속적으로 Unscheduled Pod를 관찰하고 있다가, 새로운 Node 추가를 결정하고 직접 배포한다.
- 추가된 Node가 Ready 상태가 되면 Karpenter는 kube-scheduler를 대신해 Pod의 Node binding 요청도 수행한다.
기존 CA에 비해 ASG 등에 의존하지 않고 단순한 구조를 가지고 있으며 클러스터 확장 시 일어나는 많은 부분을 Karpenter에서 직접 처리해서 빠르게 확장을 처리할 수 있도록 설계되었다. 모든 Worker Node는 Karpenter에 의해 Lifecycle이 결정된다.
Karpenter의 특징
Karpenter는 provisioners CRD 리소스를 사용해 애플리케이션 요구 사항에 따라 리소스 프로비저닝을 정의하고 관리하는 Auto Scaling에 대해 사전 예방적 접근 방식을 취한다.
Karpenter는 워크로드의 요구 사항에 맞게 프로비저닝 노드를 조절할 수 있어 최적의 리소스 활용도가 보장된다.
주요 기능
- 여러 AutoScaling Group 제거 : Karpenter는 Zone을 인식해 수 많은 AutoScaling Group을 관리하지 않고도 필요한 Zone에 노드를 프로비저닝할 수 있다.
- 지능형 노드 할당 : 정확한 워크로드 요구 사항을 기반으로 노드를 프로비저닝하여 비용과 시스템 가용성을 최적화한다.
- 다목적 노드 구성 : 다양한 구성을 지원하여 인스턴스 유형과 Pod 요구 사항을 최적으로 일치시킬 수 있다.
- 향상된 확장 및 예약 : 워크로드 및 리소스 활용률을 기반으로 빠르고 효율적인 확장을 제공하여 최적의 성능을 유지한다.
Karpenter의 장점
- 운영 부담 절감
- CA를 사용하면 Node의 운영 요구사항으로 인해 ASG나 Launch Template와 같은 AWS 자원들을 추가로 관리해야 했는데, Karpenter는 설치 후 Provisioner만 구성해주면 ASG나 Launch Template를 관리할 필요 없이 인스턴스 타입, 스토리지 크기, IAM 역할 등을 정의하여 사용할 수 있다.
- 또한 Provisioner를 용도 별로 구성하면 별도로 관리형 Node Group를 생성하고 운영할 필요가 없어 Node 운영 부담이 절감된다.
- 신속한 Node 추가와 제거
- 설계 구조에 따라 Node의 추가 속도가 빠르다. Pod를 할당할 수 있는 용량이 모자라면 즉시 추가되기 때문에 기존 CA 방식에 비해 훨씬 빠른 속도를 체감할 수 있다. Empty Node가 있는 경우 정리되는 속도도 빠르다. Node 제거에 대해서는 Provisioner에 ttlSecondsAfterEmpty 파라미터 값을 정의해 Node 제거에 대한 시간을 사용자 정의도 할 수 있다고 한다.
- 자동 Node 롤링
- EKS 클러스터 운영을 하다보면 특정 Node 사용이 장기화되어 보안 패치 등에 대한 우려가 생길 수 있다. Provisioner에 ttlSecondsUntilExpired 파라미터를 정의해 Node를 주기적으로 Rolling update 할 수 있다. 이를 이용해 Node가 수명을 다하면 Node drain과 delete를 차례대로 수행해 최신 버전의 amazon-eks-node 이미지로 신규 Node를 띄우게 된다.
- 다양한 인스턴스 타입을 쉽게 적용
- Karpenter를 사용하면 Node Group이나 Launch Template로 정의하지 않고 Provisioner로 인스턴스 타입을 정의할 수 있다. Provisioner에서는 다양한 인스턴스 타입을 정의할 수 있다. 이렇게 하면 리전 내 인스턴스 가용성을 걱정해야 할 일도 줄어들고, 신규 Node 추가 시 Karpenter가 요청 Pod에 가장 적절한 인스턴스 타입을 골라 기동하므로 Kubernetes 클러스터 운영 효율을 논할 때 나오는 bin packing 문제도 개선할 수 있다.
# Karpenter에서 사용하는 NodePool Object
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
requirements:
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["c", "m", "r"]
- key: karpenter.k8s.aws/instance-size
operator: In
values: ["nano", "micro", "small", "medium"]
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"] # ["spot","on-demand"]
nodeClassRef:
name: default
- 빠른 버전 업그레이드에 따른 기대
- Karpenter는 2021년 11월 GA되어 60여 번의 버전 릴리즈가 있을 정도로 빠르게 업데이트되고 있다고 한다. PodAffinity나 userdata를 지원하는 등 다양한 필요 기능 들이 추가되고, 업데이트 속도가 매우 빠르다는 장점을 갖고 있다.
Spot Instance로 Scaling하는 방법
Spot Instance는 Spot Price가 지정한 Maximum price 보다 높아진다면 Instance를 Terminate한다. Instance는 rebalance recommendations를 받고 이후 중지 2분 전 중지 이벤트를 받으며, 이벤트 이후 2분 내로 Instance는 종료(Terminate)된다.
최대 15분 전 발생하는 Rebalance Recommendation을 이용해 Instance가 Interrupt 되기 전에 새로운 Spot Instance를 띄우거나 온 디맨드 인스턴스로 전환해 Interrupt를 최소화 할 수 있다.
그럼 여기서 생각이 드는것은 어쨌든 노드 프로비저닝을 하고 파드를 새로 띄우게 될것인데 만약 Spot Price 가격이 높아져서 노드를 종료해야 한다면 안정성이나 가용성을 보장해줄수 있는지이다.
여러가지 방법이 있을 것 같은데 스팟 인스턴스를 사용할 때 PCO(가격 용량 최적화) 할당 전략 로 할당 전략을 선택하는 것이 가장 중단 가능성을 낮출 수 있는 방법이라고 한다.
그리고 Interrupt 이벤트가 발생하기 전에 자동으로 노드를 다른 노드로 교체해주는 Node Termination Handler(NTH)도 있다. Karpenter에서 Interrupt에 대한 처리(InterruptionQueueName로 지원됨)는 지원하지만 Rebalance Recommendation에 대한 처리는 지원되지 않는다고 한다. NTH는 Rebalance Recommendation 이벤트 발생 시 node를 cordon 또는 draining 하도록 설정할 수 있어 Interrupt 발생 전에 옫 디맨드 Node나 Spot Instance Node로 Pod를 옮길 수 있다.
또한 Karpenter는 Consolidation을 이용해 consolidationPolicy를 WhenUnderutilized로 설정해 disrupted하거나 더 작고 저렴한 노드로 교체할 수 있는 활용도가 낮는 노드를 자동으로 감지할수도 있다.
# karpenter의 disruption Block
disruption:
consolidationPolicy: WhenUnderutilized # WhenEmpty | WhenUnderutilized
expireAfter: 72h
Spot Node의 경우 기본적으로 Deletion Consolidation이 적용되어 있다고 한다.
Deletion
노드의 모든 Pod가 클러스터에 있는 다른 노드의 여유 용량에서 실행될 수 있는 경우 노드를 삭제할 수 있다.
Consolidation은 Karpenter로 인해 프로비저닝 된 노드를 효율적으로 종료하기 위해 사용하는 정책이다. Spot Consolidation 과 PCO 할당 전략을 이용해서 노드를 비용 효율, 리소스 효율적으로 정리할 수 있다. Deletion 과 Replace로 나뉘는데 Replace로 설정하면 노드를 새로운 노드로 교체할수도 있다.
그리고 Spot Instance를 Provisioning 할 때 EC2 Fleet API를 이용해서 노드를 직접 관리해 ASG를 사용하는 복잡하고 종속성 있는 방법을 제거하고 노드 관리를 단순화 할수있다.
카카오 기술 블로그 게시글에 더더욱 자세히 나와 있으니 꼭 읽어보면 좋을 것 같다.
Karpenter 도입을 위한 체크리스트도 있다.
내 생각
일단 쿠버네티스를 많이 다뤄보지 않아서인지 노드 스케일링이라는 개념도 잘 잡히지 않은 상태에서 정리해 설명이 부족한 것 같다. 그래도 키워드만 아는 것보다 이해가 조금 더 늘었다. 특히 Spot 인스턴스를 이용해서 Node Scaling을 한다는 개념 자체가 이해가 안갔는데 여러가지 안정성을 보장해주는 장치가 마련되어 있다는 것으로 이해했다.
[참조] :
https://velog.io/@rockwellvinca/EKS-CACluster-AutoScaling%EC%99%80-karpenter-%EC%8B%A4%EC%8A%B5
https://devblog.kakaostyle.com/ko/2022-10-13-1-karpenter-on-eks/?ref=codenary
https://dev.to/femilawal/karpenter-vs-cluster-autoscaler-in-eks-a-comparative-guide-4o71
https://tech.scatterlab.co.kr/spot-karpenter/
https://techblog.gccompany.co.kr/nth-nodeterminationhandler-9bc5b2af4ad7
'Containers > Kubernetes' 카테고리의 다른 글
Helm이란? (0) | 2024.01.17 |
---|