homelab89 Docs Logs Legacy Files ☰ TOC 🌓
noteistio 2026-06-07dnsegressenvoyservice-entryoutlier-detection

Runbook — ServiceEntry `resolution: DNS` 동작과 진단 (STRICT_DNS)

ABSTRACT

외부 도메인(httpbin.org)을 ServiceEntry(resolution: DNS)로 등록하면 istiod가 이를 Envoy의 STRICT_DNS cluster로 변환한다. 이 문서는 그 변환·DNS 갱신 메커니즘, “죽은 IP” 처리, ambient DNS 질의 경로를 홈랩 실측·재현 명령과 함께 한 줄의 멘탈모델로 꿴다. 결론 한 문장: DNS refresh는 health check가 아니다 — DNS는 “목록"만 줄 뿐 IP의 생사를 모르므로, 죽은 IP 회피는 outlier detection으로 명시해야 한다.

환경: cluster homelab(k8s v1.30.6, istiod 1.30.0 / istioctl 1.27.0 client·skew) / egress gateway 경유 / 관련: ingress·egress 통합 리포트


1. 배경 — 왜 mesh가 외부 도메인의 IP를 직접 들어야 하나

mesh 안 트래픽은 전부 Envoy가 가로채 라우팅한다(sidecar/gateway). 그런데 Envoy는 “IP:port의 묶음"인 cluster 단위로만 upstream을 안다 — Envoy의 세계에는 호스트네임이 없고 endpoint 집합만 있다. mesh 내부 서비스는 istiod가 k8s Endpoints/EndpointSlice를 watch해서 cluster를 자동으로 채워준다. 하지만 httpbin.org 같은 mesh 밖 외부 도메인은 k8s에 Endpoints가 없다. istiod가 그 IP를 알 길이 없다.

ServiceEntry는 바로 이 공백을 메우는 CRD다 — “이 호스트는 mesh의 일부로 취급하라, IP는 이렇게 알아내라"를 선언한다. 그 “IP를 알아내는 방식"이 spec.resolution 필드이고, 이 한 필드가 Envoy cluster의 종류를 결정한다. 즉 외부 도메인 한 줄을 등록할 때 진짜로 고르는 것은 “Envoy가 IP를 들고 LB하는 전략"이다.

여기서 자연스럽게 따라오는 질문 4개가 이 문서의 뼈대다 — 이 순서대로 읽으면 길을 잃지 않는다:

resolution  ->  cluster type  ->  갱신 주기  ->  죽은 IP  ->  회피 구성
 (어떻게         (STRICT_DNS    (DNS TTL을     (DNS는        (outlier
  IP를 안다)      / LOGICAL)     따라 재질의)    생사 모름)     detection+retry)

선행 개념: Envoy cluster = endpoint(IP:port) 집합 + LB 정책 / cluster 이름 규칙 direction|port|subset|fqdn (예: outbound|443||httpbin.org) / egress gateway = mesh 밖으로 나가는 트래픽을 한 노드로 모으는 전용 Envoy. cluster 구조 자체는 Cluster 해부 참조.


2. 머릿속에 둘 그림 — DNS는 “목록”, 생사는 별도 책임

이 문서 전체를 꿰는 앵커 한 문장:

resolution이 Envoy cluster type을 정하고, cluster type이 “IP를 어떻게 들고 LB할지"를 정한다. 그러나 어느 type이든 DNS는 IP “목록"만 줄 뿐 그 IP가 살아있는지는 모른다 — liveness는 outlier detection의 별도 책임이다.

이 한 문장에서 나머지가 전부 파생된다. resolution을 STRICT_DNS로 두면 A record의 모든 IP를 펼쳐 Envoy가 직접 LB하고(§3), 갱신 주기는 DNS TTL을 따른다(§4). 하지만 TTL 만료 전에 IP가 죽으면 Envoy는 그 IP를 여전히 HEALTHY로 들고 있어 연결이 실패한다(§5) — DNS refresh는 단지 “목록 다시 받기"이지 “이 IP 살아있냐"를 묻는 게 아니기 때문이다. 그래서 죽은 IP 회피는 outlier detection + retry로 명시해야 하고(§7), GSLB 뒤라면 LB 권한을 DNS에 위임하는 LOGICAL_DNS가 더 맞는다(§6). 마지막으로 “그 DNS 질의를 실제로 누가 쏘는가"가 진단의 출발점이다 — egress 경유 시 그 주체는 egress gateway의 Envoy(c-ares)다(§8).

