본문 바로가기
배포·CI,CD·클라우드

KEDA + Prometheus Adapter로 HPA 커스텀 메트릭 오토스케일링(스레드·큐·지연 기반)

by yamoojin83 2025. 10. 31.

KEDA + Prometheus Adapter로 HPA 커스텀 메트릭 오토스케일링(스레드·큐·지연 기반)

CPU만 보고 스케일하면 늦습니다. 실제로는 웹 스레드풀 사용률, DB 커넥션 대기열, 요청 지연(p95), 메시지 큐 길이가 먼저 비명을 지릅니다. 이 글은 Kubernetes에서 KEDAPrometheus Adapter를 이용해 이런 도메인 메트릭으로 HPA를 구동하는 방법을 단계별로 정리합니다.


1) 아키텍처 개요

Prometheus가 애플리케이션/인프라 지표를 수집하고, Recording Rule로 스케일에 적합한 지표를 만듭니다. Prometheus Adapter는 해당 지표를 Kubernetes Custom Metrics API로 노출합니다. HPA는 그 지표를 목표값으로 삼아 레플리카를 조정하고, KEDA는 큐/외부 지표 이벤트 기반 스케일을 담당합니다.

[App Metrics] → Prometheus → (Recording Rules) → Prometheus Adapter → HPA(Custom Metrics)
[Queue/External] → KEDA Scalers → HPA(External Metrics)

2) 전제 조건

  • 클러스터에 Prometheus & Alertmanager가 동작 중
  • 애플리케이션이 /actuator/prometheus 등으로 지표 노출(Micrometer 권장)
  • kubectl, helm 사용 가능

3) Prometheus Recording Rule: 스레드/대기열/지연 파생 지표

지난 글의 USE 파생지표를 스케일 친화적 이름으로 별도 기록해 둡니다.

# use-hpa.yml
groups:
- name: hpa.targets
  rules:
  - record: hpa:thread_utilization
    expr: (sum by (application) (tomcat_threads_busy))
          / ignoring() (sum by (application) (tomcat_threads_config_max))

  - record: hpa:db_pending
    expr: sum by (application) (hikaricp_connections_pending)

  - record: hpa:http_p95
    expr: histogram_quantile(0.95,
            sum by (le) (rate(http_server_requests_seconds_bucket{uri!~"/actuator.*"}[5m])))

적용 후 리로드:

promtool check rules use-hpa.yml
kubectl -n monitoring create configmap use-hpa --from-file=use-hpa.yml
# Prometheus 설정에 ruleFiles로 포함 후 재기동 또는 SIGHUP

4) Prometheus Adapter 설치 & 매핑

helm으로 배포하고, 매핑 규칙에서 어떤 지표를 Custom Metrics로 노출할지 정의합니다.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prom-adapter prometheus-community/prometheus-adapter -n monitoring \
  --create-namespace \
  -f values-adapter.yaml

values-adapter.yaml의 핵심만:

rules:
  custom:
    - seriesQuery: 'hpa:thread_utilization'
      resources:
        overrides:
          namespace: {resource: "namespace"}
          pod: {resource: "pod"}
      name:
        matches: "hpa:thread_utilization"
        as: "svc_thread_utilization"
      metricsQuery: 'avg(<<.Series>>{namespace="prod"})'

    - seriesQuery: 'hpa:db_pending'
      resources:
        overrides:
          namespace: {resource: "namespace"}
      name:
        matches: "hpa:db_pending"
        as: "db_pending"
      metricsQuery: 'sum(<<.Series>>{namespace="prod"})'

    - seriesQuery: 'hpa:http_p95'
      resources:
        overrides:
          namespace: {resource: "namespace"}
      name:
        matches: "hpa:http_p95"
        as: "http_p95_seconds"
      metricsQuery: 'avg(<<.Series>>{namespace="prod"})'

정상 노출 확인:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta2" | jq .

5) HPA: 커스텀 메트릭으로 스케일

스레드풀 사용률을 0.70으로 유지하도록 목표를 설정합니다. 대기열/지연은 보조 기준으로 함께 쓸 수 있습니다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-api
  namespace: prod
