CloudNet@ Team Study/EKS Workshop 4th Cohort

KEDA (Kubernetes Event-driven AutoScaling)

"Everything about infra" 2026. 4. 2. 03:16
포스팅은 CloudNet@팀 서종호(Gasida)님이 진행하시는
AWS EKS Workshop Study 내용을 참고하여 작성합니다.

 

안녕하세요!

이번 포스팅은 KEDA AutoScaler에 대해서

소개하고 실습하는 시간을 갖도록 하겠습니다.

 

 

 

01. KEDA AutoScaler 소개

기존의 HPA(Horizontal Pod AutoScaler)는 리소스(CPU, Memory) 메트릭을 기반으로

스케일 여부를 결정했었습니다.

 

하지만, 실제 프로덕션 환경에서는 아래와 같은 상황이 더 많이 발생합니다.

Kafka 토픽에 메시지가 10만개 쌓였다 → worker 늘려야 함
RabbitMQ 큐가 비었다 → worker 0개로 줄여야 함
DB에 대기 중인 작업이 많다 → 처리 파드 늘려야 함

CPU는 멀쩡한데 일감이 폭발하는 상황이에요. 이 상황들은 HPA로는 못잡죠.

이러한 이벤트를 활용하여 worker의 scale을 결정한다면 queue에 task가

많이 추가되는 시점에 더 빠르게 확장이 가능합니다.

 

즉, KEDA는 이런 외부 이벤트 소스를 직접 보고 스케일링 합니다.

 

KEDA의 내부 구조 3가지는 아래와 같습니다.

keda-operator
    └─ ScaledObject 감시
    └─ 이벤트 소스 폴링
    └─ 0 ↔ 1 활성화/비활성화 담당

keda-operator-metrics-apiserver  
    └─ 외부 메트릭을 HPA가 읽을 수 있게 변환
    └─ external.metrics.k8s.io API 서빙
    └─ HPA가 이 값 기반으로 실제 스케일 수행

keda-admission-webhooks
    └─ ScaledObject 잘못 설정 방지
    └─ 동일 Deployment에 ScaledObject 중복 방지

 

그렇다고 해서 KEDA가 HPA를 대체하는 것이 아니고, KEDA가 HPA를 생성하고 관리합니다.

KEDA ScaledObject 생성
    ↓
KEDA가 자동으로 HPA 생성
    ↓
외부 메트릭 → keda-metrics-apiserver → HPA → Deployment 스케일

 

📌 핵심 기능: Scale to Zero

HPA는 최소 1개 파드를 유지해야 하는데, KEDA는 0개까지 줄일 수 있습니다.

이벤트 없음 → 파드 0개 (비용 절감)
이벤트 발생 → 파드 1개로 활성화 → HPA가 추가 스케일

 

📌 ScaledObject 구조

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: my-scaler
spec:
  scaleTargetRef:
    name: my-deployment      # 스케일 대상
  minReplicaCount: 0         # 0까지 줄임
  maxReplicaCount: 10
  triggers:
  - type: kafka              # 어떤 이벤트 소스를 볼지
    metadata:
      topic: my-topic
      lagThreshold: '5'      # 파드당 lag 5 초과 시 스케일

KEDA는 Scaler 종류가 많습니다 (60개 이상)

메시지 큐 : Kafka, RabbitMQ, SQS, NATS
Database : MySQL, PostgreSQL, MongoDB
Cloud : AWS CloudWatch, GCP Pub/Sub, Azure Queue
기타 : Prometheus, cron, Redis ...

 


 

02.KEDA with Helm 실습

KEDA 설치 전 기존 metrics-server 제공 metrics API 확인

 

📌 KEDA 설치 - serviceMonitor

cat <<EOT > keda-values.yaml
metricsServer:
  useHostNetwork: true

prometheus:
  metricServer:
    enabled: true
    port: 9022
    portName: metrics
    path: /metrics
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus Operator
      enabled: true
  operator:
    enabled: true
    port: 8080
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus Operator
      enabled: true
  webhooks:
    enabled: true
    port: 8020
    serviceMonitor:
      # Enables ServiceMonitor creation for the Prometheus webhooks
      enabled: true
EOT

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 -f keda-values.yaml

아래와 같이 helm install로 keda를 불러오자.

 

KEDA 설치 확인

KEDA가 잘 올라온 것을 확인할 수 있습니다.