왜 이렇게 책임을 쪼갰는가? DNS와 health는 원래 다른 시스템이기 때문이다. DNS 서버는 “이 이름에 어떤 IP들이 매핑되는가"의 권위자일 뿐, 그 IP 뒤 프로세스가 지금 요청을 받을 수 있는지는 모른다(특히 같은 A record 안에서 일부만 죽은 경우). Envoy는 이 둘을 의도적으로 분리한다 — DNS는 endpoint 발견(discovery), outlier detection은 endpoint 축출(ejection). 이 분리를 모르면 “DNS만 믿으면 알아서 죽은 IP 빠지겠지"라는 가장 흔한 오해에 빠진다.


3. 변환 구조 — resolution이 cluster type을 결정한다

ServiceEntry.spec.resolution 필드가 Envoy cluster type을 결정한다. 이 매핑을 먼저 확립해야 이후 갱신 주기(§4)·죽은 IP(§5)를 STRICT/LOGICAL 대비로 읽을 수 있다.

resolution Envoy type endpoint 적재 적합 대상
DNS STRICT_DNS A record 모든 IP를 펼침 개별 IP를 Envoy가 직접 LB하고 싶을 때
DNS_ROUND_ROBIN LOGICAL_DNS 첫 IP 1개만, 연결 재사용 CDN/GSLB/대형 LB 뒤 “논리적 단일 endpoint”

차이의 본질은 “endpoint를 펼치느냐(STRICT) vs 한 점으로 접느냐(LOGICAL)” 한 곳이다 — 나머지(갱신·LB·연결 재사용)는 전부 이 한 델타에서 따라온다. resolution: DNS(STRICT_DNS) 변환 결과:

ServiceEntry(resolution: DNS, hosts: httpbin.org)
        |  istiod 변환
        v
Envoy cluster "outbound|443||httpbin.org"
   type: STRICT_DNS          <- Envoy가 직접 DNS 질의
   respect_dns_ttl: true     <- 갱신 주기 = DNS 응답의 TTL
   dns_refresh_rate: 60s     <- TTL이 0/없을 때만 쓰는 fallback
   endpoints: [8 IPs]        <- A record 전체를 펼쳐서 보관

경유 구성 주의: 트래픽 경로가 sleep → egress gateway → httpbin.org이므로 실제 외부 DNS를 도는 주체는 egress gateway의 Envoy다. sleep proxy에도 같은 cluster가 보이지만 라우팅이 egress gateway로 향하므로 실질 사용처가 아니다. 진단은 egress gateway 기준.

STRICT_DNS vs LOGICAL_DNS 실측 (example.comDNS_ROUND_ROBIN으로 별도 등록):

SERVICE FQDN    TYPE          endpoints(proxy-config)
httpbin.org     STRICT_DNS    다수 (A record 전체)
example.com     LOGICAL_DNS   1

istioctl proxy-config endpoints <pod> --cluster "outbound|443||<host>"의 endpoint 개수가 STRICT=다수 / LOGICAL=1로 갈리는 것이 Envoy 반영의 결정적 증거다. LOGICAL_DNS의 핵심은 “DNS 레코드가 자주 바뀌어도 기존 연결을 유지(connection draining/cycling 제거)“라서, 공식 문서가 “large web scale services accessed via DNS"에 권장한다(상세 권장 근거는 §6).


4. 갱신 주기 결정 규칙 — 왜 60s가 함정값인가

필드 현재 값 의미
respect_dns_ttl true DNS 응답 TTL을 갱신 주기로 사용
dns_refresh_rate 60s TTL이 0/없을 때만 쓰는 fallback
httpbin.org 실제 TTL 10s (dig) 실제 갱신 ≈ 10초

dns_refresh_rate: 60s는 함정값이다 — config_dump에 60s가 박혀 있어도 respect_dns_ttl: true면 거의 안 쓰인다(TTL=0이거나 응답에 TTL이 없을 때만 fallback으로 등판). 즉 실제 갱신 주기를 알려면 cluster 설정이 아니라 권위 DNS의 TTL을 봐야 한다 — config만 읽고 “60초마다 갱신되겠지"라고 단정하면 틀린다(실측은 10초).

mesh 전역 기본은 meshConfig.dnsRefreshRate 필드(configmap/istio.data.mesh)로 조정한다. 단 효력 범위는 좁다: respect_dns_ttl=true인 외부 DNS cluster에서는 TTL이 우선이라 거의 무효이고, TTL을 무시하는 cluster나 NDS(DNS auto-allocation) 경로에 실질 영향을 준다. 확인:

kubectl --context homelab -n istio-system get cm istio -o jsonpath='{.data.mesh}' | grep -i dnsRefreshRate
# 미설정이면 출력 없음 = Envoy 기본 fallback(위 60s)

5. “죽은 IP” 처리 — 이 문서의 심장

§2 앵커의 핵심이 여기서 구체화된다. DNS refresh는 liveness 체크가 아니므로, TTL 만료 전에 IP가 죽으면 Envoy는 그 IP를 HEALTHY로 들고 있다가 LB가 고르면 연결 실패한다. 갱신 주기를 아무리 줄여도 이 창은 닫히지 않는다 — TTL이 10초여도, 죽고 나서 다음 재질의 전까지(그리고 권위 DNS가 그 IP를 빼줄 때까지)는 무방비다.

[갱신 전, IP 죽음]
client --> egress GW --> [죽은 IP 선택] --X TCP connect 실패
                          +-- retry O      --> 다른 IP 재시도 --> 성공
                          +-- retry X      --> 요청 실패 (503/connect error)

IP가 LB 목록에서 빠지는 경로는 3가지이고 서로 독립적이다 — 어느 것을 켜느냐가 죽은 IP 회피의 설계다:

메커니즘 트리거 기본 구성
DNS refresh TTL 만료 → 재질의 시 DNS가 해당 IP 제외 O (수동적)
Outlier detection (passive HC) 연속 5xx/connect 실패 → 일시 ejection X (DestinationRule 미설정)
Active health check Envoy가 직접 주기적 probe X

세 경로를 갈라 보는 이유: DNS refresh는 권위 DNS가 빼주길 기다리는 수동적 경로라 내 통제 밖이다. outlier detection은 내가 본 실패로 즉시 빼는 능동적 경로라 통제 안에 있다. 그래서 —

프로덕션 결론: 죽은 IP 회피는 DNS TTL에 기대지 말고 DestinationRule.trafficPolicy.outlierDetection + VirtualService.http.retries로 명시 설정한다. 구체 구성(YAML)·passthrough 함정은 §7, outlier 필드 의미의 정본은 circuit-breaking 메커니즘 note 참조.


6. GSLB 환경 권장 — LOGICAL_DNS

GSLB(DNS가 클라이언트 위치·health로 최적 IP를 고르는 모델) 뒤 호스트는 LOGICAL_DNS 권장. 이유를 한 축으로 압축하면 “LB 권한을 누구에게 둘 것인가” — GSLB가 이미 최적 IP를 골라 주는데 STRICT_DNS로 전체 IP를 펼치면 Envoy가 그 위에서 또 LB하며 GSLB 결정을 덮어쓴다.

근거:

  1. GSLB 결정 존중: STRICT_DNS는 A record 전부를 펼쳐 Envoy가 자체 LB → GSLB가 고른 IP 외 원격 리전까지 섞어 GSLB 의도 무력화. LOGICAL_DNS는 DNS가 준 첫 IP만 써 결정을 따른다.
  2. 동적 변경 + 짧은 TTL 친화: LOGICAL_DNS는 새 연결 시 재해석해 최신 GSLB 결정 반영. STRICT_DNS는 전체 IP에 conn pool 유지 → stale·원격 연결 부담.
  3. 자원 낭비 방지: GSLB 뒤는 사실상 “하나의 논리적 endpoint” → 전체 IP에 사전 연결은 낭비.

Trade-off: LOGICAL_DNS는 endpoint가 1개라 Envoy outlier/세밀 LB 효력이 약화 → “장애 판정을 GSLB에 위임"하는 셈. GSLB health가 느슨하면 STRICT_DNS + outlier가 나을 수도. 본질은 “LB 권한을 DNS(GSLB)에 둘 것인가 Envoy에 둘 것인가"의 선택.


7. 죽은 IP 회피 구성 — outlier detection + retry

DNS refresh는 liveness를 모르므로(§5), 죽은 IP 회피는 아래로 명시한다. outlier detection 필드 자체의 일반 의미(consecutive5xxErrors/baseEjectionTime 배수 증가 등)는 circuit-breaking 메커니즘 note가 정본이고, 여기서는 egress passthrough(L4) 특수성에 집중한다. 이 특수성 한 줄이 §7 전체를 지배한다: egress gateway가 TLS PASSTHROUGH면 HTTP/5xx를 못 보므로, L7에 기대는 회피 장치는 전부 무력화된다.

