Skip to content

KEDA를 활용한 Istio 메트릭 기반 오토스케일링

지원 버전: KEDA 2.18, Istio 1.28 마지막 업데이트: 2026년 2월 19일 Kubernetes 호환성: 1.34

이 문서는 Istio 메트릭을 활용한 실전 오토스케일링 전략을 다룹니다. KEDA를 사용하여 Prometheus 및 CloudWatch 메트릭을 기반으로 워크로드를 스케일링하는 다양한 패턴과 실무 사례를 제공합니다.

학습 목표:

  • Prometheus PromQL을 활용한 정교한 스케일링 정책 작성
  • CloudWatch 메트릭 통합 및 AWS 서비스 조합
  • RPS, Latency, 에러율 등 다양한 메트릭 기반 전략
  • Circuit Breaker 및 시간대별 예측 스케일링
  • 프로덕션 환경을 위한 안정화 및 모니터링

목차

  1. 개요
  2. 아키텍처
  3. Prometheus 메트릭 기반 스케일링
  4. CloudWatch 메트릭 기반 스케일링
  5. 실전 스케일링 전략
  6. 모범 사례
  7. 문제 해결
  8. 참고: KEDA 설치

개요

이 문서는 Istio 메트릭을 활용한 실전 오토스케일링 전략에 초점을 맞춥니다. KEDA는 Kubernetes HPA를 확장하여 Prometheus 및 CloudWatch의 복잡한 메트릭 쿼리를 기반으로 스케일링할 수 있게 해줍니다.

핵심 Istio 메트릭

Istio Envoy 프록시가 제공하는 메트릭을 스케일링에 활용합니다:

메트릭설명스케일링 활용
istio_requests_total총 요청 수RPS 기반 스케일링
istio_request_duration_milliseconds요청 지연 시간지연 기반 스케일링
istio_tcp_connections_opened_totalTCP 연결 수연결 기반 스케일링
istio_request_bytes_sum요청 바이트처리량 기반 스케일링
envoy_cluster_upstream_rq_pending_overflowCircuit Breaker overflow과부하 감지

왜 KEDA를 사용하는가?

기본 Kubernetes HPA와 비교했을 때 KEDA의 장점:

기능Kubernetes HPAKEDA
메트릭 소스CPU/Memory + Custom Metrics API60+ Scaler 직접 지원
PromQL 쿼리⚠️ Custom Metrics Adapter 필요✅ 네이티브 지원
CloudWatch 통합❌ 불가능✅ 직접 쿼리
Scale to Zero❌ 최소 1개✅ 0개 가능
다중 메트릭⚠️ 제한적✅ 여러 트리거 조합
Cron 스케줄❌ 미지원✅ 시간대별 스케일링

이 문서의 초점: KEDA 설치보다는 Prometheus와 CloudWatch 메트릭을 활용한 실전 스케일링 패턴과 전략에 집중합니다.

주요 스케일링 전략

이 문서에서 다루는 실전 스케일링 패턴:

전략주 메트릭적합한 시나리오핵심 장점
RPS 기반istio_requests_totalAPI 서버, 웹 서비스직관적, 구현 간단
Latency 기반P50/P95/P99 지연 시간결제, 주문 등 지연 민감 서비스사용자 경험 보장
에러율 기반5xx 응답 비율고가용성 필수 서비스빠른 장애 대응
복합 메트릭RPS + Latency + Error프로덕션 서비스안정적, 정확한 스케일링
Circuit Breaker 기반overflow, connection pool외부 의존성 많은 서비스연쇄 장애 방지
시간대별 예측Cron + 메트릭트래픽 패턴 예측 가능비용 최적화, 사전 대응

아키텍처

메트릭 기반 스케일링 흐름

KEDA가 Prometheus에서 Istio 메트릭을 조회해 임계값과 비교한 뒤 HPA를 통해 Pod 수를 조정하는 시퀀스

ScaledObject 기본 구조

KEDA의 핵심은 ScaledObject CRD입니다. Prometheus나 CloudWatch 메트릭을 기반으로 HPA를 자동 생성/관리합니다:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: my-app-scaler
  namespace: default
spec:
  # 스케일 대상
  scaleTargetRef:
    name: my-app           # Deployment 이름
    kind: Deployment

  # 스케일링 정책
  pollingInterval: 30      # 30초마다 메트릭 확인
  cooldownPeriod: 300      # 스케일 다운 후 5분 대기
  minReplicaCount: 2       # 최소 Pod 수
  maxReplicaCount: 20      # 최대 Pod 수

  # 메트릭 트리거
  triggers:
  - type: prometheus       # 또는 aws-cloudwatch
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |             # PromQL 쿼리
        sum(rate(istio_requests_total{
          destination_workload="my-app"
        }[1m]))
      threshold: '1000'    # 임계값: 1000 RPS

