Skip to content

필사 모드: Istio 트래픽 관리 실전 — VirtualService부터 카나리 자동화까지

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며

Istio를 도입하는 이유를 한 가지만 꼽으라면 대부분의 팀이 트래픽 관리를 듭니다. 코드 배포와 트래픽 전환을 분리하는 것 — 즉 새 버전을 클러스터에 올려두고도 트래픽은 1%만 흘려보는 것 — 은 배포 사고를 구조적으로 줄이는 가장 효과적인 방법입니다. 그런데 실제 현장에서는 VirtualService와 DestinationRule의 역할 구분부터 헷갈리고, 재시도 설정 하나 잘못해서 장애를 증폭시키고, 503 에러의 원인을 며칠씩 추적하는 일이 반복됩니다.

이 글은 Istio 트래픽 관리를 "동작하는 YAML" 중심으로 정리한 실전 가이드입니다. API 간 관계도에서 시작해 라우팅 매칭 규칙, 카나리 자동화(Flagger, Argo Rollouts), 미러링, 장애 주입, 복원력 패턴의 설정값 산정 논리까지 다루고, 마지막에 재시도 폭풍·타임아웃 중첩·503 디버깅이라는 3대 함정을 짚습니다. 예제는 사이드카 모드 기준이며, Ambient 모드에서는 동일한 API가 waypoint 프록시에서 집행된다는 점만 기억하시면 됩니다.

트래픽 API 관계도 — 누가 무엇을 결정하는가

Istio 트래픽 관리의 4대 리소스는 Gateway, VirtualService, DestinationRule, ServiceEntry입니다. 각자의 책임을 먼저 그림으로 보겠습니다.

(메시 외부에서 들어오는 트래픽)

|

v

+-----------------------------+

| Gateway |

| "어떤 포트/호스트/TLS로 |

| 트래픽을 받을 것인가" |

+-----------------------------+

| (호스트 매칭으로 연결)

v

+---------------------------------------------------------------+

| VirtualService |

| "받은 요청을 어디로, 어떻게 보낼 것인가" |

| - 매칭: 경로/헤더/메서드/쿼리 |

| - 행동: 가중치 분배, 리다이렉트, 재작성, 재시도, 타임아웃, |

| 장애 주입, 미러링 |

+---------------------------------------------------------------+

| |

| (호스트의 subset 지정) | (외부 호스트로 라우팅)

v v

+---------------------------+ +---------------------------+

| DestinationRule | | ServiceEntry |

| "목적지에 도착한 후의 정책" | | "메시 외부 서비스를 메시의 |

| - subset 정의 (버전 라벨) | | 서비스 레지스트리에 등록" |

| - 로드밸런싱 알고리즘 | | - 외부 API, 레거시 DB 등 |

| - 커넥션 풀, 아웃라이어 감지 | +---------------------------+

| - TLS 설정 (업스트림) |

+---------------------------+

혼동을 줄이는 한 줄 요약은 이렇습니다. VirtualService는 라우팅(어디로 보낼까), DestinationRule은 목적지 정책(도착한 트래픽을 어떻게 다룰까)입니다. 카나리를 만들 때 "v1 90%, v2 10%"라는 분배는 VirtualService에 쓰지만, v1과 v2라는 subset 자체는 DestinationRule이 정의합니다. 둘 중 하나만 만들면 동작하지 않는 이유가 여기에 있습니다.

라우팅 규칙 상세 — 매칭과 분배

기본: subset 정의와 가중치 분배

apiVersion: networking.istio.io/v1

kind: DestinationRule

metadata:

name: reviews

namespace: bookinfo

spec:

host: reviews.bookinfo.svc.cluster.local

subsets:

- name: v1

labels:

version: v1 # 파드 라벨과 매칭

- name: v2

labels:

version: v2

apiVersion: networking.istio.io/v1

kind: VirtualService

metadata:

name: reviews

namespace: bookinfo

spec:

hosts:

- reviews.bookinfo.svc.cluster.local

http:

- route:

- destination:

host: reviews.bookinfo.svc.cluster.local

subset: v1

weight: 90

- destination:

host: reviews.bookinfo.svc.cluster.local

subset: v2

weight: 10

헤더·경로·메서드 매칭