3개의 KEDA 파드 모두 Running 상태 확인.
KEDA CRD 6개 모두 등록 확인.
external.metrics.k8s.io/v1beta1 API 정상 응답 확인.

 

📌 KEDA 네임스페이스에 디플로이먼트 생성

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml -n keda
kubectl get pod -n keda

테스트용 파드인 php-apache.yaml 파일을 올렸습니다.

 

📌 ScaledObject 생성 (cron 기반)

# ScaledObject 정책 생성 : cron
cat <<EOT > keda-cron.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: php-apache-cron-scaled
spec:
  minReplicaCount: 0
  maxReplicaCount: 2  # Specifies the maximum number of replicas to scale up to (defaults to 100).
  pollingInterval: 30  # Specifies how often KEDA should check for scaling events
  cooldownPeriod: 300  # Specifies the cool-down period in seconds after a scaling event
  scaleTargetRef:  # Identifies the Kubernetes deployment or other resource that should be scaled.
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  triggers:  # Defines the specific configuration for your chosen scaler, including any required parameters or settings
  - type: cron
    metadata:
      timezone: Asia/Seoul
      start: 00,15,30,45 * * * *
      end: 05,20,35,50 * * * *
      desiredReplicas: "1"
EOT

kubectl apply -f keda-cron.yaml -n keda

오브젝트 핵심 옵션 상세 내용:

minReplicaCount: 0 → HPA는 최소 1개 유지, KEDA는 0개까지 가능 (비용 절감)
pollingInterval: 30 → 30초마다 이벤트 소스 체크, Cron은 시간 기반이라 크게 의미 없습니다.
cooldownPeriod: 300 → 이벤트 없어진 후 바로 0으로 내리지 않고 5분 대기 후 스케일 다운 (flapping 방지)

cron 메타데이터를 보면 5분 동안만 파드가 떠있고 나머지는 0개인 트리거 정책이네요.

 

그라파나 대시보드 추가 - KEDA용

KEDA 대시보드 Import URL

curl -s https://raw.githubusercontent.com/kedacore/keda/main/config/grafana/keda-dashboard.json

그러면 아래와 같이 JSON 내용이 출력이 되는데, 이 내용을 그라파나에 Import 하면 됩니다.

아래와 같이 JSON 내용을 복사하여 붙혀넣고 Load 하시면 됩니다.

대시보드를 아래와 같이 추가가 완료되었으면, namespace를 keda로 변경 !

 

📌 모니터링

# 터미널 1: 실시간 모니터링
watch -d 'kubectl get ScaledObject,hpa,pod -n keda'

# 터미널 2: HPA 상세 확인
kubectl get hpa -o jsonpath="{.items[0].spec}" -n keda | jq

 

 

현재 시작 03:47분 스케일 인 시간(50분) 전이라 파드가 아직 1개가 남아있습니다.

 

현재 시작 03:50분으로 스케일 인 시간입니다.

하지만 파드가 아직 6m AGE로 살아있습니다. (cooldownPeriod: 300초 이기 때문입니다)

03:45분 → 스케줄 아웃 (파드 1개 생성)
03:50분 → 스케줄 인 시작
+ cooldown 300초 대기
03:55분 → 실제 파드 0개로 내려감

 

현재 시작 03:55분 cooldownPeriod 시간이 끝나서 파드가 0개가 된 것을 확인할 수 있습니다.

 

그라파나 대쉬보드를 봐도 변화가 생겼습니다. (데이터가 쌓이지 않아서 좀 휑~ 하네요)

Current/max replicas: 1 → 0 으로 내려감  ← 파드 사라짐
Changes in replicas: 초록 막대 표시      ← 스케일 이벤트 발생

 

현재 시작 04:00, cron start 시간에 도달하여 스케일 아웃 (파드 수 증가)가 일어났습니다.

 

실습 내용을 Cron 예제로 들어서 막 크게 KEDA의 장점이 와닿지 않을 수 있습니다.

"5분 마다 스케일 인/아웃 현상"이 마치 그냥 스케줄링과 비슷하게 느껴질 수도 있네요.

 

하지만, KEDA가 빛나는 상황은 위에서 설명드렸다 싶이 데이터 기반 이벤트 발생 시 입니다.

 

예시) Kafka 기반 데이터 파이프라인

메시지 0개 → worker 파드 0개 (비용 0)
메시지 10만개 갑자기 적재 → 즉시 worker 50개로 확장
메시지 소진 → 다시 0개로