Prometheus 메트릭 기반 스케일링

1. RPS (Requests Per Second) 기반 스케일링

ScaledObject 정의

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-rps-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  # 스케일링 정책
  pollingInterval: 30  # 30초마다 메트릭 확인
  cooldownPeriod: 300  # 스케일 다운 후 5분 대기
  minReplicaCount: 2   # 최소 레플리카
  maxReplicaCount: 20  # 최대 레플리카

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(rate(istio_requests_total{
          destination_workload="reviews",
          destination_workload_namespace="default",
          response_code=~"2.*"
        }[1m]))
      threshold: '100'  # 100 RPS 이상이면 스케일 아웃
      activationThreshold: '50'  # 50 RPS 이상이면 활성화

동작 방식

RPS 임계값에 따라 레플리카를 늘리거나 줄이고 다시 대기 상태로 순환하는 KEDA 스케일링 결정 루프

2. Latency (지연 시간) 기반 스케일링

P95 지연 시간으로 스케일링

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-latency-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 20

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      # P95 지연 시간 (95th percentile)
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="reviews",
            destination_workload_namespace="default"
          }[2m])) by (le)
        )
      threshold: '200'  # 200ms 이상이면 스케일 아웃
      activationThreshold: '100'

P50 및 P99 조합 스케일링

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-multi-latency-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 20

  # 여러 트리거 중 하나라도 임계값 초과 시 스케일링
  triggers:
  # P50 지연 시간
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.50,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="reviews",
            destination_workload_namespace="default"
          }[2m])) by (le)
        )
      threshold: '50'  # P50 > 50ms

  # P95 지연 시간
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="reviews",
            destination_workload_namespace="default"
          }[2m])) by (le)
        )
      threshold: '200'  # P95 > 200ms

  # P99 지연 시간 (극단적 상황)
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.99,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="reviews",
            destination_workload_namespace="default"
          }[2m])) by (le)
        )
      threshold: '500'  # P99 > 500ms

3. 성공률 기반 스케일링

에러율이 높을 때 스케일 아웃하여 부하 분산:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-error-rate-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 20

  triggers:
  # 에러율이 5% 이상이면 스케일 아웃
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        (
          sum(rate(istio_requests_total{
            destination_workload="reviews",
            response_code=~"5.*"
          }[2m]))
          /
          sum(rate(istio_requests_total{
            destination_workload="reviews"
          }[2m]))
        ) * 100
      threshold: '5'  # 5% 에러율
      activationThreshold: '2'

4. 복합 메트릭 스케일링

RPS와 Latency를 함께 고려:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-composite-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 20

  # 고급 스케일링 동작
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 300  # 5분 안정화
          policies:
          - type: Percent
            value: 10  # 최대 10%씩 감소
            periodSeconds: 60
        scaleUp:
          stabilizationWindowSeconds: 0  # 즉시 스케일 아웃
          policies:
          - type: Percent
            value: 50  # 최대 50%씩 증가
            periodSeconds: 60
          - type: Pods
            value: 5  # 한 번에 최대 5개 추가
            periodSeconds: 60
          selectPolicy: Max  # 더 큰 값 선택

  triggers:
  # RPS 기반
  - type: prometheus
    metricType: AverageValue
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(rate(istio_requests_total{
          destination_workload="reviews",
          destination_workload_namespace="default"
        }[1m])) / count(kube_pod_info{pod=~"reviews-.*"})
      threshold: '50'  # Pod당 50 RPS

  # P95 Latency 기반
  - type: prometheus
    metricType: Value
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="reviews"
          }[2m])) by (le)
        )
      threshold: '200'  # P95 > 200ms

CloudWatch 메트릭 기반 스케일링

개요

CloudWatch는 Prometheus보다 응답 속도가 느리지만 (1-3분 지연), AWS 네이티브 서비스와의 통합과 장기 보관에 유리합니다.

사용 시나리오:

  • ✅ AWS 서비스 메트릭과 조합 (ALB, RDS, SQS 등)
  • ✅ 장기 추세 분석 및 비용 최적화
  • ✅ 멀티 리전 환경에서 중앙 집중 모니터링
  • ❌ 실시간 스케일링 (Prometheus 권장)