매칭 규칙은 위에서 아래로 평가되며, 첫 번째로 매칭된 규칙이 적용됩니다. 따라서 구체적인 규칙을 위에, 포괄 규칙(catch-all)을 맨 아래에 두는 것이 철칙입니다.

apiVersion: networking.istio.io/v1

kind: VirtualService

metadata:

name: reviews-routing

namespace: bookinfo

spec:

hosts:

- reviews.bookinfo.svc.cluster.local

http:

규칙 1: 내부 QA 헤더가 있으면 항상 v2로 (가장 구체적 — 맨 위)

- match:

- headers:

x-qa-user:

exact: "true"

route:

- destination:

host: reviews.bookinfo.svc.cluster.local

subset: v2

규칙 2: 특정 경로 + GET만 v2 후보군으로

- match:

- uri:

prefix: /api/v2/

method:

exact: GET

route:

- destination:

host: reviews.bookinfo.svc.cluster.local

subset: v2

규칙 3: catch-all — 반드시 마지막에

- route:

- destination:

host: reviews.bookinfo.svc.cluster.local

subset: v1

매칭 연산자는 exact(완전 일치), prefix(전방 일치), regex(정규식)를 지원합니다. regex는 강력하지만 Envoy의 RE2 문법을 따르고 평가 비용이 있으므로, prefix로 충분한 곳에 regex를 남용하지 않는 것이 좋습니다.

같은 match 블록 안은 AND, 블록 사이는 OR

흔히 틀리는 부분이라 명시적으로 짚습니다. 하나의 match 항목 안에 uri와 headers를 같이 쓰면 둘 다 만족해야(AND) 하고, match 배열에 항목을 여러 개 두면 하나만 만족하면(OR) 됩니다.

AND: 경로가 /admin이고 동시에 헤더가 있어야 매칭

- match:

- uri:

prefix: /admin

headers:

x-role:

exact: admin

OR: 경로가 /admin이거나, 헤더가 있으면 매칭

- match:

- uri:

prefix: /admin

- headers:

x-role:

exact: admin

카나리 배포 실전 — 가중치 단계와 메트릭 게이트

수동 카나리의 한계

가중치를 10 → 30 → 50 → 100으로 사람이 직접 올리는 수동 카나리는 시작점으로는 좋지만 두 가지 약점이 있습니다. 첫째, 단계마다 메트릭을 사람이 보고 판단해야 하므로 야간 배포가 어렵습니다. 둘째, 나쁜 지표를 보고도 롤백을 망설이는 인간적 편향이 끼어듭니다. 그래서 메트릭 게이트(오류율·지연이 기준을 넘으면 자동 롤백)를 갖춘 자동화 도구를 결합하는 것이 정석입니다.

Flagger로 카나리 자동화

Flagger는 Canary라는 CRD 하나로 VirtualService와 DestinationRule을 자동 생성·조정합니다.

apiVersion: flagger.app/v1beta1

kind: Canary

metadata:

name: payments-api

namespace: payments

spec:

targetRef:

apiVersion: apps/v1

kind: Deployment

name: payments-api

service:

port: 8080

gateways:

- istio-system/public-gateway

hosts:

- payments.example.com

analysis:

interval: 1m # 1분마다 메트릭 평가

threshold: 5 # 5회 연속 실패 시 롤백

maxWeight: 50 # 카나리 최대 가중치

stepWeight: 10 # 단계당 10%씩 증가

metrics:

- name: request-success-rate

thresholdRange:

min: 99 # 성공률 99% 미만이면 실패 카운트

interval: 1m

- name: request-duration

thresholdRange:

max: 500 # p99 500ms 초과면 실패 카운트

interval: 1m

webhooks:

- name: load-test # 트래픽이 적은 시간대 대비 합성 부하

url: http://flagger-loadtester.test/

metadata:

cmd: "hey -z 1m -q 10 -c 2 http://payments-api-canary.payments:8080/healthz"

Flagger가 진행하는 단계는 다음과 같습니다.

1. 새 이미지 감지 → payments-api-canary Deployment 생성

2. 가중치 10% → 1분간 성공률/지연 평가 → 통과 시 20% → ... → 50%

3. 모든 단계 통과 → primary를 새 버전으로 교체, 카나리 가중치 0%

4. 어느 단계든 threshold(5회) 연속 실패 → 가중치 0%로 즉시 롤백

→ Deployment는 그대로 두므로 원인 분석 가능

Argo Rollouts를 쓰는 경우

