Skip to content
Published on

Istio 트래픽 관리 엔진 심층 분석

Authors

들어가며

Istio의 트래픽 관리는 시험 범위의 40%를 차지할 만큼 핵심적인 영역입니다. 이 글에서는 Istio CRD가 어떻게 Envoy 구성으로 변환되고, Envoy가 실제로 트래픽을 어떻게 처리하는지 내부 메커니즘을 분석합니다.

VirtualService에서 Envoy RouteConfiguration으로

변환 파이프라인

VirtualService (Istio CRD)
Pilot 변환 엔진
Envoy Route Configuration
  ├── VirtualHost (호스트 기반 매칭)
  │   ├── Route (경로/헤더 매칭)
  │   │   ├── RouteAction (라우팅 대상)
  │   │   ├── WeightedCluster (가중치 기반 분할)
  │   │   └── RetryPolicy (리트라이 정책)
  │   └── Route (기본 경로)
  └── VirtualHost (다른 호스트)

매칭 우선순위

VirtualService의 HTTP 매치 규칙은 정의 순서대로 평가됩니다. Envoy의 RouteConfiguration에서도 동일한 순서를 유지합니다:

  1. 가장 구체적인 매치가 먼저 (exact > prefix > regex)
  2. 여러 match 블록이 있으면 첫 번째 매칭이 적용
  3. 매치 조건이 없는 route는 catch-all로 동작

트래픽 분할 (Traffic Shifting)

가중치 기반 트래픽 분할의 내부 구현:

# Istio VirtualService
spec:
  http:
    - route:
        - destination:
            host: reviews
            subset: v1
          weight: 75
        - destination:
            host: reviews
            subset: v2
          weight: 25

이것이 Envoy에서는 WeightedCluster로 변환됩니다:

{
  "route": {
    "weighted_clusters": {
      "clusters": [
        {
          "name": "outbound|9080|v1|reviews.default.svc.cluster.local",
          "weight": 75
        },
        {
          "name": "outbound|9080|v2|reviews.default.svc.cluster.local",
          "weight": 25
        }
      ],
      "total_weight": 100
    }
  }
}

Envoy는 각 요청마다 난수를 생성하고, 가중치 범위에 따라 클러스터를 선택합니다. 이는 확률적 분할이므로, 정확히 75:25가 아닌 근사치로 동작합니다.

DestinationRule에서 Envoy Cluster Configuration으로

Cluster 구성 변환

DestinationRule은 Envoy의 Cluster 구성으로 변환됩니다:

DestinationRule
  ├── host → Cluster name prefix
  ├── subsets → 개별 Cluster 생성
  │   ├── subset v1 → outbound|9080|v1|reviews.default.svc.cluster.local
  │   └── subset v2 → outbound|9080|v2|reviews.default.svc.cluster.local
  └── trafficPolicy → Cluster-level 설정
      ├── connectionPool → circuit_breakers
      ├── outlierDetection → outlier_detection
      └── loadBalancer → lb_policy

Cluster 이름 규칙

Envoy Cluster의 이름은 다음 형식을 따릅니다:

방향|포트|서브셋|FQDN

예시:
outbound|9080|v1|reviews.default.svc.cluster.local
inbound|8080||productpage.default.svc.cluster.local

로드밸런싱 알고리즘

Round Robin (기본값)

요청 1Endpoint A
요청 2Endpoint B
요청 3Endpoint C
요청 4Endpoint A  (순환)

Envoy 구현: 각 엔드포인트에 순서대로 요청을 분배합니다. 가중치가 있으면 Weighted Round Robin으로 동작합니다.

Least Connections

Endpoint A: 활성 연결 3Endpoint B: 활성 연결 1개  ← 다음 요청 여기로
Endpoint C: 활성 연결 5

Envoy 구현: O(1) 연산으로 가장 적은 활성 요청을 가진 엔드포인트를 선택합니다.

Random

각 요청마다 무작위로 엔드포인트 선택
대규모에서는 통계적으로 균등 분배

Consistent Hash

세션 어피니티가 필요한 경우 사용합니다:

spec:
  trafficPolicy:
    loadBalancer:
      consistentHashLB:
        httpHeaderName: x-user-id

Envoy 구현: Ketama 해시 알고리즘 기반. 해시 링에 엔드포인트를 배치하고, 요청 키를 해싱하여 가장 가까운 엔드포인트를 선택합니다.

해시 링:
   0 ─── EP_A ─── EP_B ─── EP_C ─── MAX
         │              │
    x-user-id:alice  x-user-id:bob
    (항상 EP_A)      (항상 EP_B)