전제 조건: Istio 메트릭이 CloudWatch로 전송되고 있어야 합니다. ADOT Collector 설정은 참고: KEDA 설치 섹션을 참조하세요.

CloudWatch 메트릭으로 스케일링

RPS 기반 스케일링

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-cloudwatch-rps
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  pollingInterval: 60  # CloudWatch는 1분 간격 권장
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 20

  triggers:
  - type: aws-cloudwatch
    metadata:
      namespace: IstioMetrics
      metricName: IstioRequestsTotal
      dimensionName: destination_workload
      dimensionValue: reviews
      targetMetricValue: '1000'  # 1000 요청/분
      minMetricValue: '100'

      # 통계 유형
      metricStatPeriod: '60'  # 1분
      metricStat: Sum

      # AWS 리전
      awsRegion: us-west-2

      # IRSA 사용
      identityOwner: operator

Latency 기반 스케일링

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-cloudwatch-latency
  namespace: default
spec:
  scaleTargetRef:
    name: reviews
    kind: Deployment

  pollingInterval: 60
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 20

  triggers:
  - type: aws-cloudwatch
    metadata:
      namespace: IstioMetrics
      metricName: IstioRequestDuration
      dimensionName: destination_workload
      dimensionValue: reviews

      # P95 지연 시간 (CloudWatch에서 계산)
      targetMetricValue: '200'  # 200ms
      minMetricValue: '50'

      metricStatPeriod: '60'
      metricStat: 'p95'  # 95th percentile

      awsRegion: us-west-2
      identityOwner: operator

실전 스케일링 전략

전략 1: 트래픽 패턴 기반 예측 스케일링

시간대별 트래픽 패턴을 고려한 사전 스케일링:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: frontend-predictive-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: frontend
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 50

  # 고급 HPA 동작 설정
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 600  # 10분 안정화
          policies:
          - type: Percent
            value: 10
            periodSeconds: 120  # 2분마다 10%씩 감소
        scaleUp:
          stabilizationWindowSeconds: 0
          policies:
          - type: Percent
            value: 100  # 한 번에 2배까지 증가 가능
            periodSeconds: 30
          - type: Pods
            value: 10  # 한 번에 최대 10개 추가
            periodSeconds: 30
          selectPolicy: Max

  triggers:
  # RPS 기반
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(rate(istio_requests_total{
          destination_workload="frontend"
        }[1m])) / scalar(count(up{job="frontend"}))
      threshold: '100'  # Pod당 100 RPS

  # P95 지연 시간
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="frontend"
          }[2m])) by (le)
        )
      threshold: '300'

  # Cron 기반 사전 스케일링 (피크 시간대)
  - type: cron
    metadata:
      timezone: Asia/Seoul
      start: 0 9 * * 1-5  # 평일 오전 9시
      end: 0 18 * * 1-5   # 평일 오후 6시
      desiredReplicas: '20'  # 피크 시간대는 최소 20개

전략 2: Circuit Breaker 상태 기반 스케일링

Circuit이 Open될 때 자동으로 스케일 아웃:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: backend-circuit-breaker-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: backend
    kind: Deployment

  pollingInterval: 15  # Circuit Breaker는 빠른 반응 필요
  cooldownPeriod: 180
  minReplicaCount: 3
  maxReplicaCount: 30

  triggers:
  # Circuit Breaker Overflow 감지
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(increase(envoy_cluster_upstream_rq_pending_overflow{
          cluster_name=~"outbound.*backend.*"
        }[1m]))
      threshold: '10'  # 1분에 10개 이상 overflow
      activationThreshold: '5'

  # Upstream connection pool saturation
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(envoy_cluster_upstream_cx_active{
          cluster_name=~"outbound.*backend.*"
        })
        /
        sum(envoy_cluster_circuit_breakers_default_cx_open{
          cluster_name=~"outbound.*backend.*"
        }) * 100
      threshold: '80'  # Connection pool 80% 이상 사용

전략 3: 다단계 스케일링 (Tiered Scaling)

부하 수준에 따라 다른 스케일링 속도 적용:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: payment-tiered-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: payment-service
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 3
  maxReplicaCount: 50

  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleUp:
          policies:
          # 낮은 부하 (< 임계값 150%): 천천히 증가
          - type: Percent
            value: 20
            periodSeconds: 120
          # 중간 부하 (150-200%): 빠르게 증가
          - type: Percent
            value: 50
            periodSeconds: 60
          # 높은 부하 (> 200%): 매우 빠르게 증가
          - type: Pods
            value: 10
            periodSeconds: 30
          selectPolicy: Max

        scaleDown:
          policies:
          - type: Percent
            value: 5  # 천천히 감소 (5%씩)
            periodSeconds: 180  # 3분마다

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(rate(istio_requests_total{
          destination_workload="payment-service",
          response_code=~"2.*"
        }[1m]))
      threshold: '500'  # 500 RPS