Argo CD 기반 GitOps 조직이라면 Argo Rollouts가 자연스럽습니다. Rollouts는 Deployment를 Rollout CRD로 대체하고, trafficRouting에 Istio를 지정하면 VirtualService 가중치를 직접 조작합니다.

apiVersion: argoproj.io/v1alpha1

kind: Rollout

metadata:

name: payments-api

namespace: payments

spec:

replicas: 5

strategy:

canary:

trafficRouting:

istio:

virtualService:

name: payments-api # 기존 VirtualService를 Rollouts가 조작

routes:

- primary

steps:

- setWeight: 10

- pause: { duration: 5m } # 5분 관찰

- analysis: # AnalysisTemplate로 메트릭 게이트

templates:

- templateName: success-rate

- setWeight: 30

- pause: { duration: 5m }

- setWeight: 60

- pause: { duration: 5m }

selector:

matchLabels:

app: payments-api

template:

metadata:

labels:

app: payments-api

spec:

containers:

- name: app

image: registry.example.com/payments-api:v2

Flagger는 "기존 Deployment를 그대로 두고 옆에서 조율"하는 비침투형, Argo Rollouts는 "Deployment를 Rollout으로 교체"하는 침투형입니다. 이미 GitOps 파이프라인이 Argo 생태계라면 Rollouts, 기존 매니페스트를 건드리기 어렵다면 Flagger가 무난한 선택입니다.

트래픽 미러링 — 운영 트래픽으로 섀도 테스트

미러링(섀도잉)은 운영 트래픽의 사본을 새 버전으로 흘려보내되, 응답은 버리는 기법입니다. 사용자 영향 없이 운영 트래픽 패턴으로 새 버전을 검증할 수 있습니다.

apiVersion: networking.istio.io/v1

kind: VirtualService

metadata:

name: orders

namespace: commerce

spec:

hosts:

- orders.commerce.svc.cluster.local

http:

- route:

- destination:

host: orders.commerce.svc.cluster.local

subset: v1

weight: 100 # 실제 응답은 전부 v1이 처리

mirror:

host: orders.commerce.svc.cluster.local

subset: v2 # 사본은 v2로

mirrorPercentage:

value: 20.0 # 트래픽의 20%만 미러링

주의할 점이 두 가지 있습니다. 첫째, 미러링된 요청도 부수 효과(DB 쓰기, 외부 API 호출)를 일으킵니다. 쓰기 경로를 미러링하려면 v2가 섀도 모드(쓰기를 무시하거나 별도 스토리지에 기록)로 동작하도록 애플리케이션 차원의 준비가 필요합니다. 둘째, 미러 요청의 Host 헤더에는 -shadow 접미사가 붙으므로, 로그에서 섀도 트래픽을 구분할 수 있고 또 구분해야 합니다.

장애 주입 — 메시 차원의 카오스 테스트

복원력 설정(재시도, 타임아웃, 서킷브레이커)은 장애가 나야 검증됩니다. fault injection은 코드 수정 없이 메시 차원에서 지연과 오류를 주입합니다.

apiVersion: networking.istio.io/v1

kind: VirtualService

metadata:

name: ratings-fault

namespace: bookinfo

spec:

hosts:

- ratings.bookinfo.svc.cluster.local

http:

- match:

- headers:

x-chaos-test: # 테스트 트래픽에만 주입 (전체 주입 금지)

exact: "true"

fault:

delay:

percentage:

value: 50.0 # 매칭 트래픽의 50%에

fixedDelay: 3s # 3초 지연 주입

abort:

percentage:

value: 10.0 # 10%에는 HTTP 503 반환

httpStatus: 503

route:

- destination:

host: ratings.bookinfo.svc.cluster.local

subset: v1

- route: # 일반 트래픽은 그대로

- destination:

host: ratings.bookinfo.svc.cluster.local

subset: v1

실전 팁: 운영 클러스터에서 전체 트래픽에 fault를 거는 것은 카오스 테스트가 아니라 장애입니다. 반드시 헤더 매칭으로 합성 트래픽에만 주입하고, 주입 상태를 대시보드에 표시해 "지금 fault가 걸려 있다"는 사실을 팀이 알게 하시기 바랍니다. 위 예시처럼 "ratings가 3초 느려지면 상위 서비스 reviews의 타임아웃·폴백이 의도대로 동작하는가"를 검증하는 것이 표준 시나리오입니다.

