안녕하세요?
이번에는 Karpenter + Keda로 특정 시간에 Autoscaling 걸기에 대해 다뤄보겠습니다.
Karpenter와 Keda에 대하여
Karpenter가 뭔가요?
Karpenter는 Kubernetes를 autoscaling 할 수 있게 도와주는 도구입니다.
Autoscaling이란 말의 의미는 자동적으로 클러스터에 속한 노드를 증가, 축소할 수 있다는 뜻입니다.


출처 : https://www.youtube.com/watch?v=FPlCVVrCD64
Karpenter는 Kubernetes에서 Pod를 스케줄링 할 수 있는 노드가 없어서 pending 이벤트가 발생하면, 이를 감지하여 Auto scaling을 시작합니다.
pending 이벤트가 발생했다는 것은 Kubernetes Scheduler가 스케줄링을 시도했는데 적합한 노드를 못 찾았다는 뜻입니다.
이때 Karpenter로 인해 생성된 EC2 Fleet이 노드를 생성하고 삭제합니다.
그리고 위 작업으로 노드가 생성되었다면 Karpenter는 이후 그 노드들 중 더 이상 필요가 없는 노드를 삭제합니다.
그 판단 기준은 정할 수 있으며 리스트는 아래와 같습니다.
- Karpenter로 생성한 노드에 Pod가 없을 때 (Daemonset 제외)
- 해당 노드의 유지 시간이 지났을 때
- 여러 노드를 하나로 합쳐도 될 때
Karpenter와 AWS ASG
그런데 만약에 EKS를 쓰고 있는 사람이면 EKS에는 원래 AWS가 제공하는 Auto Scaling Group(ASG)가 존재하는데 왜 Karpenter를 써야 하는지에 대한 의문이 들 수 있습니다.
Karpenter와 ASG의 차이점을 비교하자면,
- 프로비저닝 속도가 수초~1분 내로 매우 빠르고 (ASG는 수 분 수준)
- 요청된 스펙내에서 유연하게 다양한 인스턴스를 고르기에 리소스 활용 효율성와 유연성이 매우 높고(ASG는 고정된 인스턴스 타입 사용)
- 자동화 수준이 높아서 운영 복잡도가 낮고 (ASG는 수동 설정해야 하는 부분이 많음)
- 비용 효율성이 높다 (ASG에 비해 최적의 인스턴스 타입을 고르기 때문에)
- ASG EKS에서 노드를 지우면 인스턴스 삭제가 안된다.
알려 진 바 위와 같이 Karpenter가 ASG에 비해서 더 효율적인 툴이라고 할 수 있습니다.
Keda가 뭔가요?
Kubernetes Event-Drived Autoscaler 로 KEDA를 사용하면 처리해야 할 이벤트의 수에 따라 Kubernetes의 모든 컨테이너를 확장할 수 있습니다.
KEDA는 Horizontal Pod Autoscaler와 같은 기존 Kubernetes의 구성요소와 함께 동작합니다.