전략 4: 비용 최적화 스케일링

업무 시간과 비업무 시간을 구분:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: analytics-cost-optimized-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: analytics-service
    kind: Deployment

  pollingInterval: 60
  cooldownPeriod: 600  # 비용 최적화를 위해 더 긴 대기
  minReplicaCount: 1
  maxReplicaCount: 30

  triggers:
  # 업무 시간 (09:00-18:00): 적극적 스케일링
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        (
          sum(rate(istio_requests_total{
            destination_workload="analytics-service"
          }[2m]))
          and
          (hour() >= 9 and hour() < 18)
        )
      threshold: '50'
      activationThreshold: '20'

  # 비업무 시간: Scale to Zero 허용
  - type: cron
    metadata:
      timezone: Asia/Seoul
      start: 0 18 * * *  # 오후 6시
      end: 0 9 * * *     # 오전 9시
      desiredReplicas: '0'  # Scale to Zero

전략 5: Gateway 메트릭 기반 스케일링

Istio Gateway의 부하를 모니터링하여 백엔드 스케일링:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: backend-gateway-based-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: backend
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 40

  triggers:
  # Gateway를 통한 유입 트래픽 모니터링
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(rate(istio_requests_total{
          source_workload="istio-ingressgateway",
          destination_service="backend.default.svc.cluster.local"
        }[1m]))
      threshold: '1000'

  # Gateway의 pending 연결 수
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(envoy_http_downstream_rq_active{
          app="istio-ingressgateway"
        })
      threshold: '500'  # 500개 이상의 동시 요청

모범 사례

1. 메트릭 선택 가이드

워크로드 특성(상태 유무, 트래픽 예측 가능성, 지연 시간 민감도)에 따라 RPS, Latency, 예측, 복합 스케일링 전략을 선택하는 의사결정 트리

권장 메트릭:

워크로드 유형주 메트릭보조 메트릭이유
API 서버RPSP95 Latency요청 수가 부하의 직접적 지표
웹 서버RPS에러율동시 연결 수보다 요청 수가 중요
데이터 처리P95 LatencyCPU/Memory처리 시간이 부하 지표
StreamingTCP 연결 수처리량연결 수가 리소스 소비의 핵심
배치 작업큐 길이처리 시간작업 대기 수가 스케일링 기준

2. 임계값 설정 가이드

yaml
# 적절한 임계값 찾기 프로세스

# 1단계: 현재 워크로드 측정
# 평상시 RPS
kubectl exec -it prometheus-xxx -n istio-system -- promtool query instant \
  'sum(rate(istio_requests_total{destination_workload="reviews"}[5m]))'

# 피크 시간대 RPS
# 평상시: ~500 RPS
# 피크: ~2000 RPS

# 2단계: Pod당 처리 능력 측정
# 부하 테스트 수행
kubectl run load-test --image=fortio/fortio -- load -c 50 -qps 0 -t 60s http://reviews:9080

# 결과: Pod당 약 200 RPS까지 P95 < 100ms 유지

# 3단계: 임계값 계산
# 목표 P95: 100ms
# Pod당 처리 능력: 200 RPS
# 안전 마진: 70% (140 RPS/pod)
# → threshold: '140'

# 4단계: ScaledObject 작성
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: reviews-optimized-scaler
spec:
  scaleTargetRef:
    name: reviews
  minReplicaCount: 3  # 평상시 500 RPS / 140 = 3.5 → 4개
  maxReplicaCount: 20  # 피크 2000 RPS / 140 = 14.2 → 20개 (여유)
  triggers:
  - type: prometheus
    metadata:
      query: |
        sum(rate(istio_requests_total{destination_workload="reviews"}[1m]))
        / count(kube_pod_info{pod=~"reviews-.*"})
      threshold: '140'  # Pod당 140 RPS