복원력 패턴 — 설정값을 어떻게 정할 것인가

타임아웃과 재시도

apiVersion: networking.istio.io/v1

kind: VirtualService

metadata:

name: inventory

namespace: commerce

spec:

hosts:

- inventory.commerce.svc.cluster.local

http:

- route:

- destination:

host: inventory.commerce.svc.cluster.local

timeout: 3s # 전체 요청 데드라인 (재시도 포함)

retries:

attempts: 2 # 최대 2회 재시도

perTryTimeout: 1s # 시도 1회당 1초

retryOn: 5xx,reset,connect-failure,retriable-4xx

설정값 산정의 논리는 다음과 같습니다.

1. perTryTimeout: 대상 서비스의 정상 p99 지연 x 1.5~2배

(예: p99가 400ms라면 perTryTimeout 1s)

2. attempts: 멱등 요청(GET)은 2, 비멱등(POST)은 0~1

비멱등 요청 재시도는 중복 처리 위험 — 멱등성 키가 없으면 금지

3. timeout(전체): perTryTimeout x (attempts + 1) 이상이어야

재시도가 실제로 의미를 가짐. 1s x 3 = 3s

4. retryOn에서 reset/connect-failure는 안전 (요청이 도달 전 실패),

5xx는 비멱등 요청에서는 신중하게

서킷브레이커 — outlierDetection과 connectionPool

apiVersion: networking.istio.io/v1

kind: DestinationRule

metadata:

name: inventory-cb

namespace: commerce

spec:

host: inventory.commerce.svc.cluster.local

trafficPolicy:

connectionPool:

tcp:

maxConnections: 100 # 인스턴스당이 아니라 엔드포인트 풀 기준

http:

http1MaxPendingRequests: 50 # 대기열 한도 — 초과분은 즉시 503

http2MaxRequests: 200 # 동시 요청 한도

maxRequestsPerConnection: 0 # 0 = 무제한 (keep-alive 유지)

outlierDetection:

consecutive5xxErrors: 5 # 연속 5xx 5회면

interval: 10s # 10초 주기로 평가

baseEjectionTime: 30s # 30초간 풀에서 퇴출 (반복 시 배수 증가)

maxEjectionPercent: 50 # 동시 퇴출은 전체의 50%까지

minHealthPercent: 30 # 건강한 인스턴스 30% 미만이면 퇴출 중단

산정 가이드입니다.

- maxConnections / http2MaxRequests:

평시 피크 동시 요청 수 x 1.5~2배. 너무 작으면 평시에도 503,

너무 크면 서킷브레이커가 사실상 꺼진 것과 같음.

Grafana에서 istio_requests_total 기반 동시성 추정 후 설정.

- http1MaxPendingRequests:

"대기시킬 바에 빨리 실패시키고 싶은 한도". 지연에 민감한 서비스는

작게(10~50), 배치성 트래픽은 크게.

- consecutive5xxErrors + baseEjectionTime:

배포 직후 워밍업 중 일시 오류로 과잉 퇴출되지 않도록

consecutive5xxErrors를 너무 작게(1~2) 잡지 말 것.

- maxEjectionPercent:

인스턴스가 3개뿐인 서비스에서 100%로 두면 전체 퇴출로

오히려 전면 장애. 50% 이하 + minHealthPercent로 안전판.

로드밸런싱과 locality

DestinationRule의 loadBalancer로 알고리즘을 선택합니다.

| 알고리즘 | 동작 | 적합한 경우 |

| --- | --- | --- |

| LEAST_REQUEST (기본) | 활성 요청이 적은 엔드포인트 선호 | 대부분의 경우 — 기본값 유지 권장 |

| ROUND_ROBIN | 순차 분배 | 요청 비용이 균일할 때 |

| RANDOM | 무작위 | 헬스체크 없는 단순 분배 |

| consistentHash | 해시 키 기반 고정 | 세션 어피니티, 로컬 캐시 적중률 |

apiVersion: networking.istio.io/v1

kind: DestinationRule

metadata:

name: search-locality

namespace: commerce

spec:

host: search.commerce.svc.cluster.local

trafficPolicy:

loadBalancer:

simple: LEAST_REQUEST

localityLbSetting:

enabled: true

failoverPriority: # 같은 zone → 같은 region → 그 외 순서로 선호

