LabHub

Istio 서비스 메시 · 트래픽 관리 · 이론

VirtualService 와 DestinationRule — 역할이 갈리는 이유

LabHub 에서 이어서 보기

한 줄 요약

VirtualService 는 어느 요청을 어디로 보낼지를, DestinationRule 은 거기에 도착한 뒤 어떻게 다룰지를 정한다.

왜 이게 필요했나

쿠버네티스 Service 는 요청을 파드 집합에 던져 주는 것까지만 한다. 그 이상은 없다. "이 헤더가 붙은 요청만 새 버전으로", "이 서비스는 3초 안에 응답이 없으면 포기", "연속으로 5xx 를 내는 인스턴스는 30초간 빼기" 같은 요구는 전부 애플리케이션이나 앞단 게이트웨이가 떠안아야 했다.

Istio 는 이 요구를 두 리소스로 쪼갠다. 쪼갠 이유가 중요하다. 라우팅 규칙은 자주 바뀌고 부서마다 다르지만, 대상의 성질(어떤 라벨이 어떤 버전인지, 연결 풀을 얼마로 둘지)은 상대적으로 안정적이다. 그래서 배포 파이프라인이 매 배포마다 건드리는 것은 VirtualService 하나면 되고, DestinationRule 은 서비스 소유자가 한 번 정해 두면 오래 간다.

어떻게 동작하나

두 리소스가 Envoy 의 어디로 번역되는지 보면 역할 분리가 선명해진다.

| Istio 리소스 | Envoy 대응 | 결정하는 것 |
| --- | --- | --- |
| VirtualService | RouteConfiguration | 매칭 조건, 가중치, 재작성, 타임아웃, 재시도, 결함 주입, 미러링 |
| DestinationRule | Cluster | subset(라벨로 나눈 대상), 로드밸런싱, 연결 풀, 이상치 감지, 클라이언트 TLS |

Envoy 클러스터 이름은 방향|포트|서브셋|FQDN 형식이라 outbound|9080|v2|reviews.mesh-lab.svc.cluster.local 처럼 생겼다. subset 이름이 그 자리에 그대로 박힌다는 점만 기억해도 로그를 읽기가 훨씬 쉬워진다.

매칭에는 순서가 있다. spec.http 는 배열이고, Envoy 는 위에서부터 내려오며 처음 걸리는 것을 쓴다. 그래서 조건 없는 catch-all 라우트를 맨 위에 두면 아래 규칙은 영원히 실행되지 않는다. 실무 규칙은 하나다 — 조건이 좁은 규칙일수록 위로, 조건 없는 기본 경로는 반드시 맨 아래로. 그리고 기본 경로가 아예 없으면 매칭에 실패한 요청은 NR(no route) 플래그와 함께 503 이 된다.

subset 은 이름이 아니라 라벨로 파드를 고른다. DestinationRule 에 name: v2, labels: {version: v2} 라고 적으면, 그 subset 은 version=v2 라벨이 붙은 파드만 가리킨다. 이름과 라벨을 헷갈려 라벨을 빠뜨리거나 오타를 내면 그 subset 에는 엔드포인트가 없고, 트래픽은 UH(no healthy upstream) 503 이 된다. 메시에서 가장 흔한 503 의 원인이 바로 이 라벨 불일치다.

프로토콜 판별도 놓치기 쉽다. 메시는 Service 의 포트 이름으로 L7 처리 여부를 정한다. 포트 이름이 http, http-api, grpc, tcp 처럼 시작해야 HTTP 라우팅이 걸린다. 이름이 없거나 엉뚱하면 TCP 로 취급되어 헤더 매칭이 통째로 무시된다.

복원력 설정은 숫자 근거를 갖고 정해야 한다.

현장에서 만나는 모습

첫째, 재시도 폭풍. A → B → C 체인에서 각 단계가 재시도 2회를 갖고 있으면 C 가 받는 요청은 최악의 경우 9배가 된다. 4단이면 27배다. 죽어가는 서비스에 관을 박는 격이라, 재시도는 체인의 한 곳(가급적 바깥)에만 두고 안쪽은 0~1로 둔다.

둘째, 타임아웃 중첩. 바깥 타임아웃이 안쪽보다 짧으면 안쪽이 성실히 일하는 중에 바깥이 끊고 재시도한다. 안쪽 서비스는 "절대 완료될 수 없는 작업"을 반복한다. 데드라인은 바깥에서 안쪽으로 갈수록 짧아져야 한다.

셋째, 503 은 플래그부터 본다. UH 는 엔드포인트 없음(subset 라벨 확인), UO 는 연결 풀 초과(서킷 브레이커 동작 중), UF 는 연결 실패(한쪽만 STRICT 인 mTLS 불일치가 단골), NR 은 라우트 없음(catch-all 확인), URX 는 재시도 소진. 이 다섯 개만 알아도 디버깅 시간이 절반으로 준다.

다음 실습에서 할 것

픽스처의 reviews v1/v2 워크로드를 올리고 subset 을 정의한 뒤, 기본 경로는 v1 로 두고 특정 헤더가 붙은 요청만 v2 로 보내는 VirtualService 를 만든다. ratings 에는 경로 접두어 매칭과 재작성을 걸고 타임아웃·재시도를 붙인 다음, 이상치 감지와 결함 주입까지 얹는다. 마지막에는 istioctl analyze 로 설정을 검증하고 라우트 순서를 JSON 으로 정리한다. 트래픽이 실제로 흐르지는 않지만, 라우트 배열의 순서와 subset 의 라벨 일치는 정적 분석만으로도 정확히 판정된다.