장점:

  • 엔드포인트 추가/제거 시 최소한의 재매핑
  • 동일 키는 항상 동일 엔드포인트로 (스티키 세션)

서킷 브레이커 구현

DestinationRule outlierDetection에서 Envoy 변환

# Istio DestinationRule
spec:
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 10s
      baseEjectionTime: 30s
      maxEjectionPercent: 50

Envoy에서의 동작:

{
  "outlier_detection": {
    "consecutive_5xx": 5,
    "interval": "10s",
    "base_ejection_time": "30s",
    "max_ejection_percent": 50,
    "enforcing_consecutive_5xx": 100
  }
}

서킷 브레이커 상태 머신

[Closed] ──── 연속 5xx >= 5 ────→ [Open/Ejected]
   ▲                                    │
   │                              baseEjectionTime 경과
   │                                    │
   └──────── 성공 요청 ──────── [Half-Open]
                                   (재시도)

상세 동작:

  1. Closed: 정상 상태. 모든 요청이 엔드포인트로 전달
  2. Open (Ejected): 연속 에러 임계값 초과. 해당 엔드포인트가 로드밸런싱 풀에서 제거
  3. Half-Open: baseEjectionTime 후 엔드포인트가 풀에 복귀. 재실패 시 ejection 시간 증가

연결 풀 기반 서킷 브레이커

spec:
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        h2UpgradePolicy: DEFAULT
        http1MaxPendingRequests: 1024
        http2MaxRequests: 1024
        maxRequestsPerConnection: 10
        maxRetries: 3

Envoy 내부 동작:

요청 도착
maxConnections (100) 초과? ──→ 503 (overflow)
No
http1MaxPendingRequests (1024) 초과? ──→ 503 (overflow)
No
http2MaxRequests (1024) 초과? ──→ 503 (overflow)
No
요청 처리 시작
maxRequestsPerConnection (10) 도달? ──→ 연결 종료 후 새 연결

리트라이 구현

VirtualService retries에서 Envoy 변환

spec:
  http:
    - route:
        - destination:
            host: reviews
      retries:
        attempts: 3
        perTryTimeout: 2s
        retryOn: gateway-error,connect-failure,retriable-4xx

Envoy 리트라이 정책:

{
  "retry_policy": {
    "retry_on": "gateway-error,connect-failure,retriable-4xx",
    "num_retries": 3,
    "per_try_timeout": "2s",
    "retry_host_predicate": [
      {
        "name": "envoy.retry_host_predicates.previous_hosts"
      }
    ]
  }
}

리트라이 동작 상세

원본 요청 → Endpoint A (실패, 503)
리트라이 1Endpoint B (실패, 503)  ← previous_hosts로 A 회피
리트라이 2Endpoint C (실패, 503)A, B 회피
리트라이 3Endpoint A (성공, 200)  ← 후보 소진 시 재사용
최종 응답: 200

핵심 포인트:

  • previous_hosts: 이전에 실패한 호스트를 회피하여 다른 엔드포인트에 리트라이
  • perTryTimeout: 각 시도별 타임아웃 (전체 타임아웃과 별개)
  • 리트라이 버짓: Envoy 내부적으로 동시 리트라이 수를 제한

retryOn 조건 상세

조건Envoy 동작
5xx업스트림이 5xx 응답 반환 시
gateway-error502, 503, 504 응답 시
connect-failureTCP 연결 실패 시
retriable-4xx409 (Conflict) 등 특정 4xx
refused-stream업스트림이 REFUSED_STREAM 에러 코드로 스트림 리셋 시
reset응답 없이 연결이 리셋된 경우

폴트 인젝션 구현

지연 주입 (Delay Injection)

spec:
  http:
    - fault:
        delay:
          percentage:
            value: 10
          fixedDelay: 5s
      route:
        - destination:
            host: reviews

Envoy 구현: envoy.filters.http.fault 필터가 HTTP 필터 체인에 삽입됩니다.

요청 도착
Fault Filter: 난수 생성 (0-100)
    ├── 난수 <= 105초 지연 후 다음 필터로 전달
    └── 난수 > 10 → 즉시 다음 필터로 전달
Router Filter → 업스트림으로 전달

중단 주입 (Abort Injection)

spec:
  http:
    - fault:
        abort:
          percentage:
            value: 20
          httpStatus: 503
      route:
        - destination:
            host: reviews

Envoy 구현:

요청 도착
Fault Filter: 난수 생성 (0-100)
    ├── 난수 <= 20 → 즉시 503 응답 반환 (업스트림에 요청 안 함)
    └── 난수 > 20 → 다음 필터로 전달

복합 폴트 인젝션

지연과 중단을 동시에 적용할 수 있습니다:

spec:
  http:
    - fault:
        delay:
          percentage:
            value: 50
          fixedDelay: 3s
        abort:
          percentage:
            value: 10
          httpStatus: 500

평가 순서: 지연이 먼저 적용되고, 그 다음 중단이 평가됩니다.

트래픽 미러링 구현

VirtualService mirror에서 Envoy 변환

spec:
  http:
    - route:
        - destination:
            host: reviews
            subset: v1
      mirror:
        host: reviews
        subset: v2
      mirrorPercentage:
        value: 100

Envoy 구현:

요청 도착
    ├── 원본 요청 → reviews v1 (응답을 클라이언트에 반환)
    └── 미러 요청 → reviews v2 (응답 무시, fire-and-forget)
         └── Host 헤더에 "-shadow" 접미사 추가
             : reviews → reviews-shadow

핵심 특징:

  • 미러 요청의 응답은 완전히 무시됨
  • 미러 요청 실패가 원본 요청에 영향 없음
  • Host 헤더에 "-shadow"가 추가되어 미러 트래픽 식별 가능

ServiceEntry: 메시 확장

외부 서비스 등록

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-api
spec:
  hosts:
    - api.external-service.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS

이것이 Envoy에서 생성하는 리소스:

ServiceEntry
    ├── Listener: 0.0.0.0:443 (아웃바운드)에 필터 체인 추가
    ├── Cluster: outbound|443||api.external-service.com
    │   ├── type: STRICT_DNS
    │   └── dns_lookup_family: V4_ONLY
    └── Route: api.external-service.com → 해당 Cluster로 라우팅

Resolution 모드별 동작

ResolutionEnvoy Cluster Type동작
NONEORIGINAL_DST원래 요청 주소로 전달
STATICSTATICendpoints 필드의 IP 직접 사용
DNSSTRICT_DNSDNS로 주기적 해석
DNS_ROUND_ROBINLOGICAL_DNSDNS 결과 중 하나를 라운드 로빈

고급 라우팅 패턴

헤더 기반 카나리 배포

spec:
  http:
    - match:
        - headers:
            x-canary:
              exact: 'true'
      route:
        - destination:
            host: reviews
            subset: v2
    - route:
        - destination:
            host: reviews
            subset: v1

URI 기반 라우팅

spec:
  http:
    - match:
        - uri:
            prefix: '/api/v2'
      route:
        - destination:
            host: api-v2
    - match:
        - uri:
            prefix: '/api'
      route:
        - destination:
            host: api-v1

소스 기반 라우팅

spec:
  http:
    - match:
        - sourceLabels:
            app: frontend
            version: v2
      route:
        - destination:
            host: reviews
            subset: v2
    - route:
        - destination:
            host: reviews
            subset: v1

디버깅 도구

istioctl 명령어 모음

# VirtualService 적용 상태 확인
istioctl analyze -n NAMESPACE

# 특정 서비스의 라우팅 구성 확인
istioctl proxy-config routes PODNAME -o json | python3 -m json.tool

# Envoy의 클러스터 통계 확인
istioctl proxy-config clusters PODNAME --fqdn reviews.default.svc.cluster.local

# 서킷 브레이커 상태 확인 (Envoy 관리 포트)
kubectl exec PODNAME -c istio-proxy -- curl -s localhost:15000/clusters | grep outlier

# 리트라이 통계
kubectl exec PODNAME -c istio-proxy -- curl -s localhost:15000/stats | grep retry

일반적인 문제 패턴

  1. 503 UC (Upstream Connection): 업스트림 연결 실패 - connectionPool 설정 확인
  2. 503 UO (Upstream Overflow): 서킷 브레이커 트리거 - maxConnections/maxPendingRequests 확인
  3. 503 NR (No Route): 라우트 매칭 실패 - VirtualService host/match 확인
  4. 503 UH (Upstream Unhealthy): 모든 엔드포인트 ejected - outlierDetection 설정 확인

마무리

Istio의 트래픽 관리 엔진은 결국 Envoy의 강력한 프록시 기능 위에 구축됩니다. Istio CRD는 사용자 친화적인 추상화이고, 실제 동작은 Envoy의 리스너, 라우트, 클러스터, 엔드포인트 구성에 의해 결정됩니다.

트래픽 관리 문제를 디버깅할 때는 항상 최종적으로 Envoy에 전달된 구성을 확인하는 것이 핵심입니다. istioctl proxy-config 명령어를 숙지하면 대부분의 트래픽 문제를 빠르게 진단할 수 있습니다.

다음 글에서는 Istio 보안 모델의 내부 구현을 살펴보겠습니다.