3. 스케일링 속도 조정

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: balanced-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: myapp
    kind: Deployment

  pollingInterval: 30
  cooldownPeriod: 300
  minReplicaCount: 2
  maxReplicaCount: 50

  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        # 스케일 다운: 보수적 (서비스 안정성 우선)
        scaleDown:
          stabilizationWindowSeconds: 600  # 10분 관찰
          policies:
          - type: Percent
            value: 10  # 10%씩 감소
            periodSeconds: 180  # 3분마다
          - type: Pods
            value: 2  # 또는 최대 2개씩
            periodSeconds: 180
          selectPolicy: Min  # 더 보수적인 값 선택

        # 스케일 업: 적극적 (빠른 대응)
        scaleUp:
          stabilizationWindowSeconds: 0  # 즉시
          policies:
          - type: Percent
            value: 100  # 2배까지 증가
            periodSeconds: 30
          - type: Pods
            value: 10  # 또는 10개씩
            periodSeconds: 30
          selectPolicy: Max  # 더 적극적인 값 선택

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: sum(rate(istio_requests_total{destination_workload="myapp"}[1m]))
      threshold: '1000'

4. 멀티 클러스터 환경에서의 스케일링

yaml
# Cluster 1: 주 트래픽 처리
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: frontend-cluster1-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: frontend
  minReplicaCount: 5
  maxReplicaCount: 30

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      # 글로벌 트래픽의 60%를 이 클러스터에서 처리
      query: |
        sum(rate(istio_requests_total{
          destination_workload="frontend",
          source_cluster="cluster1"
        }[1m])) * 0.6
      threshold: '600'
---
# Cluster 2: 보조 트래픽 처리
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: frontend-cluster2-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: frontend
  minReplicaCount: 3
  maxReplicaCount: 20

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      # 글로벌 트래픽의 40%
      query: |
        sum(rate(istio_requests_total{
          destination_workload="frontend",
          source_cluster="cluster2"
        }[1m])) * 0.4
      threshold: '400'

모범 사례

1. 메트릭 수집 최적화

yaml
# Prometheus scrape 간격 조정
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus
  namespace: istio-system
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s  # 기본 15초
      evaluation_interval: 15s

    scrape_configs:
    # Istio 메트릭은 더 자주 수집
    - job_name: 'istio-mesh'
      scrape_interval: 10s  # 10초
      kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names:
          - default
          - production
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true

2. 스케일링 안정성 확보

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: stable-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: myapp

  # 1. 적절한 폴링 간격
  pollingInterval: 30  # 너무 짧으면 불안정, 너무 길면 반응 느림

  # 2. 충분한 쿨다운
  cooldownPeriod: 300  # 5분은 일반적으로 적절

  # 3. 안전한 최소/최대값
  minReplicaCount: 2  # 0은 위험, 최소 2개 권장
  maxReplicaCount: 20  # 클러스터 용량의 70% 이하

  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          # 4. 긴 안정화 윈도우
          stabilizationWindowSeconds: 600
          policies:
          - type: Percent
            value: 10
            periodSeconds: 120

3. 모니터링 및 알림

yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: keda-scaling-alerts
  namespace: keda
spec:
  groups:
  - name: keda-scaling
    interval: 30s
    rules:
    # 최대 레플리카에 도달
    - alert: KEDAMaxReplicasReached
      expr: |
        kube_horizontalpodautoscaler_status_current_replicas
        >= kube_horizontalpodautoscaler_spec_max_replicas
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "KEDA scaled to maximum replicas"
        description: "{{ $labels.horizontalpodautoscaler }} has reached max replicas ({{ $value }})"

    # 스케일링 실패
    - alert: KEDAScalingFailed
      expr: |
        increase(keda_scaler_errors_total[5m]) > 0
      labels:
        severity: critical
      annotations:
        summary: "KEDA scaling failed"
        description: "KEDA scaler {{ $labels.scaledObject }} has errors"

    # 빈번한 스케일링 (Flapping)
    - alert: KEDAFlapping
      expr: |
        rate(keda_scaler_active[10m]) > 0.1
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "KEDA is flapping"
        description: "ScaledObject {{ $labels.scaledObject }} is scaling too frequently"

4. 리소스 제한 설정

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews
  namespace: default
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: reviews
        image: istio/examples-bookinfo-reviews-v1:1.17.0

        # 리소스 요청/제한 (스케일링 계산에 중요)
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 200m
            memory: 256Mi

        # Readiness Probe (스케일 아웃 시 안전성)
        readinessProbe:
          httpGet:
            path: /health
            port: 9080
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 3

        # Liveness Probe
        livenessProbe:
          httpGet:
            path: /health
            port: 9080
          initialDelaySeconds: 30
          periodSeconds: 10

문제 해결

1. KEDA가 메트릭을 가져오지 못함

증상:

bash
kubectl get scaledobject -n default
# STATUS: Unknown

