- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며
- VirtualService에서 Envoy RouteConfiguration으로
- DestinationRule에서 Envoy Cluster Configuration으로
- 로드밸런싱 알고리즘
- 서킷 브레이커 구현
- 리트라이 구현
- 폴트 인젝션 구현
- 트래픽 미러링 구현
- ServiceEntry: 메시 확장
- 고급 라우팅 패턴
- 디버깅 도구
- 마무리
들어가며
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에서도 동일한 순서를 유지합니다:
- 가장 구체적인 매치가 먼저 (exact > prefix > regex)
- 여러 match 블록이 있으면 첫 번째 매칭이 적용
- 매치 조건이 없는 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 (기본값)
요청 1 → Endpoint A
요청 2 → Endpoint B
요청 3 → Endpoint C
요청 4 → Endpoint A (순환)
Envoy 구현: 각 엔드포인트에 순서대로 요청을 분배합니다. 가중치가 있으면 Weighted Round Robin으로 동작합니다.
Least Connections
Endpoint A: 활성 연결 3개
Endpoint 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]
(재시도)
상세 동작:
- Closed: 정상 상태. 모든 요청이 엔드포인트로 전달
- Open (Ejected): 연속 에러 임계값 초과. 해당 엔드포인트가 로드밸런싱 풀에서 제거
- 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)
│
리트라이 1 → Endpoint B (실패, 503) ← previous_hosts로 A 회피
│
리트라이 2 → Endpoint C (실패, 503) ← A, B 회피
│
리트라이 3 → Endpoint A (성공, 200) ← 후보 소진 시 재사용
│
최종 응답: 200
핵심 포인트:
- previous_hosts: 이전에 실패한 호스트를 회피하여 다른 엔드포인트에 리트라이
- perTryTimeout: 각 시도별 타임아웃 (전체 타임아웃과 별개)
- 리트라이 버짓: Envoy 내부적으로 동시 리트라이 수를 제한
retryOn 조건 상세
| 조건 | Envoy 동작 |
|---|---|
| 5xx | 업스트림이 5xx 응답 반환 시 |
| gateway-error | 502, 503, 504 응답 시 |
| connect-failure | TCP 연결 실패 시 |
| retriable-4xx | 409 (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)
│
├── 난수 <= 10 → 5초 지연 후 다음 필터로 전달
│
└── 난수 > 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 모드별 동작
| Resolution | Envoy Cluster Type | 동작 |
|---|---|---|
| NONE | ORIGINAL_DST | 원래 요청 주소로 전달 |
| STATIC | STATIC | endpoints 필드의 IP 직접 사용 |
| DNS | STRICT_DNS | DNS로 주기적 해석 |
| DNS_ROUND_ROBIN | LOGICAL_DNS | DNS 결과 중 하나를 라운드 로빈 |
고급 라우팅 패턴
헤더 기반 카나리 배포
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
일반적인 문제 패턴
- 503 UC (Upstream Connection): 업스트림 연결 실패 - connectionPool 설정 확인
- 503 UO (Upstream Overflow): 서킷 브레이커 트리거 - maxConnections/maxPendingRequests 확인
- 503 NR (No Route): 라우트 매칭 실패 - VirtualService host/match 확인
- 503 UH (Upstream Unhealthy): 모든 엔드포인트 ejected - outlierDetection 설정 확인
마무리
Istio의 트래픽 관리 엔진은 결국 Envoy의 강력한 프록시 기능 위에 구축됩니다. Istio CRD는 사용자 친화적인 추상화이고, 실제 동작은 Envoy의 리스너, 라우트, 클러스터, 엔드포인트 구성에 의해 결정됩니다.
트래픽 관리 문제를 디버깅할 때는 항상 최종적으로 Envoy에 전달된 구성을 확인하는 것이 핵심입니다. istioctl proxy-config 명령어를 숙지하면 대부분의 트래픽 문제를 빠르게 진단할 수 있습니다.
다음 글에서는 Istio 보안 모델의 내부 구현을 살펴보겠습니다.