Bytewax KEDA 자동 스케일링
Epic #1084 M3 가 추가한 infra/keda/scaledobjects/bytewax-anomaly-scaledobject.yaml 의 운영 가이드. Bytewax gend-streaming-anomaly Deployment 를 Kafka consumer lag + Pod CPU 사용률 기준으로 1~4 replica 사이에서 자동 확장한다.
M1 (PR #1106) 은 단일 replica Deployment 만 가졌고, KEDA 도입은 본 M3 PR 에서 처리한다. 본 PR scope 는 manifest 와 docs 만이며, AKS 실 적용은 ESO/operator 별도 사이클에서 진행한다.
1. 전제 조건
1.1 KEDA operator 설치 확인
kubectl --context aks-genos-prod get pods -n keda
# expected: keda-operator, keda-operator-metrics-apiserver Ready
미설치 시 helm 으로 설치한다.
helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda --namespace keda --create-namespace --version 2.13.x
GenD AKS prod 는 다른 ScaledObject (Trino worker, Flink TaskManager, Spark executor) 가 이미 같은 KEDA operator 를 사용하므로 별도 설치가 필요 없다.
1.2 Prometheus 접근 가능 확인
prometheus-server.monitoring.svc:80 가 같은 클러스터 내 in-cluster 주소로 노출되어 있어야 한다.
kubectl --context aks-genos-prod run -it --rm curl-test --image=curlimages/curl --restart=Never -- \
curl -s http://prometheus-server.monitoring.svc:80/api/v1/query?query=up | head -c 200
1.3 Kafka consumer lag 메트릭 노출
kafka_consumergroup_lag 메트릭이 Prometheus 에 노출되어야 한다. 두 가지 옵션:
- kafka_exporter (권장) ―
danielqsj/kafka_exporter또는 helm chartprometheus-community/prometheus-kafka-exporter로 설치. namespacegend의kafka.gend.svc:9092에 붙어 모든 consumer group / topic / partition 의 lag 를 노출한다. - jmx_exporter ― Kafka broker 사이드카로 동작. broker 단위 메트릭에 강하지만 consumer group lag 는 별도 polling 이 필요하다.
검증:
curl -s http://prometheus-server.monitoring.svc:80/api/v1/query \
--data-urlencode 'query=kafka_consumergroup_lag{namespace="gend",consumergroup="bytewax-anomaly"}' | jq .
응답에 data.result 가 빈 배열이면 exporter 가 아직 메트릭을 노출하지 않은 것 ― 트러블슈팅 섹션 4.2 참조.
2. ScaledObject 적용
# AKS prod
kubectl --context aks-genos-prod apply -f infra/keda/scaledobjects/bytewax-anomaly-scaledobject.yaml
# Kind dev
kubectl --context kind-datax-local apply -f infra/keda/scaledobjects/bytewax-anomaly-scaledobject.yaml
검증:
kubectl --context aks-genos-prod get scaledobject -n gend bytewax-anomaly-scaledobject
# NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE
# bytewax-anomaly-scaledobject apps/v1.Deployment gend-streaming-anomaly 1 4 True False
kubectl --context aks-genos-prod get hpa -n gend keda-hpa-bytewax-anomaly-scaledobject
# KEDA 가 자동 생성하는 HPA ― 직접 수정하지 말 것.
READY=True 이지만 ACTIVE=False 는 정상 ― 트리거 threshold 가 아직 도달하지 않은 상태.
3. 트리거 동작 파라미터
| 트리거 | 메트릭 | threshold | activationThreshold | 의도 |
|---|---|---|---|---|
| 1 | sum(kafka_consumergroup_lag{topic=~"anomaly.*"}) | 100 | 10 | Kafka backlog 가 100 메시지 넘으면 Pod 추가 |
| 2 | avg(rate(container_cpu_usage_seconds_total{...}[5m])) * 100 | 70 | 30 | Pod CPU 평균이 5분간 70% 넘으면 Pod 추가 |
스케일 정책:
pollingInterval: 15― 15초마다 Prometheus 쿼리.cooldownPeriod: 300― replica 0 으로 축소 전 5분 idle 대기 (본 ScaledObject 는 minReplicaCount=1 이므로 사실상 미사용).scaleUp.stabilizationWindowSeconds: 60+ 60s 당 +1 Pod.scaleDown.stabilizationWindowSeconds: 300+ 120s 당 -1 Pod ― Kafka offset commit + graceful shutdown 보장.
4. 운영 시나리오
4.1 Burst load 시나리오 (1 → 4 확장)
Bytewax consumer 가 lag 1,000 메시지를 따라잡지 못하는 burst 가 들어왔을 때.
# t=0 : lag 1,000, replicas 1
# t=15s: KEDA polling, lag 트리거 활성화 (threshold 100 초과)
# t=75s: scaleUp stabilization 60s 경과 → +1 Pod (replicas 2)
# t=135s: 여전히 lag 잔존 → +1 Pod (replicas 3)
# t=195s: → +1 Pod (replicas 4, max 도달)
관찰 명령:
watch -n 5 'kubectl --context aks-genos-prod get pods,scaledobject,hpa -n gend -l app.kubernetes.io/component=bytewax-worker'
Prometheus 에서 lag 추이:
sum by (consumergroup) (kafka_consumergroup_lag{namespace="gend",consumergroup="bytewax-anomaly"})
4.2 정상화 후 4 → 1 축소
Burst 해소 후 lag 가 threshold 아래로 떨어진 시점.
# t=0 : lag 0, replicas 4, CPU 평균 20%
# t=15s: 두 트리거 모두 비활성화
# t=300s: scaleDown stabilization 5분 경과 → -1 Pod (replicas 3)
# t=420s: → -1 Pod (replicas 2)
# t=540s: → -1 Pod (replicas 1, minReplicaCount 도달)
축소 중 graceful shutdown:
- KEDA → HPA → Deployment 가 Pod 1개 삭제 시그널.
- Pod 가
preStop의sleep 2실행 (in-flight poll 완료 대기). - SIGTERM 전송,
gend_streaming.__main__의finally블록이 Kafka producer flush + offset commit. terminationGracePeriodSeconds: 30안에 종료.
축소 후에도 Kafka rebalance 가 진행 중일 수 있으므로 즉시 다음 burst 가 들어오면 lag 회복까지 1~2 polling cycle 의 지연이 있을 수 있다.
4.3 ScaledObject 일시 정지
운영자가 의도적으로 자동 스케일을 멈추고 싶을 때.
kubectl --context aks-genos-prod annotate scaledobject bytewax-anomaly-scaledobject \
-n gend autoscaling.keda.sh/paused-replicas=2 --overwrite
# replicas 가 2 로 고정되고 트리거 무시.
# 재개
kubectl --context aks-genos-prod annotate scaledobject bytewax-anomaly-scaledobject \
-n gend autoscaling.keda.sh/paused-replicas-
5. 트러블슈팅
5.1 HPA 충돌
증상: 다른 HPA 가 같은 Deployment 를 target 하면 KEDA HPA 와 충돌, READY=False.
kubectl --context aks-genos-prod get hpa -n gend -o yaml | grep -A 3 'scaleTargetRef:' | grep gend-streaming-anomaly
수동 HPA 가 있으면 삭제. KEDA 가 만든 keda-hpa-* 만 남겨야 한다.
5.2 Prometheus 메트릭 없음
증상: READY=True, ACTIVE=False 가 영구 지속, burst 가 들어와도 스케일 안 됨.
확인:
# kafka_exporter 가 떠 있는지
kubectl --context aks-genos-prod get pods -A -l app.kubernetes.io/name=kafka-exporter
# Prometheus 가 lag 메트릭을 노출하는지
curl -s http://prometheus-server.monitoring.svc:80/api/v1/label/__name__/values | jq '.data | map(select(startswith("kafka_")))'
kafka_consumergroup_lag 가 안 보이면 kafka_exporter 설치 또는 ServiceMonitor 설정 누락.
5.3 ScaledObject paused
증상: 어노테이션 autoscaling.keda.sh/paused-replicas 가 남아있어 트리거 무시.
kubectl --context aks-genos-prod get scaledobject bytewax-anomaly-scaledobject -n gend \
-o jsonpath='{.metadata.annotations}' | jq .
paused-replicas 어노테이션이 있으면 5.3 운영자가 의도적으로 정지한 상태 ― 5.4.3 의 재개 명령으로 제거.
5.4 minReplicaCount 미준수
증상: minReplicaCount=1 인데 replicas=0 으로 떨어짐.
KEDA 버전 차이 가능 ― idleReplicaCount 와 minReplicaCount 의 의미가 2.x 버전마다 다르다. 본 ScaledObject 는 idleReplicaCount 를 지정하지 않았으므로 minReplicaCount=1 이 floor 다. KEDA 버전 확인:
kubectl --context aks-genos-prod get deployment -n keda keda-operator -o jsonpath='{.spec.template.spec.containers[0].image}'
2.12.x 이상이면 안정적. 2.10 미만은 minReplicaCount=0 으로 떨어지는 회귀가 있었음.
5.5 트리거 threshold 조정
anomaly.* 토픽 partition 수가 증가하거나 메시지 throughput 이 바뀌면 threshold 재조정 필요.
- partition 수 증가 →
maxReplicaCount도 같이 증가 (partition 수 = max replica 권장). - 메시지 throughput 증가 →
kafka_consumergroup_lagthreshold 를 100 → 500 으로 올려 hysteresis 확보. - CPU 가 lag 보다 먼저 한계에 닿으면 → CPU threshold 를 70 → 60 으로 내리거나 Pod resource limits 상향.
조정 후 같은 manifest 를 kubectl apply 하면 KEDA 가 HPA 를 자동 갱신한다.
6. 회귀 가드
- 본 ScaledObject 는
infra/streaming/bytewax-base/kustomization.yaml의 kustomize tree 와 분리되어 있다. 의도적 분리이며, ScaledObject 는 별도kubectl apply사이클로 적용한다. KEDA 미설치 클러스터에서도 streaming Deployment 자체는 안전하게 가동되도록 한 결정. minReplicaCount=1을 0 으로 내리면 cold consumer 지연 + Kafka rebalance 폭주 가능 ― 의도적 변경 시 [feedback_no_workarounds] 메모리 참조하여 운영팀 합의 필요.- AKS prod 에서 KEDA HPA 와 수동 HPA 가 같은 Deployment 를 target 하면
READY=False― 5.1 참조.