VS·DR·subset의 역할 분담, spec.hosts의 실제 매칭 대상, 두 리소스를 연결하는 매핑 키, istiod → Envoy xDS 변환 경로, 설정이 꼬였을 때의 계층별 진단 절차를 정리함.
spec.hosts = 요청의 목적지 이름과 매칭 (HTTP는 :authority 헤더, TLS는 SNI, TCP는 Service VIP). DR과는 무관함.route.destination.host ⟷ DR spec.host — FQDN 정규화 후 문자열 비교임.destination.subset ⟷ DR subsets[].name — subset labels가 Pod label과 일치해야 endpoint가 채워짐.NC=cluster 부재(DR/host 문제), UH=endpoint 부재(label/Pod 문제), NR=route 부재(VS/hosts 문제).Istio는 트래픽 제어를 라우팅 결정과 목적지 정책으로 분리함. 변경 주기·소유자가 다른 관심사를 리소스 단위로 나눈 설계임.
| 리소스 | 책임 | 답하는 질문 | Envoy 대응 |
|---|---|---|---|
| VirtualService | L7 라우팅 규칙 (match / rewrite / weight / retry / timeout / fault) | "이 요청을 어디로 보낼까" | Route (RDS) |
| DestinationRule | 목적지 정책 (LB, connection pool, outlier detection, mTLS 모드) + subset 정의 | "그 목적지로 가는 트래픽을 어떻게 다룰까" | Cluster (CDS) |
| subset | 같은 Service 뒤 Pod들을 label로 나눈 논리적 버전 그룹 | "목적지 안에서 어느 그룹인가" | 별도 Cluster + endpoint 필터 (EDS) |
| Service / ServiceEntry | service registry 등록 — VS·DR이 참조 가능한 이름의 원천 | "mesh가 아는 이름인가" | Cluster·Endpoint의 기반 |
| Gateway | edge에서 수신할 port·protocol·host 선언 | "외부 트래픽을 어디서 받을까" | Listener (LDS) |
subset은 라우팅 대상 그룹의 "정의"만 담당하고, 그 그룹을 "선택"하는 것은 VS의 몫임. 정의(DR)와 선택(VS)이 분리되어 있어 이름 참조로만 연결됨 — 이 참조가 끊기는 것이 대부분의 장애 원인임.
spec.hosts는 무엇과 매칭되는가spec.hosts는 "요청이 향하는 목적지 이름"과 매칭되어 이 VS의 발동 여부를 결정함. DR의 host와 비교되는 필드가 아님. 매칭은 두 단계로 일어남.
istiod가 hosts:를 보고 이 VS의 규칙을 어느 Envoy virtualHost에 부착할지 결정함. sidecar(mesh) 트래픽의 경우 host는 service registry에 등록된 이름(K8s Service 또는 ServiceEntry)이어야 함. registry에 없는 이름을 쓰면 규칙이 어디에도 부착되지 않고 조용히 무시됨 — 에러가 나지 않아 발견이 늦어지는 유형임.
프로토콜에 따라 매칭 재료가 다름. L7 정보가 있는 프로토콜만 이름 기반 매칭이 가능함.
| 프로토콜 | 매칭 재료 | 비고 |
|---|---|---|
| HTTP/1.1, HTTP/2, gRPC | :authority (Host) 헤더 | virtualHost domains에 FQDN·short name·Service VIP 등 변형이 모두 등록되어 어느 형태로 불러도 매칭됨 |
| TLS (passthrough) | SNI | tls: 라우트 블록 사용 시 |
| Plain TCP | 목적지 IP (Service VIP) | L7 헤더가 없어 이름 매칭 불가 — Service VIP 기준 listener 매칭임 |
| gateways 필드 | 적용 범위 | hosts 매칭 대상 |
|---|---|---|
미지정 또는 mesh | 모든 sidecar의 outbound | mesh 내부 요청의 목적지 이름 (service registry 등록 필수) |
[my-gateway] | 해당 ingress gateway | Gateway servers[].hosts와의 교집합 + 외부 인입 요청의 Host/SNI. 외부 도메인(api.example.com) 사용 가능 |
[mesh, my-gateway] | 둘 다 | 각각 위 규칙 적용 |
spec.hosts = "요청의 목적지 이름과 매칭해 이 VS를 발동시킬지", route.destination.host = "발동 후 실제로 보낼 곳(= DR 매칭 키)". 단순 케이스에선 둘이 같지만, hosts: [reviews]로 받아 destination.host: reviews-canary로 보내는 식으로 다를 수 있음.VS와 DR을 잇는 키는 정확히 두 개임. 그 외 필드는 매핑에 관여하지 않음.
host: reviews처럼 short name을 쓰면 그 리소스가 정의된 namespace 기준으로 reviews.<ns>.svc.cluster.local로 확장됨. 호출자 namespace 기준이 아님. VS와 DR이 서로 다른 namespace에 있으면 같은 short name이 다른 FQDN으로 확장되어 매핑이 조용히 끊어짐. 운영 환경에서는 FQDN 명시가 원칙임.
디버깅 시 "어느 계층에서 끊겼는지"를 판단하려면 각 리소스가 어떤 xDS로 변환되는지 알아야 함. istioctl proxy-config의 서브커맨드가 이 계층과 1:1 대응임.
hosts가 virtualHost의 domains가 되고, route 규칙이 cluster 이름 문자열을 가리킴.outbound|<port>|<subset>|<FQDN> 이름의 cluster가 생성됨. subset 없는 기본 cluster는 subset 자리가 빈 문자열임.# DestinationRule — subset 정의 + 목적지 정책
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
namespace: default
spec:
host: reviews.default.svc.cluster.local # KEY ①
trafficPolicy:
loadBalancer:
simple: LEAST_REQUEST
subsets:
- name: v1 # KEY ②
labels:
version: v1 # Pod label과 일치 필요
- name: v2
labels:
version: v2
---
# VirtualService — 라우팅 규칙
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
namespace: default
spec:
hosts:
- reviews.default.svc.cluster.local # 요청 매칭용 (DR과 무관)
http:
- route:
- destination:
host: reviews.default.svc.cluster.local # KEY ① → DR.host
subset: v1 # KEY ② → subsets[].name
weight: 90
- destination:
host: reviews.default.svc.cluster.local
subset: v2
weight: 10
검증 커맨드는 06 디버깅 플레이북의 각 단계에 포함함. 정상 상태 기준의 예상 출력도 함께 표기함.
원칙: 정적 검증 → 동기화 확인 → xDS 계층 순회(Route → Cluster → Endpoint) → 런타임 관측 순으로 내려감. 각 단계는 "여기까지는 정상"을 확정하고 다음 계층으로 넘어가는 이분 탐색임.
istioctl analyze리소스 간 참조 무결성을 배포 전/후 검사함. subset 참조 오류, Gateway-VS 불일치, 미등록 host 등 대부분의 설정 오류를 클러스터에 트래픽을 흘리기 전에 잡아냄.
istioctl analyze -n default # 또는 --all-namespaces
# 대표 경고:
# IST0101 referenced DestinationRule subset not found → subset 이름 오타/DR 누락
# IST0132 host not found in registry → hosts에 미등록 이름
istioctl proxy-statusistiod가 만든 설정이 각 sidecar에 실제 전파됐는지 확인함. STALE이면 이후 단계를 봐도 의미 없음 — istiod 로그와 리소스 상태부터 확인 필요.
istioctl proxy-status
# 모든 컬럼(CDS/LDS/EDS/RDS)이 SYNCED 여야 함. STALE → istiod 쪽 문제
istioctl x describe특정 Pod에 어떤 VS·DR이 적용되는지, mTLS 상태, 경고를 한 번에 요약함. "이 Pod에 내 설정이 붙긴 했나"를 가장 빨리 확인하는 방법임.
istioctl experimental describe pod reviews-v2-abc -n default
호출자 Pod 기준으로 RDS를 조회함. virtualHost domains에 대상 host가 있고, 내 route 규칙(weight 등)이 원하는 cluster 이름을 가리키는지 확인함.
istioctl proxy-config route deploy/productpage -n default --name 9080 -o json \
| jq '.[0].virtualHosts[] | select(.name | startswith("reviews"))'
# 기대: weightedClusters에 outbound|9080|v1|... =90, |v2|... =10
# 규칙이 안 보임 → VS hosts가 registry 미등록이거나 exportTo로 가려짐
istioctl proxy-config cluster deploy/productpage -n default \
--fqdn reviews.default.svc.cluster.local
# 기대: subset 컬럼에 v1, v2 행이 각각 존재
# subset 행 없음 → DR 미적용 (host FQDN 불일치, exportTo, namespace 확인)
istioctl proxy-config endpoint deploy/productpage -n default \
--cluster "outbound|9080|v2|reviews.default.svc.cluster.local"
# 기대: version=v2 Pod IP들이 HEALTHY
# 0개 → subset labels와 Pod label 불일치. 아래로 교차 확인:
kubectl get pods -n default -l app=reviews --show-labels
kubectl get endpointslices -n default -l kubernetes.io/service-name=reviews
실제 요청이 어느 계층에서 실패하는지는 호출자 sidecar의 access log flag가 알려줌. flag → 계층 매핑이 진단의 핵심임 (아래 표 참조).
kubectl logs deploy/productpage -n default -c istio-proxy --tail=100 | grep -E ' 50[03] '
# "... 503 NC ..." → cluster 부재 "... 503 UH ..." → endpoint 부재
# Envoy 전체 설정 스냅샷 (istioctl 출력의 원본)
kubectl exec deploy/productpage -n default -c istio-proxy -- \
curl -s localhost:15000/config_dump > dump.json
# 라우팅 판단 과정을 로그로 직접 관찰
istioctl proxy-config log deploy/productpage -n default --level router:debug,upstream:debug
표는 색인이며, 각 flag의 진단 방향은 아래 해설 참조.
| Flag | 의미 | 의심 계층 | 1차 확인 |
|---|---|---|---|
| NR | NoRoute — 매칭되는 route 없음 | VS / hosts | pc route, VS hosts 철자·registry 등록 |
| NC | NoCluster — route가 가리키는 cluster 없음 | DR / host·subset | pc cluster, DR host FQDN·exportTo |
| UH | NoHealthyUpstream — cluster는 있으나 endpoint 0개 | subset labels / Pod | pc endpoint, Pod label·readiness |
| UF | UpstreamConnectionFailure — 연결 실패 | mTLS / NetworkPolicy / 포트 | DR tls 모드 ↔ PeerAuthentication 정합, Calico policy |
| UO | UpstreamOverflow — circuit breaker 발동 | DR connectionPool | pc cluster -o json의 thresholds, :15000/stats의 upstream_rq_pending_overflow |
| URX | 재시도 한도 초과 | VS retries / upstream 불안정 | upstream 5xx 원인 추적 |
| DC | DownstreamConnectionTermination — 클라이언트가 끊음 | 클라이언트 timeout | 호출자 timeout vs VS timeout 비교 |
pc cluster → pc endpoint 순 확인으로 즉시 구분됨.tls.mode: DISABLE을 걸었거나, sidecar 없는 워크로드로 ISTIO_MUTUAL 트래픽을 보낸 경우가 전형적임.| 증상 | 진입 단계 | 전형적 원인 |
|---|---|---|
| 배포 직후 전면 503 | 1 → 7 (flag 확인) | subset 오타(NC), DR host 불일치(NC) |
| 특정 namespace에서만 503 | 4, 5 | VS/DR exportTo 제한, short name의 namespace 확장 차이 |
| 가중치가 반영 안 됨 | 4 | 다른 VS가 같은 host 선점(host당 sidecar 기준 VS 규칙 충돌), match 순서 |
| 일부 요청만 간헐 503 | 6, 7 | 일부 Pod not-ready(UH 간헐), outlier detection ejection, rollout 중 label 전환 |
| 설정 바꿨는데 반영 안 됨 | 2 | proxy STALE, istiod 재시작 필요 상태, webhook 오류로 리소스 미반영 |
| mTLS 관련 연결 실패 | 7 (UF) | DR tls 모드 ↔ PeerAuthentication 불일치 |
VS가 subset: v2를 참조하는데 DR이 없거나 host FQDN이 불일치하는 경우. cluster outbound|9080|v2|... 자체가 생성되지 않아 route가 존재하지 않는 cluster 이름을 가리킴. istioctl analyze의 IST0101로 사전 탐지 가능함.
DR은 정상 적용됐으나 version: v2 label을 가진 Pod가 0개(오타, rollout 중 label 변경, 전부 not-ready). cluster는 존재하지만 EDS 결과가 빈 목록임. 정적 검증으로는 안 잡히고 런타임에만 드러남 — pc endpoint가 유일한 확정 수단임.
VS는 team-a, DR은 team-b에 있고 둘 다 host: reviews로 작성한 경우. 각각 reviews.team-a..., reviews.team-b...로 확장되어 KEY ①이 불일치함. 에러 없이 DR 정책만 미적용되는 형태라 발견이 늦음. subset까지 참조했다면 NC로 표면화되지만, trafficPolicy만 있는 DR이면 증상 없이 정책만 사라짐.
DR에 exportTo: ["."]가 걸려 있으면 다른 namespace의 sidecar에는 subset cluster가 생성되지 않음. "같은 namespace에서는 되는데 다른 namespace에서 호출하면 NC"라는 위치 의존적 증상이 시그니처임. 호출자 Pod 기준으로 pc cluster를 조회해야 보이는 문제이므로, 반드시 실패하는 쪽 Pod를 기준으로 proxy-config를 확인해야 함.