- "topology.kubernetes.io/zone"

- "topology.kubernetes.io/region"

outlierDetection: # locality 페일오버는 outlierDetection이 필수

consecutive5xxErrors: 5

interval: 10s

baseEjectionTime: 30s

locality 로드밸런싱에서 가장 많이 빠지는 함정: outlierDetection 없이 localityLbSetting만 켜면 페일오버가 동작하지 않습니다. Envoy는 "이 zone의 엔드포인트가 불건강하다"는 판단 근거가 있어야 다음 우선순위로 넘어가는데, 그 판단이 바로 outlierDetection이기 때문입니다.

ServiceEntry — 외부 서비스를 메시의 통제 아래로

기본 설정에서 메시 밖으로 나가는 트래픽은 통제 없이 통과합니다(ALLOW_ANY). 외부 의존성을 메시의 가시성·정책 아래 두려면 ServiceEntry로 등록하고, 필요시 egress 모드를 REGISTRY_ONLY로 잠급니다.

apiVersion: networking.istio.io/v1

kind: ServiceEntry

metadata:

name: external-payment-gateway

namespace: payments

spec:

hosts:

- api.pgprovider.example

location: MESH_EXTERNAL

ports:

- number: 443

name: https

protocol: TLS

resolution: DNS

등록된 외부 호스트에도 타임아웃/재시도 적용 가능

apiVersion: networking.istio.io/v1

kind: VirtualService

metadata:

name: external-payment-gateway

namespace: payments

spec:

hosts:

- api.pgprovider.example

tls:

- match:

- sniHosts:

- api.pgprovider.example

route:

- destination:

host: api.pgprovider.example

메시 전역에서 미등록 외부 트래픽 차단 (IstioOperator 또는 MeshConfig)

apiVersion: install.istio.io/v1alpha1

kind: IstioOperator

spec:

meshConfig:

outboundTrafficPolicy:

mode: REGISTRY_ONLY # ServiceEntry에 없는 외부 호스트는 차단

보안 요건이 높은 환경(금융권 망분리 보완 등)에서는 여기에 egress gateway를 더해 "외부로 나가는 모든 트래픽이 지정된 출구 노드를 통과"하도록 강제할 수 있습니다. egress gateway는 외부 호출의 단일 감사 지점이 되어, 방화벽 정책을 워크로드 IP가 아니라 게이트웨이 IP에 고정할 수 있게 해줍니다.

Gateway API와의 관계 — 마이그레이션 관점

Kubernetes Gateway API는 Ingress의 후속 표준이며, Istio는 이 표준의 대표 구현체 중 하나입니다. 2026년 현재 방향성은 명확합니다. 신규 구축에서 인그레스(외부 진입) 계층은 Gateway API(Gateway + HTTPRoute)로 작성하는 것이 권장이고, Istio 고유의 Gateway/VirtualService는 계속 지원되지만 신규 표준 기능은 Gateway API 쪽에 먼저 실립니다. Ambient의 waypoint가 Gateway API 리소스로 선언된다는 점도 이 방향을 보여줍니다.

Istio Gateway/VirtualService 조합의 Gateway API 등가물

apiVersion: gateway.networking.k8s.io/v1

kind: Gateway

metadata:

name: public-gateway

namespace: istio-ingress

spec:

gatewayClassName: istio

listeners:

- name: https

port: 443

protocol: HTTPS

hostname: shop.example.com

tls:

mode: Terminate

certificateRefs:

- name: shop-example-com-cert

apiVersion: gateway.networking.k8s.io/v1

kind: HTTPRoute

metadata:

name: shop-route

namespace: commerce

spec:

parentRefs:

- name: public-gateway

namespace: istio-ingress

hostnames:

- shop.example.com

rules:

- matches:

- path:

type: PathPrefix

value: /api/

backendRefs:

- name: shop-api

port: 8080

weight: 90

- name: shop-api-canary

port: 8080

weight: 10

마이그레이션 우선순위는 인그레스 계층부터입니다. 메시 내부(east-west) 라우팅은 VirtualService가 여전히 표현력에서 앞서는 부분(미러링 등 일부 기능)이 있으므로, 내부 라우팅까지 한 번에 바꾸려 하지 말고 Gateway API의 기능 성숙도를 보며 단계적으로 옮기는 것이 안전합니다.