원인 분석:

bash
# 1. KEDA Operator 로그 확인
kubectl logs -n keda -l app=keda-operator

# 2. ScaledObject 상태 확인
kubectl describe scaledobject reviews-rps-scaler -n default

# 3. Prometheus 연결 테스트
kubectl run curl-test --image=curlimages/curl -it --rm -- \
  curl -s http://prometheus.istio-system.svc:9090/api/v1/query \
  --data-urlencode 'query=up'

해결 방법:

  1. Prometheus 주소 확인:
bash
# Prometheus Service 확인
kubectl get svc -n istio-system | grep prometheus

# ScaledObject에서 올바른 주소 사용
serverAddress: http://prometheus.istio-system.svc:9090
  1. PromQL 쿼리 테스트:
bash
# Prometheus UI에서 직접 쿼리 테스트
kubectl port-forward -n istio-system svc/prometheus 9090:9090

# 브라우저: http://localhost:9090
# 쿼리 입력 후 결과 확인

2. 스케일링이 너무 느림

증상: 트래픽 급증 시 스케일 아웃이 늦음

해결 방법:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: fast-scaler
spec:
  # 1. 폴링 간격 단축
  pollingInterval: 15  # 30초 → 15초

  # 2. 스케일 업 안정화 윈도우 제거
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleUp:
          stabilizationWindowSeconds: 0  # 즉시 반응
          policies:
          - type: Pods
            value: 5  # 한 번에 5개씩
            periodSeconds: 30

  # 3. 낮은 activation threshold
  triggers:
  - type: prometheus
    metadata:
      query: sum(rate(istio_requests_total{...}[1m]))
      threshold: '100'
      activationThreshold: '30'  # 낮은 임계값으로 조기 활성화

3. Flapping (불안정한 스케일링)

증상: Pod 수가 계속 증가/감소 반복

원인: 임계값이 너무 민감하거나 안정화 기간 부족

해결 방법:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: stable-scaler
spec:
  # 1. 더 긴 쿨다운
  cooldownPeriod: 600  # 10분

  # 2. 더 긴 PromQL 평가 기간
  triggers:
  - type: prometheus
    metadata:
      query: |
        sum(rate(istio_requests_total{...}[5m]))  # 1m → 5m
      threshold: '100'

  # 3. 보수적인 스케일 다운
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 600
          policies:
          - type: Percent
            value: 5  # 5%씩만 감소
            periodSeconds: 180

4. CloudWatch 지연 시간

증상: CloudWatch 메트릭이 실시간이 아님 (1-3분 지연)

해결 방법:

yaml
# Prometheus 메트릭을 주로 사용하고, CloudWatch는 보조로
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: hybrid-metrics-scaler
spec:
  triggers:
  # 주 메트릭: Prometheus (실시간)
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: sum(rate(istio_requests_total{...}[1m]))
      threshold: '1000'

  # 보조 메트릭: CloudWatch (추세 분석)
  - type: aws-cloudwatch
    metadata:
      namespace: IstioMetrics
      metricName: IstioRequestsTotal
      targetMetricValue: '5000'  # 더 높은 임계값
      metricStatPeriod: '300'  # 5분 집계

실전 예제

예제 1: 이커머스 결제 서비스

지연 시간이 매우 중요한 서비스:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: payment-service-scaler
  namespace: production
spec:
  scaleTargetRef:
    name: payment-service
    kind: Deployment

  pollingInterval: 15  # 빠른 반응
  cooldownPeriod: 180  # 3분 쿨다운
  minReplicaCount: 5   # 항상 5개 이상 유지
  maxReplicaCount: 50

  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleUp:
          stabilizationWindowSeconds: 0
          policies:
          - type: Percent
            value: 100  # 빠르게 2배로
            periodSeconds: 30
        scaleDown:
          stabilizationWindowSeconds: 900  # 15분 안정화
          policies:
          - type: Percent
            value: 5
            periodSeconds: 300  # 5분마다 5%씩

  triggers:
  # P50 지연 시간 (일반적인 경우)
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.50,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="payment-service",
            destination_workload_namespace="production"
          }[1m])) by (le)
        )
      threshold: '50'  # P50 > 50ms
      activationThreshold: '30'

  # P95 지연 시간 (품질 보장)
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="payment-service",
            destination_workload_namespace="production"
          }[1m])) by (le)
        )
      threshold: '200'  # P95 > 200ms

  # 에러율 (5% 이상이면 긴급 스케일 아웃)
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        (
          sum(rate(istio_requests_total{
            destination_workload="payment-service",
            response_code=~"5.*"
          }[1m]))
          /
          sum(rate(istio_requests_total{
            destination_workload="payment-service"
          }[1m]))
        ) * 100
      threshold: '5'

