들어가며
쿠버네티스에는 기본적으로 Pod의 CPU, Memory 사용량을 기반으로 Replica 수를 동적으로 조정할 수 있도록 HPA(Horizontal Pod Autoscaler)를 지원합니다.
그러나 실제 운영 환경에서는 오토스케일링의 기준을 다양하게 가져가야하는 경우나, 조금 더 디테일하게 Pod 수를 조절하고 싶은 경우가 빈번하게 발생합니다.
이를 위한 KEDA(Kuberneters Event-Driven Autoscaler)에 대해서 알아보고 실습해보겠습니다.
KEDA
KEDA는 Kubernetes Event-driven Autoscaling의 약자로, Microsoft와 Red Hat이 공동으로 개발한 오픈소스 프로젝트입니다.
KEDA는 Kubernetes의 기본 오토스케일링 기능을 확장하여, 다양한 이벤트 소스로부터의 메트릭을 기반으로 워크로드를 스케일링할 수 있게 만듭니다.
기존에 쿠버네티스 HPA는 CPU, Memory를 기반으로 Pod 수를 조정하는데, Pod 수를 조정하는 기준을 CPU, Memory 뿐만아니라 다양한 기준을 사용하여 조정할 수 있도록 도와주는 도구라고 이해하시면 됩니다.
KEDA Architecture
아래는 KEDA의 아키텍처 디자인으로써 Kubernetes HPA와 어떻게 동작하는지, 또한 외부 이벤트 소스(External trigger source)와 함께 작동하는 방식을 설명합니다.
여기서 눈여겨볼 것은 KEDA는 기존 Kubernetes 클러스터의 HPA를 교체하는 방식이 아닌, HPA와 함께 동작하며 오토스케일링 기능을 확장한다는 점입니다.
이는 클러스터 관리자입장에서 굉장한 이점으로 작용합니다. KEDA를 사용하기 위해 기존 클러스터의 일부를 교체/변경하는 것이 아니기 때문입니다.
또한 마이그레이션 과정이 특별히 없다고 봐도 됩니다. KEDA를 설치하더라도 기존 HPA는 그대로 동작하니까요.
덕분에 당장 모든 워크로드들이 스케일링 동작을 위해 KEDA의 scaledObject로 변경하지 않아도됩니다.
물론 KEDA를 통해 스케일링하려면 기존 HPA는 삭제해야하지만, 이게 큰 어려움은 아닙니다.
기본 노드 오토스케일러인 Cluster Autoscaler 에서 Karpenter로 마이그레이션 하는 것과는 그 무게감이 완전히 다릅니다.
KEDA의 장점
•
쉬운 설치/적용
◦
기존 Kubernetes HPA와 함께 동작하며, 클러스터에 영향도가 적음
•
비용 효율성
◦
실제 사용에 따른 정확한 스케일링으로 리소스 낭비 감소
KEDA CustomResource
ScaledObject
ScaledObject KEDA의 핵심 커스텀 리소스로, 스케일링 대상과 조건을 정의합니다.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-scaledobject
spec:
scaleTargetRef: # 스케일링할 대상 워크로드
apiVersion: apps/v1
kind: Deployment
name: consumer-deployment
pollingInterval: 30 # 메트릭 수집 주기 (초)
cooldownPeriod: 300 # 스케일 다운 전 대기 시간
minReplicaCount: 0 # 최소 레플리카 수
maxReplicaCount: 100 # 최대 레플리카 수
triggers: # 스케일링 트리거 정의
- type: rabbitmq
metadata:
queueName: myqueue
host: rabbitmq.default
queueLength: "50" # 큐 길이가 50 이상이면 스케일 아웃
YAML
복사
•
scaleTargetRef: 스케일링할 대상 워크로드를 지정
•
pollingInterval: 메트릭 수집 주기 설정
•
cooldownPeriod: 스케일 다운 전 대기 시간
•
minReplicaCount: 최소 레플리카 수 (0으로 설정 가능)
•
maxReplicaCount: 최대 레플리카 수
•
triggers: 스케일링 트리거 조건 정의 (여러 트리거 동시 설정 가능)
ScaledJob
Kubernetes Job을 이벤트 기반으로 스케일링할 때 사용됩니다.
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: prometheus-scaledjob
spec:
jobTargetRef: # 실행할 Job 템플릿
template:
spec:
containers:
- name: processor
image: myregistry/processor:v1
pollingInterval: 30 # 메트릭 수집 주기
successfulJobsHistoryLimit: 5 # 성공한 Job 히스토리 보존 수
failedJobsHistoryLimit: 5 # 실패한 Job 히스토리 보존 수
maxReplicaCount: 100 # 동시 실행 가능한 최대 Job 수
triggers: # 스케일링 트리거 정의
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring
query: sum(rate(http_requests_total{code=~"^5.*"}[2m]))
threshold: "100"
YAML
복사
•
jobTargetRef: 실행할 Job의 템플릿 정의
•
pollingInterval: 메트릭 수집 주기
•
successfulJobsHistoryLimit: 성공한 Job의 히스토리 보존 수
•
failedJobsHistoryLimit: 실패한 Job의 히스토리 보존 수
•
maxReplicaCount: 동시 실행 가능한 최대 Job 수
•
triggers: Job 생성 트리거 조건 정의
TriggerAuthentication
TriggerAuthentication 는 외부 메트릭 소스에 접근하기 위한 인증 정보를 관리합니다.
시크릿, 환경 변수 등 다양한 방식의 인증을 지원합니다.
ClusterTriggerAuthentication
TriggerAuthentication 의 클러스터 범위 리소스입니다.
클러스터 전체에서 재사용 가능한 인증 설정을 정의합니다.
ScaledObject를 통한 스케일링 테스트
Cron을 통한 특정 시간 스케일링
일반적으로 HPA를 사용한다면 트래픽 유입에 따라 높아지는 Pod의 CPU/Memory 사용량을 기반으로 Pod를 오토스케일링하는 방법을 사용합니다.
하지만 이 방법은 트래픽이 이미 높아진 상황에서의 스케일링이라 반응속도가 늦기 마련입니다.
어떤 서비스를 운영하다보면 특정 시간대에 트래픽이 집중되는 경향을 보이는 것이 일반적입니다.
이를 대비하기 위해 특정 시간대에 미리 Pod를 스케일 아웃하는 방법을 한 번 알아보겠습니다.
오후 17:40~18:00 동안 트래픽이 대량으로 유입되는 서비스가 있다고 가정하겠습니다.
해당 시간에 Pod 수를 미리 늘려 놓으면 대량 트래픽을 훨씬 잘 소화할 수 있을 것 입니다.
아래 scaledObject는 nginx-keda-test 라는 Deployment의 Pod 갯수를 기본 2개에서, 오후 17:40~18:00 동안 최소 5개가 유지될 수 있도록 합니다.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: nginx-cron
namespace: keda-test
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-keda-test # 타겟 디플로이먼트 이름
minReplicaCount: 2 # 기본 최소 레플리카 수
maxReplicaCount: 20 # 최대 레플리카 수
fallback:
failureThreshold: 3
replicas: 2
triggers:
# 기본 트리거 (CPU 사용률)
- type: cpu
metricType: Utilization
metadata:
value: "50"
# 매 정각마다 5개의 레플리카를 보장하는 Cron trigger
- type: cron
metadata:
timezone: Asia/Seoul # 타임존 설정
start: 40 17 * * * # 17:40 시작
end: 00 18 * * * # 18:00 종료
desiredReplicas: "5" # 원하는 레플리카 수
YAML
복사
먼저 스케일링 대상인 Deployment를 생성하고, 위에 작성해둔 scaledObject를 생성합니다.
사용한 Deployment의 yaml은 클릭하여 확인하세요.
scaledObject를 생성하니 KEDA의 scaledObject 뿐만아니라, hpa까지 함께 생성된 것을 확인할 수 있습니다.
kubeadm ~/workspace/seowoo.gong/keda $ kubectl get pods,hpa,scaledobject -n keda-test
NAME READY STATUS RESTARTS AGE
pod/nginx-keda-test-695dbf7d87-fcf2p 1/1 Running 0 25s
pod/nginx-keda-test-695dbf7d87-n89gc 1/1 Running 0 25s
kubeadm ~/workspace/seowoo.gong/keda $ kubectl apply -f scaledobject-cron-1.yaml
scaledobject.keda.sh/nginx-cron created
Bash
복사
오후 5시40분, scaledObject의 ACTIVE: True 가 되며, Pod가 5개로 증설된 것을 확인할 수 있습니다.
kubeadm ~/workspace/seowoo.gong/keda $ kubectl get pods,hpa,scaledobject -n keda-test
NAME READY STATUS RESTARTS AGE
pod/nginx-keda-test-695dbf7d87-fcf2p 1/1 Running 0 2m12s
pod/nginx-keda-test-695dbf7d87-n89gc 1/1 Running 0 2m12s
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/keda-hpa-nginx-cron Deployment/nginx-keda-test 500m/1 (avg), 0%/50% 2 20 2 100s
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
scaledobject.keda.sh/nginx-cron apps/v1.Deployment nginx-keda-test 2 20 True False False Unknown 100s
Bash
복사
오후 6시, scaledObject의 ACTIVE: False 로 변경되며, 잠시 후 원래 Pod 갯수인 2개로 돌아오게 됩니다.
kubeadm ~/workspace/seowoo.gong/keda $ kubectl get pods,hpa,scaledobject -n keda-test
NAME READY STATUS RESTARTS AGE
pod/nginx-keda-test-695dbf7d87-2jjxm 1/1 Running 0 41s
pod/nginx-keda-test-695dbf7d87-7q6n9 1/1 Running 0 26s
pod/nginx-keda-test-695dbf7d87-8rbgt 1/1 Running 0 41s
pod/nginx-keda-test-695dbf7d87-fcf2p 1/1 Running 0 6m58s
pod/nginx-keda-test-695dbf7d87-n89gc 1/1 Running 0 6m58s
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/keda-hpa-nginx-cron Deployment/nginx-keda-test 1/1 (avg), 2%/50% 2 20 5 6m26s
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
scaledobject.keda.sh/nginx-cron apps/v1.Deployment nginx-keda-test 2 20 True True False Unknown 6m26s
Bash
복사
kubeadm ~/workspace/seowoo.gong/keda $ kubectl get pods,hpa,scaledobject -n keda-test
NAME READY STATUS RESTARTS AGE
pod/nginx-keda-test-695dbf7d87-2jjxm 1/1 Running 0 21m
pod/nginx-keda-test-695dbf7d87-7q6n9 1/1 Running 0 20m
pod/nginx-keda-test-695dbf7d87-8rbgt 1/1 Running 0 21m
pod/nginx-keda-test-695dbf7d87-fcf2p 1/1 Running 0 27m
pod/nginx-keda-test-695dbf7d87-n89gc 1/1 Running 0 27m
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/keda-hpa-nginx-cron Deployment/nginx-keda-test 200m/1 (avg), 0%/50% 2 20 5 26m
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
scaledobject.keda.sh/nginx-cron apps/v1.Deployment nginx-keda-test 2 20 True False False Unknown 26m
-------------- 잠시 후 --------------
kubeadm ~/workspace/seowoo.gong/keda $ kubectl get pods,hpa,scaledobject -n keda-test
NAME READY STATUS RESTARTS AGE
pod/nginx-keda-test-695dbf7d87-2jjxm 1/1 Running 0 24m
pod/nginx-keda-test-695dbf7d87-7q6n9 1/1 Running 0 24m
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/keda-hpa-nginx-cron Deployment/nginx-keda-test 200m/1 (avg), 0%/50% 2 20 5 30m
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
scaledobject.keda.sh/nginx-cron apps/v1.Deployment nginx-keda-test 2 20 True False False Unknown 30m
Bash
복사
마치며
KEDA는 쿠버네티스에서 다양한 방법과 조건으로 Pod를 오토스케일링할 수 있도록 만들어준다는 점에서 아주 유용합니다. (실제로 HPA가 너무 단순해서 대응하기 어려운 경우가 많습니다.)
개인적으로 가장 맘에드는 점은 다양한 스케일러가 있다는 것보다도 기존 오토스케일링 리소스인 HPA를 대체하는 것이 아닌, HPA와 함께 동작하며 그 기능을 확장하는 개념이라는 부분입니다.
기존 클러스터의 구조를 크게 변경하지 않으면서 다양한 방법으로 Pod를 오토스케일링 할 수 있도록 도와주는 KEDA를 어느정도 쿠버네티스에 익숙해지신 분들이라면 한 번 적용해보는 것도 좋을 것 같습니다.