outlier detection (DestinationRule.trafficPolicy.outlierDetection — 적용 후 Envoy 반영 확인됨):

trafficPolicy:
  connectionPool: { tcp: { connectTimeout: 2s } }  # connect 실패 빨리 판정
  outlierDetection:
    consecutiveLocalOriginFailures: 3   # ★ connect 실패(L4) 3회 → eject  (passthrough 핵심)
    splitExternalLocalOriginErrors: true
    consecutive5xxErrors: 5             # 5xx(L7) 5회 → eject
    interval: 10s
    baseEjectionTime: 30s
    maxEjectionPercent: 50              # 전멸 방지
    minHealthPercent: 40

egress 특화 포인트: TLS PASSTHROUGH(L4)에서 egress gateway는 HTTP/5xx를 볼 수 없으므로 consecutive5xxErrors는 발화하지 않는다. consecutiveLocalOriginFailures(connect 실패 기반)가 유일한 자동 회피 경로다. 따라서 짧은 connectTimeout과 함께 써야 빠르게 eject된다.

retry (VirtualService.http.retries) — L7 HTTP route에서만 동작:

http:
  - route: [...]
    retries:
      attempts: 3
      perTryTimeout: 2s
      retryOn: connect-failure,refused-stream,5xx,gateway-error

⚠️ 함정: 현재 egress는 TLS PASSTHROUGH(L4) 라 egress gateway가 HTTP를 못 본다 → http.retries조용히 무시된다. retry를 쓰려면 TLS origination(L7) 구성으로 전환해야 한다.

  • passthrough(L4): outlierDetection(consecutiveLocalOriginFailures) + 짧은 connectTimeout 이 유일한 자동 회피
  • TLS origination(L7): 위 + http.retries(retryOn: connect-failure)

8. 적용·검증 (재현 가능)

8.1 진단 명령 — cluster가 어떻게 IP를 들고 있나

CTX=homelab
GW=deploy/istio-egressgateway.istio-system
CL="outbound|443||httpbin.org"

# (1) 현재 endpoint IP 목록 + outlier 상태
istioctl --context $CTX proxy-config endpoints $GW --cluster "$CL"

# (2) cluster DNS 설정 (type / refresh_rate / respect_dns_ttl)
kubectl --context $CTX -n istio-system exec deploy/istio-egressgateway -c istio-proxy -- \
  curl -s "localhost:15000/config_dump?resource=dynamic_active_clusters" \
  | grep -A10 '"'"$CL"'"'

# (3) 런타임 endpoint 상태(success_rate, healthy flags)
kubectl --context $CTX -n istio-system exec deploy/istio-egressgateway -c istio-proxy -- \
  curl -s "localhost:15000/clusters" | grep "httpbin.org"

# (4) DNS 원천 데이터(IP + TTL)
dig +noall +answer httpbin.org           # TTL = 첫 컬럼 숫자

# (5) DNS 갱신 카운터를 보려면 sidecar stats 필터를 풀어야 함(기본 미노출):
#   pod annotation proxy.istio.io/config 의 proxyStatsMatcher.inclusionRegexps 에 ".*httpbin\.org.*" 추가

검증 실험 — “DNS 주기 = endpoint 갱신 주기”: dig IP 집합과 proxy-config endpoints를 ~10초 간격 2회씩 떠서, IP 변동 시 endpoint도 따라 바뀌면 둘이 동기임이 확인된다. 이게 §4의 “실제 갱신 ≈ TTL(10s)“의 실측 근거다.

8.2 retry가 정말 무시됨을 입증 (gap finding: 기대 vs 실제)

§7의 “L4에서 http.retries는 조용히 무시"라는 주장은 추정이 아니라 두 관측으로 입증된다 — HTTP route가 없으면 retry 정책이 붙을 곳이 없고, 붙을 곳이 없으면 retry 카운터가 영영 0이다:

# (a) egress gateway에 HTTP route가 없음 = retry 정책이 붙을 곳이 없음을 확인.
#     passthrough는 TCP/SNI 매칭만 존재 → routes 출력이 비거나 TCP proxy만 보임.
istioctl --context homelab proxy-config routes deploy/istio-egressgateway.istio-system \
  | grep -i httpbin            # 기대: 매칭 없음(HTTP route 부재)

# (b) retry 카운터가 0으로 고정 = 재시도가 한 번도 발생하지 않음.
kubectl --context homelab -n istio-system exec deploy/istio-egressgateway -c istio-proxy -- \
  curl -s localhost:15000/stats | grep 'httpbin.*upstream_rq_retry'