예제 2: 데이터 처리 서비스

배치 처리 및 큐 기반 스케일링:

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: data-processor-scaler
  namespace: default
spec:
  scaleTargetRef:
    name: data-processor
    kind: Deployment

  pollingInterval: 60  # 배치는 느린 반응 허용
  cooldownPeriod: 600  # 10분 쿨다운
  minReplicaCount: 0   # Scale to Zero 허용
  maxReplicaCount: 30

  triggers:
  # SQS 큐 길이 (주 메트릭)
  - type: aws-sqs-queue
    metadata:
      queueURL: https://sqs.us-west-2.amazonaws.com/123456789/data-processing-queue
      queueLength: '10'  # 큐에 10개 이상이면 활성화
      awsRegion: us-west-2
      identityOwner: operator

  # Istio 처리 시간 (보조 메트릭)
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="data-processor"
          }[5m])) by (le)
        )
      threshold: '5000'  # 5초 이상 소요 시 스케일 아웃

예제 3: 멀티 리전 글로벌 서비스

지연 시간 기반 지역별 스케일링:

yaml
# US Region
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: api-us-scaler
  namespace: default
  labels:
    region: us-east-1
spec:
  scaleTargetRef:
    name: api-service
  minReplicaCount: 3
  maxReplicaCount: 30

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      # US 사용자 트래픽만 집계
      query: |
        sum(rate(istio_requests_total{
          destination_workload="api-service",
          source_canonical_service=~".*-us-.*"
        }[1m]))
      threshold: '500'

  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      # US 지역 P95 지연 시간
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="api-service",
            destination_region="us-east-1"
          }[2m])) by (le)
        )
      threshold: '100'  # US 사용자는 100ms 목표
---
# EU Region
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: api-eu-scaler
  namespace: default
  labels:
    region: eu-west-1
spec:
  scaleTargetRef:
    name: api-service
  minReplicaCount: 2
  maxReplicaCount: 20

  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        sum(rate(istio_requests_total{
          destination_workload="api-service",
          source_canonical_service=~".*-eu-.*"
        }[1m]))
      threshold: '300'

  - type: prometheus
    metadata:
      serverAddress: http://prometheus.istio-system.svc:9090
      query: |
        histogram_quantile(0.95,
          sum(rate(istio_request_duration_milliseconds_bucket{
            destination_workload="api-service",
            destination_region="eu-west-1"
          }[2m])) by (le)
        )
      threshold: '150'  # EU는 150ms 허용

참고: KEDA 설치

참고: 이 섹션은 KEDA를 처음 설치하는 경우에만 필요합니다. 이미 설치되어 있다면 Prometheus 메트릭 기반 스케일링부터 시작하세요.

Helm으로 설치

bash
# KEDA Helm 레포지토리 추가
helm repo add kedacore https://kedacore.github.io/charts
helm repo update

# KEDA 설치
helm install keda kedacore/keda \
  --namespace keda \
  --create-namespace \
  --set prometheus.metricServer.enabled=true \
  --set prometheus.metricServer.port=9022 \
  --set operator.replicaCount=2

# 설치 확인
kubectl get pods -n keda
# 출력:
# NAME                                      READY   STATUS
# keda-operator-xxxxx                       1/1     Running
# keda-operator-metrics-apiserver-xxxxx     1/1     Running

AWS IRSA 설정 (CloudWatch 사용 시)

CloudWatch 메트릭을 사용하는 경우 KEDA Operator에 IAM 권한이 필요합니다:

bash
# IRSA 설정
eksctl create iamserviceaccount \
  --name keda-operator \
  --namespace keda \
  --cluster my-cluster \
  --attach-policy-arn arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess \
  --approve \
  --override-existing-serviceaccounts

# ServiceAccount 확인
kubectl get sa keda-operator -n keda -o yaml | grep eks.amazonaws.com/role-arn

CloudWatch 메트릭 전송 설정 (선택 사항)

CloudWatch 메트릭 기반 스케일링을 사용하려면 ADOT Collector로 Istio 메트릭을 전송해야 합니다:

1단계: ADOT Collector 설치

yaml
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: istio-metrics-collector
  namespace: istio-system