흔한 함정과 안티패턴

함정 1: 재시도 폭풍 (retry storm)

호출 체인 A → B → C에서 각 단계가 재시도 2회를 가지면, C의 장애 1건이 최악의 경우 A 기준 9배의 트래픽으로 증폭됩니다.

재시도 증폭 산수:

A가 B를 호출: 1 + 2회 재시도 = 최대 3회

B가 C를 호출: 각각 1 + 2회 = 최대 3회

→ C가 받는 요청: 3 x 3 = 최대 9회 (원래는 1회)

체인이 4단이면 27배. C가 과부하로 죽어가는 중이라면

재시도가 관에 못을 박는 격.

원칙:

- 재시도는 체인의 한 곳(가급적 가장 바깥 계층)에만

- 내부 깊은 계층은 attempts: 0 또는 1

- outlierDetection과 함께 써서 죽어가는 인스턴스로의 재시도를 차단

함정 2: 타임아웃 중첩

상위 타임아웃이 하위보다 짧으면, 하위가 성실하게 처리 중이어도 상위가 먼저 끊고 재시도합니다. 결과적으로 하위 서비스는 "절대 완료될 수 없는 작업"을 반복 수행합니다.

잘못된 구성:

A --(timeout 1s)--> B --(timeout 3s)--> C

B가 C 응답을 2.5초에 받아도 A는 이미 1초에 끊고 재시도함

원칙: 바깥 타임아웃 >= 안쪽 타임아웃 합계 + 여유

A --(timeout 5s)--> B --(timeout 3s)--> C (각 재시도 포함 계산)

데드라인은 바깥에서 안쪽으로 갈수록 짧아져야 한다

함정 3: 503 디버깅 플로우

Istio의 503은 원인이 다양해서 체계 없이 찾으면 시간을 잃습니다. 응답 플래그(response flags) 기준 디버깅 순서를 추천합니다.

503 디버깅 플로우:

1. 액세스 로그에서 RESPONSE_FLAGS 확인

kubectl logs deploy/app -c istio-proxy | grep ' 503 '

2. 플래그별 분기:

UH (no healthy upstream)

→ 엔드포인트 없음. DestinationRule subset 라벨과 파드 라벨

불일치가 단골 원인

→ istioctl proxy-config endpoints deploy/app | grep 대상서비스

UO (upstream overflow)

→ connectionPool 한도 초과. 서킷브레이커가 의도대로 동작 중인지,

한도가 너무 작은지 판단

UF (upstream connection failure)

→ 연결 자체가 실패. mTLS 설정 불일치(한쪽만 STRICT) 또는

포트/프로토콜 미스매치

URX (max retries reached)

→ 재시도 소진. 근본 원인은 다른 플래그와 함께 추적

NR (no route)

→ VirtualService 매칭 누락. catch-all 규칙 존재 여부 확인

3. 설정과 실제 상태 대조:

istioctl analyze -n 네임스페이스

istioctl proxy-config cluster deploy/app --fqdn 대상FQDN -o json

그 밖의 안티패턴

- **VirtualService 한 호스트 분산 정의**: 같은 호스트에 대한 VirtualService를 여러 네임스페이스/파일에 쪼개 정의하면 병합 순서가 보장되지 않습니다. 호스트당 하나의 VirtualService로 관리하시기 바랍니다.

- **subset만 만들고 라벨 누락**: DestinationRule subset의 라벨과 실제 파드 라벨이 다르면 UH 503이 납니다. 배포 파이프라인에서 라벨 일치를 검증하세요.

- **catch-all 없는 매칭 규칙**: 모든 match가 실패하면 NR이 됩니다. 마지막 규칙은 항상 무조건 라우트로.

운영 검증 — istioctl proxy-config 읽는 법

Envoy에 실제로 내려간 설정을 확인하는 것이 모든 디버깅의 출발점입니다.

1) 리스너: 이 프록시가 어떤 포트를 듣고 있는가

istioctl proxy-config listeners deploy/app -n commerce

2) 라우트: VirtualService가 어떤 라우팅 테이블로 번역되었는가

istioctl proxy-config routes deploy/app -n commerce --name 8080 -o json

3) 클러스터: DestinationRule(서킷브레이커, TLS)이 반영되었는가

istioctl proxy-config cluster deploy/app -n commerce \

--fqdn inventory.commerce.svc.cluster.local -o json