#   기대: upstream_rq_retry: 0 (혹은 카운터 자체 부재) — L4라 retry 경로 미존재

9. “ambient DNS” — 도메인 질의는 어디로 가나 (실측)

§2 앵커의 마지막 가닥: “그 DNS 질의를 누가 쏘는가"가 진단의 출발점이다. config_dump의 cluster를 아무리 들여다봐도, 실제 질의를 누가/어디로 보내는지를 모르면 “왜 stale IP가 안 빠지나"를 추적할 수 없다.

공식 문서의 “querying the ambient DNS"에서 ambient는 Istio ambient mesh가 아니라 “그 프록시 환경에 깔린 기본 resolver(/etc/resolv.conf)“라는 영어 일반어다. STRICT/LOGICAL 둘 다 동일.

측정 결과 질의 경로(실선 = DNS capture OFF 실측 경로, 점선 = capture ON 시 분기):

flowchart LR
  GW["egress GW Envoy<br/>(c-ares; no dns_resolver)"]
  RC["/etc/resolv.conf<br/>nameserver 169.254.25.10"]
  NL["nodelocaldns<br/>169.254.25.10 (link-local)"]
  CD["CoreDNS<br/>(cluster DNS)"]
  UP["upstream DNS<br/>(recursive)"]
  AU["authoritative<br/>A record"]
  AG["istio-agent<br/>DNS proxy :53"]

  GW --> RC --> NL --> CD
  CD -->|"not cluster domain"| UP --> AU
  AU -. "A record back" .-> GW
  GW -. "capture ON" .-> AG
  AG -. "internal: NDS local / external: forward" .-> UP

핵심: **질의 주체는 egress gateway의 Envoy(c-ares)**이며, capture OFF에서는 istio-agent를 거치지 않고 resolv.conf의 nodelocaldns → CoreDNS → upstream으로 직접 나간다.

확인된 사실:

  • 질의 주체 = Envoy의 c-ares (typed_dns_resolver_config: envoy.network.dns_resolver.cares). resolver 주소 미지정 → c-ares가 resolv.conf를 그대로 읽음.
  • sleep/egress gateway pod의 nameserver = 169.254.25.10 (nodelocaldns) → CoreDNS → 외부 upstream.
  • Istio DNS capture(ISTIO_META_DNS_CAPTURE)는 비활성 (meshConfig·pod env 모두 미주입) → istio-agent DNS proxy를 안 거치고 Envoy가 직접 나간다.

DNS capture를 켜면: istio-agent가 53을 가로채(DNS proxy), 메시 내부 호스트는 NDS로 로컬 응답, 외부만 upstream 포워딩 → “질의 위치"가 istio-agent로 바뀐다. 진단 전에 capture 여부를 먼저 봐야 하는 이유.

진단 명령:

# resolver(=ambient DNS) 확인
kubectl -n mesh-test exec deploy/sleep -c istio-proxy -- cat /etc/resolv.conf
# Envoy가 쓰는 resolver 종류
kubectl -n istio-system exec deploy/istio-egressgateway -c istio-proxy -- \
  curl -s "localhost:15000/config_dump?resource=dynamic_active_clusters" \
  | grep -A60 '"outbound|443||httpbin.org"' | grep -i dns_resolver
# DNS capture 활성 여부
kubectl -n istio-system get cm istio -o jsonpath='{.data.mesh}' | grep -i DNS_CAPTURE

핵심 정리

  • resolution이 cluster type을 결정: DNS→STRICT_DNS(A record 전 IP 펼침, Envoy 자체 LB), DNS_ROUND_ROBIN→LOGICAL_DNS(첫 IP 1개, 연결 재사용). endpoint 개수가 결정적 증거.
  • DNS refresh ≠ health check. DNS는 endpoint 발견, outlier detection은 endpoint 축출 — liveness는 outlier detection / active HC의 몫이지 DNS TTL이 아니다.
  • respect_dns_ttl=true면 실제 갱신 주기는 권위 DNS TTL(실측 10s)이지 dns_refresh_rate(60s)가 아니다.
  • egress gateway 경유 시 DNS 질의 주체는 gateway의 Envoy(c-ares), 클라이언트 sidecar가 아니다.
  • passthrough(L4)에서는 consecutiveLocalOriginFailures가 유일한 자동 회피http.retries·consecutive5xxErrors는 HTTP를 못 봐서 무효(§7).
  • GSLB 뒤 호스트는 LOGICAL_DNS — LB 권한을 DNS에 위임. STRICT_DNS는 GSLB 의도를 무력화.

