KEDA
Kubernetes Event-driven Autoscaling
이벤트 기반 오토스케일링을 위한 Kubernetes 컴포넌트
Kubernetes Event-driven Autoscaling
이벤트 기반 오토스케일링을 위한 Kubernetes 컴포넌트
KEDA(Kubernetes Event-driven Autoscaling)는 외부 이벤트 소스를 기반으로 Kubernetes 워크로드를 스케일링하는 오픈소스 컴포넌트입니다. Microsoft와 Red Hat이 공동 개발했으며, CNCF 프로젝트로 Graduated 상태입니다. 기본 HPA가 CPU/메모리만 지원하는 것과 달리, KEDA는 60개 이상의 이벤트 소스(Scaler)를 지원합니다.
주요 기능으로 Scale to Zero가 있습니다. 이벤트가 없으면 Pod를 0개로 줄여 비용을 절감합니다. 메시지 큐에 메시지가 들어오면 Pod를 생성하고, 처리가 끝나면 다시 0으로 줄입니다. 이는 서버리스와 유사한 운영 방식을 Kubernetes에서 구현할 수 있게 합니다.
지원 이벤트 소스는 매우 다양합니다. 메시지 큐: Kafka, RabbitMQ, AWS SQS, Azure Service Bus, Google Pub/Sub. 데이터베이스: PostgreSQL, MySQL, MongoDB, Redis. 메트릭: Prometheus, Datadog, New Relic. 클라우드 서비스: AWS CloudWatch, Azure Monitor, GCP Stackdriver. 스케줄: Cron 기반 스케일링도 가능합니다.
아키텍처는 크게 세 가지 컴포넌트로 구성됩니다. keda-operator는 ScaledObject와 ScaledJob CRD를 감시하고 HPA를 생성/관리합니다. keda-metrics-apiserver는 외부 메트릭을 Kubernetes 메트릭 API로 노출합니다. Scaler는 각 이벤트 소스에서 메트릭을 수집합니다.
# KEDA 설치 (Helm)
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda \
--namespace keda \
--create-namespace \
--set prometheus.metricServer.enabled=true
# Kafka Consumer ScaledObject
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: kafka-consumer-scaler
namespace: production
spec:
scaleTargetRef:
name: order-processor # Deployment 이름
pollingInterval: 15 # 메트릭 확인 주기 (초)
cooldownPeriod: 300 # 스케일 다운 대기 시간
minReplicaCount: 0 # Scale to Zero 활성화
maxReplicaCount: 50 # 최대 Pod 수
fallback:
failureThreshold: 3 # 스케일러 오류 시
replicas: 2 # 폴백 replica 수
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-cluster:9092
consumerGroup: order-consumer-group
topic: orders
lagThreshold: "100" # Consumer Lag 임계값
offsetResetPolicy: earliest
authenticationRef:
name: kafka-credentials
---
# Kafka 인증 정보
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: kafka-credentials
namespace: production
spec:
secretTargetRef:
- parameter: sasl
name: kafka-secrets
key: sasl-mechanism
- parameter: username
name: kafka-secrets
key: username
- parameter: password
name: kafka-secrets
key: password
---
# Consumer Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-processor
namespace: production
spec:
replicas: 0 # KEDA가 관리하므로 0으로 시작
selector:
matchLabels:
app: order-processor
template:
metadata:
labels:
app: order-processor
spec:
terminationGracePeriodSeconds: 60
containers:
- name: processor
image: myapp/order-processor:v1.2.0
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
env:
- name: KAFKA_BROKERS
value: "kafka-cluster:9092"
- name: CONSUMER_GROUP
value: "order-consumer-group"
# AWS SQS ScaledObject
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: sqs-processor-scaler
namespace: production
spec:
scaleTargetRef:
name: sqs-processor
minReplicaCount: 0
maxReplicaCount: 30
triggers:
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.ap-northeast-2.amazonaws.com/123456789/order-queue
queueLength: "5" # 메시지 5개당 Pod 1개
awsRegion: ap-northeast-2
scaleOnInFlight: "true" # 처리 중 메시지도 카운트
authenticationRef:
name: aws-credentials
---
# IRSA (IAM Roles for Service Accounts) 인증
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: aws-credentials
namespace: production
spec:
podIdentity:
provider: aws-eks # EKS Pod Identity 사용
---
# ScaledJob: 일회성 작업에 적합
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: image-processor-job
namespace: batch
spec:
jobTargetRef:
parallelism: 1
completions: 1
backoffLimit: 3
template:
spec:
restartPolicy: Never
containers:
- name: processor
image: myapp/image-processor:v2.0
resources:
requests:
cpu: "1"
memory: "2Gi"
pollingInterval: 10
minReplicaCount: 0
maxReplicaCount: 100
successfulJobsHistoryLimit: 5
failedJobsHistoryLimit: 5
triggers:
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.ap-northeast-2.amazonaws.com/123456789/image-queue
queueLength: "1" # 메시지 1개당 Job 1개
awsRegion: ap-northeast-2
authenticationRef:
name: aws-credentials
---
# 멀티 트리거 예제 (SQS + Cron)
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: hybrid-scaler
spec:
scaleTargetRef:
name: hybrid-processor
minReplicaCount: 0
maxReplicaCount: 20
triggers:
# SQS 기반 스케일링
- type: aws-sqs-queue
metadata:
queueURL: https://sqs.ap-northeast-2.amazonaws.com/123456789/tasks
queueLength: "10"
authenticationRef:
name: aws-credentials
# 업무 시간 최소 유지 (9시~18시)
- type: cron
metadata:
timezone: Asia/Seoul
start: 0 9 * * 1-5 # 월-금 09:00
end: 0 18 * * 1-5 # 월-금 18:00
desiredReplicas: "3" # 최소 3개 유지
# Prometheus 메트릭 기반 스케일링
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: prometheus-scaler
namespace: production
spec:
scaleTargetRef:
name: api-gateway
minReplicaCount: 2
maxReplicaCount: 50
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
metricName: http_requests_per_second
threshold: "100" # Pod당 100 RPS
query: |
sum(rate(http_requests_total{
namespace="production",
service="api-gateway"
}[2m]))
ignoreNullValues: "false"
---
# HTTP Request 수 기반 (Prometheus + Ingress 메트릭)
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: ingress-based-scaler
spec:
scaleTargetRef:
name: web-frontend
minReplicaCount: 3
maxReplicaCount: 100
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Pods
value: 4
periodSeconds: 15
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring:9090
threshold: "50"
query: |
sum(rate(nginx_ingress_controller_requests{
namespace="production",
ingress="web-frontend"
}[1m])) /
count(kube_pod_status_ready{
namespace="production",
pod=~"web-frontend.*"
} == 1)
---
# Redis List 기반 스케일링
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: redis-worker-scaler
spec:
scaleTargetRef:
name: background-worker
minReplicaCount: 0
maxReplicaCount: 20
triggers:
- type: redis
metadata:
address: redis-master.cache:6379
listName: job_queue
listLength: "10" # 10개 항목당 Pod 1개
activationListLength: "1" # 1개 이상이면 스케일업 시작
databaseIndex: "0"
authenticationRef:
name: redis-auth
---
# 스케일링 상태 확인 명령어
# kubectl get scaledobject -A
# kubectl describe scaledobject kafka-consumer-scaler -n production
# kubectl get hpa -n production # KEDA가 생성한 HPA 확인
# kubectl logs -n keda -l app=keda-operator # KEDA 로그
PM: "주문 처리 시스템이 피크 시간에 Kafka lag이 계속 쌓여요. HPA를 쓰고 있는데 CPU 기반이라 반응이 느린 것 같아요."
DevOps: "KEDA로 전환하면 Kafka consumer lag을 직접 트리거로 사용할 수 있어요. lag이 100 이상이면 Pod를 늘리고, 0이 되면 Scale to Zero도 가능합니다. 야간에는 자동으로 0으로 줄어서 비용도 절감되고요. 현재 시스템에서 consumer group lag을 KEDA가 직접 모니터링하게 설정하면, CPU 기반보다 훨씬 빠르게 반응합니다."
면접관: "KEDA와 기본 HPA의 차이점을 설명해주시고, 언제 KEDA를 선택하시나요?"
지원자: "기본 HPA는 CPU, 메모리 같은 리소스 메트릭이나 커스텀 메트릭 서버가 필요합니다. KEDA는 60개 이상의 외부 이벤트 소스를 네이티브로 지원하고, Scale to Zero가 가능한 게 큰 차이점입니다. 메시지 큐 기반 워커나 배치 처리처럼 이벤트가 없을 때 리소스를 완전히 회수해야 하는 경우, 또는 외부 서비스의 메트릭을 기반으로 스케일링해야 할 때 KEDA를 선택합니다. 실제로 SQS consumer에 KEDA를 적용해서 야간 비용을 80% 줄인 경험이 있습니다."
개발자: "KEDA ScaledObject를 배포했는데 Pod가 스케일업이 안 돼요. HPA도 생성이 안 됐어요."
SRE: "먼저 kubectl describe scaledobject로 Status 확인해보세요. Scaler 연결 실패일 가능성이 높아요. TriggerAuthentication이 제대로 설정됐는지, Secret이 같은 네임스페이스에 있는지 확인하세요. KEDA operator 로그에서 'unable to get metrics' 에러가 있으면 인증 문제입니다. AWS SQS면 IRSA 설정 확인하고, Kafka면 SASL 인증 정보 확인해보세요."
minReplicaCount: 0 설정 시 첫 이벤트 처리에 지연이 발생합니다. Pod 시작, 컨테이너 초기화, 연결 수립까지 수십 초 걸릴 수 있어요. 실시간성이 중요하면 minReplicaCount를 1 이상으로 설정하거나 activationValue를 조정하세요.
같은 Deployment에 HPA와 KEDA ScaledObject를 모두 적용하면 충돌이 발생합니다. KEDA는 내부적으로 HPA를 생성하므로, 기존 HPA가 있다면 삭제하고 KEDA만 사용하세요. advanced.horizontalPodAutoscalerConfig로 HPA 동작을 커스터마이징할 수 있습니다.
외부 이벤트 소스 장애 시 스케일링이 멈출 수 있습니다. fallback 설정으로 스케일러 오류 시 유지할 replica 수를 지정하세요. failureThreshold와 replicas를 설정하면 메트릭 수집 실패 시에도 최소 가용성을 보장합니다.