CPU, Memory 등 리소스를 기반으로 스케줄링하는 HPA는 절대로 이 이벤트를 감당할 수 없을 것 같네요.

 


 

도전과제: KEDA HTTP Add-on 사용해보기

📌 HTTP Add-on 이란?

기본 KEDA는 Kafka, SQS 같은 외부 이벤트 소스 기반입니다.

근데, HTTP 요청 자체를 트리거로 쓰려면 Add-on이 필요해요.

 

파드가 0개인 상태에서 HTTP 요청이 오면?
    ↓
받아줄 파드가 없음 → 요청 유실

일반 HPA/KEDA로는 이 실습이 불가능합니다. 파드가 떠있어야 요청을 받을 수 있어서요.

 

HTTP Add-on 해결 방식

클라이언트 요청
    ↓
Interceptor (프록시) 가 요청을 가로챔  ← 핵심!
    ↓
파드 0개면 → KEDA에 스케일 아웃 요청
           → 파드 뜰 때까지 요청 버퍼링 (대기)
    ↓
파드 뜨면 → 버퍼링된 요청 전달

 

HTTP Add-on 구성 요소

Interceptor  → 요청 프록시 + 버퍼링
Scaler       → 현재 큐에 쌓인 요청 수를 메트릭으로 노출
Controller   → HTTPScaledObject 관리

 

그렇다면 이 애드온을 사용하는 것이 기존 KEDA와 무슨 차이가 있을까?

  • 기존 KEDA : 외부 이벤트 소스 (Kafka, SQS etc..)
  • HTTP Add-on : HTTP 요청 자체가 트리거 + Scale to Zero 상태에서도 요청 유실 없음.

 

HTTP Add-on install

Step1. helm으로 설치

helm repo add kedahttp https://kedacore.github.io/charts
helm repo update

helm install http-add-on kedahttp/keda-add-ons-http \
  --namespace keda

 

Step2. http addon pod 상태 확인

 

Step3. HTTPScaledObject 생성

# php-apache keda 네임스페이스에 이미 있으니까 그거 활용
# HTTPScaledObject 생성

cat <<'EOF' | kubectl apply -f -
kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
  name: php-apache-http
  namespace: keda
spec:
  hosts:
  - php-apache.keda.svc.cluster.local
  targetPendingRequests: 100
  scaledownPeriod: 30
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
    service: php-apache
    port: 80
  replicas:
    min: 0
    max: 10
EOF

scaledownPeriod: 30의 섹션이 30초동안 추가 요청이 없는 경우 파드를 삭제합니다.

min:0 이므로 요청이 없으면 파드가 0개 이지만 요청이 오면 즉시 깨웁니다.

 

오브젝트 디플로이먼트가 READY 상태인지 확인하고 진행하자 !

 

Step4. 모니터링 및 HTTP 요청 보내기

# 터미널 1: 모니터링
watch -d 'kubectl get httpscaledobject,pod -n keda | grep -v keda-add'

# 터미널 2: 요청 보내기
kubectl run test-curl --image=curlimages/curl --rm -it --restart=Never -n keda -- \
  curl -H "Host: php-apache.keda.svc.cluster.local" \
  http://keda-add-ons-http-interceptor-proxy.keda:8080

 

요청을 보내는 순간, 바로 테스트 파드가 생성이 되었네요 ~!

 

그리고 30초 가량 요청이 없으면 테스트 파드가 바로 삭제가 되는 것도 확인이 가능합니다.

위 에러는 요청이 계속 오는데, php-apache 파드가 없어서 그렇습니다. (스케일 할 파드가 없음)

파드가 쫙 늘어나는 것 까지 테스트를 해보려고 했으나, php-apache가 요청을 너무 빨리 처리해서

pending 요청이 안쌓임 → 스케일 아웃 트리거 안됨.

 

HTTP Add-on 실제 사용 사례는 아래와 같다고 합니다.

트래픽이 간헐적인 API 서버
배치성 웹훅 처리
개발/스테이징 환경 (밤에 파드 0개로 비용 절감)

 

KEDA의 HTTP Add-on 장점을 아래와 같이 정리해봤습니다.

  1. Scale to Zero : 트래픽이 없을 땐 파드 0개, 요청이 와도 파드를 깨워서 유실이 없습니다.
  2. HTTP 요청 자체가 트리거 : CPU 올라갈 때까지 기다릴 필요가 없습니다. (요청 시 바로 스케일 아웃)

 

긴 글 읽어주셔서 감사합니다.