What you might be missing

  • 갱신 주기를 줄여도 죽은 IP 창은 안 닫힌다: TTL을 10초로 줘도, IP가 죽고 다음 재질의 + 권위 DNS가 빼줄 때까지는 무방비. 능동 축출(outlier)이 없으면 DNS 주기 튜닝은 근본 해법이 아니다(§5).
  • dnsRefreshRate의 좁은 효력: meshConfig 필드지만 respect_dns_ttl=true 외부 cluster에선 TTL이 우선이라 거의 무효다. 실제로는 TTL을 무시하는 cluster나 NDS auto-allocation 경로에만 작동(§4).
  • “silent” 함정: http.retries는 passthrough에서 에러 없이 그냥 무시된다. proxy-config에 HTTP route가 없고 retry 카운터가 0인지로만 확인 가능(§8.2 (a)(b)).
  • “ambient DNS"는 Istio ambient mesh가 아니다 — 프록시 환경의 기본 resolver(resolv.conf)를 뜻하는 일반어.
  • DNS capture 토글이 질의 위치를 바꾼다: capture ON이면 istio-agent가 53을 가로채 질의 주체가 바뀌므로, 진단 시 capture 활성 여부를 먼저 확인해야 경로 추론이 어긋나지 않는다(§9).

참조


관련 파일 · 참조

검증 기록 (2026-07-05 · Istio 1.30.0 / k8s 1.30.6)

검증 방법: 공식 문서(istio/api proto, Envoy 문서, istio.io reference) 대조 + homelab 클러스터(istiod 1.30.0) 실측. 본문의 모든 주장은 문헌상 확인되었고, 핵심 주장(cluster type 변환, DNS refresh는 liveness check가 아님, TLS PASSTHROUGH에서 retry 무시)은 클러스터 실측으로 추가 확정되어 본문 수정 없이 유지함.

주장 판정 근거
C1. resolution: DNS → Envoy STRICT_DNS cluster 변환 ✅ 실측 확인 istio/api service_entry.proto · T49 실측
C2. resolution: DNS_ROUND_ROBIN → Envoy LOGICAL_DNS cluster 변환 ✅ 문헌 확인 istio/api service_entry.proto
C3. STRICT_DNS는 A record 전체 IP를 endpoint로 적재 ✅ 문헌 확인 envoyproxy.io service_discovery
C4. LOGICAL_DNS는 첫 IP만 사용, 연결 유지(draining 없음) ✅ 문헌 확인 envoyproxy.io service_discovery
C5. respect_dns_ttl=truedns_refresh_rate는 TTL=0/실패시만 쓰는 fallback ✅ 문헌 확인 envoyproxy.io cluster.proto
C6. DNS refresh는 liveness check가 아니다(문서의 핵심 명제) ✅ 실측 확인 envoyproxy.io outlier · T50 실측
C7. outlier detection은 기본 off, DestinationRule에 명시해야 opt-in ✅ 문헌 확인 istio.io destination-rule
C8. meshConfig.dnsRefreshRaterespect_dns_ttl=true 외부 cluster에는 거의 무효 ✅ 문헌 확인 istio.io istio.mesh.v1alpha1
C9. LOGICAL_DNS는 GSLB/CDN류 “large web scale services"에 공식 권장 ✅ 문헌 확인 envoyproxy.io service_discovery
C10. TLS PASSTHROUGH(L4)에서 consecutive5xxErrors 무효, consecutiveLocalOriginFailures만 유효 ✅ 문헌 확인 envoyproxy.io outlier
C11. TLS PASSTHROUGH에서 http.retries는 조용히 무시됨 ✅ 실측 확인 istio.io virtual-service · T47 실측
C12. “querying the ambient DNS"의 ambient는 Istio ambient mesh가 아니라 일반어 ✅ 문헌 확인 istio/api service_entry.proto
C13. resolver 미지정 시 Envoy 기본 DNS resolver는 c-ares ✅ 문헌 확인 envoyproxy.io cares_dns_resolver.proto
C14. sidecar DNS capture는 기본 비활성(opt-in), ambient는 1.25+ 기본 활성 ✅ 문헌 확인 istio.io dns-proxy
C15. Envoy/Istio cluster 이름 규칙 direction|port|subset|fqdn ✅ 문헌 확인 istio.io proxy-cmd

Files