4) 엔드포인트: subset에 실제 파드 IP가 잡혀 있는가

istioctl proxy-config endpoints deploy/app -n commerce \

| grep inventory

5) 전체 동기화 상태: istiod와 프록시 간 설정 전파 확인

istioctl proxy-status

읽는 순서는 트래픽 흐름과 같습니다. listener(진입) → route(매칭) → cluster(목적지 정책) → endpoint(실제 IP). 어느 단계에서 기대와 어긋나는지 찾으면, 그 단계에 대응하는 리소스(Gateway/VirtualService/DestinationRule/파드 라벨)를 고치면 됩니다. proxy-status에서 STALE이 보이면 istiod가 설정을 밀어내지 못하고 있는 것이므로 istiod 로그부터 확인합니다.

체크리스트

- [ ] 호스트당 VirtualService를 하나로 유지하고 catch-all 라우트를 두었다

- [ ] DestinationRule subset 라벨과 파드 라벨 일치를 파이프라인에서 검증한다

- [ ] 재시도는 호출 체인에서 한 계층에만 두고, 비멱등 요청 재시도를 금지했다

- [ ] 타임아웃이 바깥에서 안쪽으로 갈수록 짧아지도록 체인 전체를 점검했다

- [ ] 서킷브레이커 한도를 실측 동시성 기반으로 산정했다 (기본값 방치 금지)

- [ ] locality 로드밸런싱에 outlierDetection을 함께 설정했다

- [ ] 카나리에 메트릭 게이트(Flagger 또는 Argo Rollouts)를 연결했다

- [ ] 외부 의존성을 ServiceEntry로 등록하고 REGISTRY_ONLY 적용을 검토했다

- [ ] fault injection은 헤더 매칭으로 합성 트래픽에만 적용한다

- [ ] 503 대응 런북에 response flags 분기표를 포함했다

- [ ] 신규 인그레스는 Gateway API로 작성하는 방침을 정했다

마치며

Istio 트래픽 관리의 본질은 "배포와 릴리스의 분리"입니다. 코드를 클러스터에 올리는 일(배포)과 사용자 트래픽을 그 코드로 보내는 일(릴리스)을 독립적으로 제어할 수 있을 때, 비로소 금요일 오후에도 두렵지 않은 배포가 가능해집니다. VirtualService와 DestinationRule의 역할 구분, 재시도·타임아웃의 체인 전체 관점 설계, 그리고 메트릭 게이트가 있는 카나리 자동화 — 이 세 가지가 기둥입니다.

설정값은 복사해서 끝나는 것이 아니라 실측 지표로 산정하고 fault injection으로 검증해야 한다는 점, 그리고 모든 디버깅은 istioctl proxy-config로 "Envoy가 실제로 받은 설정"을 보는 데서 시작한다는 점을 기억하시면, Istio 트래픽 관리는 복잡한 마법이 아니라 예측 가능한 도구가 됩니다.

참고 자료

- [Istio 트래픽 관리 개념 문서](https://istio.io/latest/docs/concepts/traffic-management/)

- [Istio VirtualService 레퍼런스](https://istio.io/latest/docs/reference/config/networking/virtual-service/)

- [Istio DestinationRule 레퍼런스](https://istio.io/latest/docs/reference/config/networking/destination-rule/)

- [Istio ServiceEntry 레퍼런스](https://istio.io/latest/docs/reference/config/networking/service-entry/)

- [Istio 트래픽 미러링 태스크](https://istio.io/latest/docs/tasks/traffic-management/mirroring/)

- [Istio Fault Injection 태스크](https://istio.io/latest/docs/tasks/traffic-management/fault-injection/)

- [Envoy Response Flags 문서](https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage)

- [Flagger 공식 문서](https://docs.flagger.app/)

- [Argo Rollouts 공식 문서](https://argo-rollouts.readthedocs.io/)

- [Kubernetes Gateway API 명세](https://gateway-api.sigs.k8s.io/)

현재 단락 (1/484)

Istio를 도입하는 이유를 한 가지만 꼽으라면 대부분의 팀이 트래픽 관리를 듭니다. 코드 배포와 트래픽 전환을 분리하는 것 — 즉 새 버전을 클러스터에 올려두고도 트래픽은 1%만...

작성 글자: 0원문 글자: 14,403작성 단락: 0/484