spec:
  mode: deployment
  serviceAccount: adot-collector
  config: |
    receivers:
      prometheus:
        config:
          scrape_configs:
          - job_name: 'istio-mesh'
            scrape_interval: 60s  # CloudWatch는 1분 단위 권장
            kubernetes_sd_configs:
            - role: endpoints
              namespaces:
                names:
                - default
            relabel_configs:
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
              action: keep
              regex: true

    processors:
      batch:
        timeout: 60s
      metricstransform:
        transforms:
        - include: istio_requests_total
          action: update
          new_name: IstioRequestsTotal
        - include: istio_request_duration_milliseconds
          action: update
          new_name: IstioRequestDuration

    exporters:
      awsemf:
        namespace: IstioMetrics
        region: us-west-2
        dimension_rollup_option: NoDimensionRollup
        metric_declarations:
        - dimensions: [[destination_workload, destination_workload_namespace]]
          metric_name_selectors:
          - IstioRequestsTotal
          - IstioRequestDuration

    service:
      pipelines:
        metrics:
          receivers: [prometheus]
          processors: [batch, metricstransform]
          exporters: [awsemf]

2단계: IRSA 설정

bash
# IRSA 정책 생성
cat > adot-cloudwatch-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["cloudwatch:PutMetricData"],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "cloudwatch:namespace": "IstioMetrics"
        }
      }
    }
  ]
}
EOF

aws iam create-policy \
  --policy-name ADOTCollectorCloudWatchPolicy \
  --policy-document file://adot-cloudwatch-policy.json

eksctl create iamserviceaccount \
  --name adot-collector \
  --namespace istio-system \
  --cluster my-cluster \
  --attach-policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/ADOTCollectorCloudWatchPolicy \
  --approve

설치 완료 후 Prometheus 메트릭 기반 스케일링 또는 CloudWatch 메트릭 기반 스케일링 섹션으로 돌아가세요.


참고 자료

공식 문서

관련 문서

요약

메트릭 소스 선택 가이드

메트릭 소스장점단점권장 사용
Prometheus• 실시간 반응 (15-30초)
• PromQL 강력한 쿼리
• 클러스터 내부 통신
• 장기 보관 비용
• 클러스터 의존성
실시간 스케일링, 대부분의 워크로드
CloudWatch• AWS 서비스 통합
• 장기 보관
• 멀티 리전 지원
• 1-3분 지연
• 비용 (메트릭 수에 비례)
추세 분석, AWS 서비스 조합

스케일링 전략 선택 가이드

워크로드 유형주 메트릭보조 메트릭권장 설정
API 서버RPS (Pod당)P95 LatencypollingInterval: 30, cooldownPeriod: 300
결제/주문P50/P95 Latency에러율pollingInterval: 15, 빠른 스케일 아웃
데이터 처리큐 길이, P95 LatencyCPU/MemorypollingInterval: 60, Scale to Zero 허용
웹 프론트엔드RPS, P95 LatencyGateway 메트릭Cron 기반 사전 스케일링
마이크로서비스RPS, Circuit Breaker에러율다단계 스케일링 정책

프로덕션 체크리스트

스케일링 정책을 프로덕션에 적용하기 전 확인 사항:

  • [ ] 임계값 검증: 부하 테스트로 적절한 threshold 값 확인
  • [ ] 안정화 설정: stabilizationWindowSeconds 충분히 설정 (스케일 다운 최소 300초)
  • [ ] 리소스 제한: Pod의 requestslimits 명확히 정의
  • [ ] Health Check: Readiness/Liveness Probe 설정
  • [ ] 모니터링: KEDAMaxReplicasReached, KEDAScalingFailed 알림 설정
  • [ ] Flapping 방지: 긴 PromQL 평가 기간 ([5m]) 및 보수적 스케일 다운
  • [ ] 최소/최대값: 클러스터 용량의 70% 이하로 maxReplicaCount 설정
  • [ ] Fallback: Prometheus 장애 시 CPU/Memory 기반 HPA 백업

권장 시작 경로

1단계: RPS 기반 스케일링 구현
   └─> 단일 메트릭으로 시작, 임계값 조정

2단계: Latency 메트릭 추가
   └─> P95 지연 시간 모니터링 및 스케일링

3단계: 복합 메트릭 전략
   └─> RPS + Latency 조합으로 안정성 확보

4단계: 고급 전략 적용
   └─> Circuit Breaker, Cron, 에러율 등 추가

핵심 원칙:

  • Prometheus로 실시간 반응
  • 복합 메트릭으로 안정성 확보
  • 보수적인 스케일 다운, 적극적인 스케일 아웃
  • 지속적인 모니터링과 임계값 조정