본문으로 건너뛰기

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 chart prometheus-community/prometheus-kafka-exporter 로 설치. namespace gendkafka.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. 트리거 동작 파라미터

트리거메트릭thresholdactivationThreshold의도
1sum(kafka_consumergroup_lag{topic=~"anomaly.*"})10010Kafka backlog 가 100 메시지 넘으면 Pod 추가
2avg(rate(container_cpu_usage_seconds_total{...}[5m])) * 1007030Pod 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:

  1. KEDA → HPA → Deployment 가 Pod 1개 삭제 시그널.
  2. Pod 가 preStopsleep 2 실행 (in-flight poll 완료 대기).
  3. SIGTERM 전송, gend_streaming.__main__finally 블록이 Kafka producer flush + offset commit.
  4. 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 버전 차이 가능 ― idleReplicaCountminReplicaCount 의 의미가 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_lag threshold 를 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 참조.