출처 : KEDA Concepts | KEDA
Keda를 설치하면 ScaledObjects, ScaledJobs CRD가 생성됩니다.
Scaledobjects 는 Deployment, Statefulset 등을 autoscaling하는 데에,
Scaledjobs 는 Job, CronJob등을 autoscaling 하는데 사용합니다.
위 구조와 같이 ScaledObject를 생성하면 해당 리소스를 참조하는 HPA가 생성됩니다.
그러면 ScaledObject에서 trigger에 설정한 값을 감시하다가 만약에 이게 변경되면
HPA가 이 리소스를 Autoscaling하는 구조가 됩니다.
AutoScaling 테스트
Karpenter 설정
EKS 클러스터에 이미 Karpenter가 적용되어있다고 전제합니다.
Karpenter NodePool과 NodeClass를 준비합니다.
어떤 종류의 노드를 생성할지, 스펙은 무엇인지 등을 정의합니다.
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: my-nodepool
annotations:
kubernetes.io/arch: amd64
spec:
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: Never
template:
spec:
requirements:
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
- key: kubernetes.io/os
operator: In
values: ["linux"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["t"]
- key: karpenter.k8s.aws/instance-generation
operator: Gt
values: ["2"]
- key: "karpenter.k8s.aws/instance-cpu"
operator: Gt
values: ["3"]
- key: "karpenter.k8s.aws/instance-memory"
operator: Gt
values: ["8191"]
nodeClassRef:
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
name: my-node-class
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: my-node-class
spec:
amiFamily: Bottlerocket
role:
subnetSelectorTerms:
- tags:
karpenter.sh/discovery:
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery:
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeType: gp3
volumeSize: 2Gi
deleteOnTermination: true
- deviceName: /dev/xvdb
ebs:
volumeType: gp3
volumeSize: 16Gi
deleteOnTermination: true
KEDA 설치
Helm chart를 통해 KEDA를 설치해보겠습니다.
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --version 2.16.0 --namespace keda --create-namespace
잘 설치되었는지 확인합니다.
# KEDA 설치 확인
kubectl get crd | grep keda
kubectl get all -n keda
kubectl get validatingwebhookconfigurations keda-admission -o yaml
kubectl get podmonitor,servicemonitors -n keda
kubectl get apiservice v1beta1.external.metrics.k8s.io -o yaml
cloudeventsources.eventing.keda.sh 2025-03-16T00:57:42Z
clustercloudeventsources.eventing.keda.sh 2025-03-16T00:57:42Z
clustertriggerauthentications.keda.sh 2025-03-16T00:57:42Z
scaledjobs.keda.sh 2025-03-16T00:57:43Z
scaledobjects.keda.sh 2025-03-16T00:57:42Z
triggerauthentications.keda.sh 2025-03-16T00:57:42Z
NAME READY STATUS RESTARTS AGE
pod/keda-admission-webhooks-79bc6db6b-hjklr 1/1 Running 1 (47s ago) 52s
pod/keda-operator-64677bb4f9-8hjbb 1/1 Running 1 (48s ago) 52s
pod/keda-operator-metrics-apiserver-5dc57d64b-zh9v6 1/1 Running 1 (46s ago) 52s
...
KEDA 구조에서 CPU, Memory는 기존의 Kubernetes metric server에 의존해서,
Keda metrics server는 외부 이벤트 소스(Scaler) 메트릭을 노출합니다.
kubectl get pod -n keda -l app=keda-operator-metrics-apiserver
NAME READY STATUS RESTARTS AGE
keda-operator-metrics-apiserver-5dc57d64b-zh9v6 1/1 Running 1 (2m27s ago) 2m33s
KEDA 테스트
해당 테스트 내용은 이 블로그 내용을 참고했습니다.
[EKS] Karpenter + KEDA를 사용해서 특정 시간에 Auto Scaling 하는 방법 — Jen's Space
아래와 같이 affinity를 활용하면 우리의 테스트를 쉽게할 수 있도록
한 노드에 한 테스트 Pod만 배치하는 것이 가능합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 0
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 0
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: 1m
affinity:
nodeAffinity: # karpenter node 증설을 위한 노드어피니티.
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: karpenter.sh/nodepool
operator: In
values:
- my-nodepool
podAntiAffinity: # 파드 개수만큼 node 증설하기 위한 파드안티어피니티.
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: "kubernetes.io/hostname"
Keda ScaledObject를 준비합니다.
Keda를 통해 Pod가 늘어나고, 이를 배치하기 위해 Karpenter가 노드 수를 늘려야 합니다.
지정한 시간에 노드 5개를 증설하기 위한 KEDA Scalers Cron을 준비합니다.
지금이 오전 11시쯤이니 오전 11시 15분에 시작되게 하고
오전 11시 30분에 종료되게 합니다.
위에 생성한 pod가 5개로 늘어나게 합니다.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: keda-over-provioning
spec:
minReplicaCount: 1
maxReplicaCount: 10
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
triggers:
- type: cron
metadata:
timezone: Asia/Seoul
start: "15 11 * * *" # 오전 11시 15분 시작
end: "30 11 * * *" # 오전 11시 30분 종료
desiredReplicas: "5"
잠시의 시간이 흐르고 KST 기준 11시 15분이 되자,
pod의 갯수가 늘어나는게 보입니다.

그리고 Node도 빠르게 늘어나는 것을 볼 수 있습니다.
몇 초의 시간 범위 내에 Autoscaling이 정말 빠르게 일어납니다.

그리고 최종적으로 Pod가 무사히 생성된 것을 볼 수 있습니다.
k get pod
NAME READY STATUS RESTARTS AGE
nginx-54c7b9dd4c-9k4k2 1/1 Running 0 50s
nginx-54c7b9dd4c-gxnk8 1/1 Running 0 50s
nginx-54c7b9dd4c-mwzrq 1/1 Running 0 95s
nginx-54c7b9dd4c-qrwg8 1/1 Running 0 50s
nginx-54c7b9dd4c-xdlz7 0/1 Pending 0 34s
References
'Kubernetes' 카테고리의 다른 글
EKS Blue/Green Migration (0) | 2025.04.05 |
---|---|
Kubernetes Scheduling과 Scheduling plugins 파보기 (0) | 2025.03.20 |
External Secret으로 AWS Secrets Manager와 EKS Secret 동기화하기 (0) | 2025.03.15 |
Datadog Agent로 Traefik pod의 로그를 Datadog으로 전송하기 (0) | 2025.02.26 |
Mountpoint for Amazon S3로 EKS Pod에 S3 mount하기 (0) | 2025.02.18 |