spec:
  minReplicas: 2
  maxReplicas: 20
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 100     # 1분에 최대 2배
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 180
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60
  metrics:
    - type: Pods
      pods:
        metric:
          name: svc_thread_utilization
        target:
          type: AverageValue
          averageValue: "0.70"
    - type: Pods
      pods:
        metric:
          name: http_p95_seconds
        target:
          type: AverageValue
          averageValue: "0.80"

: behavior급격 스케일링을 제한/완화하여 톱니현상(Thrashing)을 줄입니다.


6) KEDA: 큐 길이/외부 지표 기반 스케일

큐(예: RabbitMQ, Kafka)나 외부 서비스 백로그로 스케일하고 싶다면 HPA 대신(또는 함께) KEDA를 사용합니다.

6-1) 설치

helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda -n keda --create-namespace

6-2) Prometheus Scaler(레이트 기반)

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-api-keda
  namespace: prod
spec:
  scaleTargetRef:
    name: order-api
  minReplicaCount: 2
  maxReplicaCount: 30
  cooldownPeriod: 180
  pollingInterval: 15
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring.svc.cluster.local:9090
        metricName: http_requests_rate
        threshold: "200"       # 초당 200req 이상이면 스케일
        query: |
          sum(rate(http_server_requests_seconds_count{namespace="prod"}[1m]))

6-3) RabbitMQ 큐 길이 기반

triggers:
  - type: rabbitmq
    metadata:
      protocol: amqp
      host: amqp://user:pass@rabbitmq:5672/
      queueName: order-events
      mode: QueueLength
      value: "1000"     # 큐 길이 1000 초과 시 스케일

KEDA는 외부 지표를 External Metrics로 HPA에 제공하므로, 트래픽 성격에 맞게 혼합 구성할 수 있습니다.


7) 안정화 파라미터 & 안전장치

  • stabilizationWindowSeconds: 최근 윈도우의 최솟값(ScaleDown)/최댓값(ScaleUp)을 사용해 요동 완화
  • pollingInterval/cooldownPeriod(KEDA): 스케일링 감지 주기/축소 지연 설정
  • 최대/최소 레플리카: 장애 시에도 과금 폭주/과소 프로비저닝 방지
  • 지표 품질: 샘플 누락/지연 시 CPU를 보조 기준으로 추가

8) 실제 튜닝 순서(짧고 확실하게)

  1. 기록식으로 hpa:* 지표를 먼저 만들고 1주일 관찰
  2. HPA 목표값을 “평시 평균 + 10% 여유”로 설정
  3. ScaleUp은 과감하게(예: 100%/분), ScaleDown은 보수적으로(예: 50%/3분)
  4. KEDA로 큐/백로그 스파이크 대응을 보강
  5. 대시보드에 레플리카 수, p95, 스레드 사용률, 큐 길이를 한 화면에 배치

9) 트러블슈팅 빠른 분기

  • “metrics not found” → Adapter rules.custom 매핑/네임스페이스 라벨 확인
  • 스케일이 늦다pollingInterval 단축, ScaleUp 정책 상향, 기록식 평균 윈도우 1m로
  • 스케일 톱니현상 → ScaleDown 안정화 창 증가, 목표값 완화(0.65→0.70), p95 대신 p90
  • KEDA 동작 안 함keda-operator 로그/TriggerAuthentication 시크릿 확인

10) 체크리스트(복붙용)

  • Recording Rules: hpa:thread_utilization, hpa:db_pending, hpa:http_p95
  • Prometheus Adapter 매핑 및 custom.metrics.k8s.io 확인
  • HPA v2 behavior로 안정화 정책 적용
  • KEDA 설치 & Prometheus/Queue Scaler 구성
  • 대시보드: 레플리카/지연/스레드/큐 길이 모니터링
  • 런북: 스케일 실패 시 수동 레플리카 명령/롤백 절차 문서화

결론: CPU를 “보조”로 두고, 사용자 체감에 직접 연결된 메트릭(스레드, 대기열, 지연, 큐 길이)을 스케일 트리거로 삼으세요. KEDA + Prometheus Adapter 조합이면 도입은 가볍고 효과는 즉각적입니다.