# 50-dns-resolution — ServiceEntry `resolution` 거동 재현 랩 `ServiceEntry.spec.resolution` 값(`DNS` vs `DNS_ROUND_ROBIN`)이 Envoy cluster type을 가르고, GSLB처럼 "같은 도메인이 매번 다른 IP를 주는" 환경에서 **기존 세션 유지/드롭**과 **트래픽 유실**이 어떻게 갈리는지를 라이브로 재현하는 랩이다. 이론 정리는 런북 `docs/runbooks/2026-06-07_serviceentry-dns-resolution.md`에 있다. 이 폴더는 그 이론을 **눈으로 증명하는 실험 장치**다. --- ## 1. 무엇을 증명하나 (메커니즘) ``` resolution: DNS -> Envoy STRICT_DNS host set = A record 전체. refresh 시 사라진 IP host 제거 -> 그 host의 커넥션을 drain(HTTP GOAWAY/connection:close) => IP flip 시 기존 롱세션 끊김(Mode1), 죽은 IP를 LB가 고르면 유실(Mode2) resolution: DNS_ROUND_ROBIN -> Envoy LOGICAL_DNS host = 논리 1개. 신규 커넥션만 최신 DNS, 기존 커넥션은 절대 drain 안 함 => IP flip 후에도 기존 세션 유지(Mode1 대조), 단 물고 있던 IP가 죽어야 뒤늦게 재연결(Mode3 = 트레이드오프) ``` | 이벤트 | `DNS` (STRICT_DNS) | `DNS_ROUND_ROBIN` (LOGICAL_DNS) | |---|---|---| | DNS를 IP1→IP2로 flip | 기존 롱세션 **drain/RST**, 신규는 IP2 | 기존 세션 **유지**, 신규만 IP2 | | 삭제됐으나 캐시된 dead IP | LB가 고르면 **connect 실패=유실** | 논리 1개, 신규 연결은 최신 DNS | | 유지 중인 IP가 죽음 | (해당 host 제거되며 정리) | **뒤늦게** 끊기고 재연결(liveness를 DNS가 아닌 커넥션 단절에 위임) | --- ## 2. 랩 구조 (자기완결형 — 공유 인프라 무변경) ``` +------------------- ns: dns-lab (injection=enabled) --------------------+ | | | [client: fortio / netshoot] dnsPolicy:None | | dnsConfig.nameservers = [lab-dns ClusterIP] | | | http://gslb.lab.internal (평문 HTTP, keepalive) | | v istio-proxy: SE.resolution -> STRICT_DNS | LOGICAL_DNS | | Envoy(c-ares) --A? gslb.lab.internal--> [lab-dns: CoreDNS] | | | ^ flip = writer(busybox)가 | | | | /hosts/addn 재작성 (+reload 2s) | | | VS:80->443 + DR:TLS origination(SIMPLE) | | +--TLS--> [backend-a svc/ClusterIP-1] -> "backend-a" | | +--TLS--> [backend-b svc/ClusterIP-2] -> "backend-b" | | | | lab-dns: gslb.lab.internal 만 authoritative(ttl 5s), | | 나머지는 cluster DNS로 forward (istiod resolve 유지) | +------------------------------------------------------------------------+ egress gateway 미경유 · cluster CoreDNS 무변경 -> blast radius = dns-lab 내부 ``` **왜 이 구조인가 (비자명한 결정 4가지)** 1. **client는 평문 HTTP로 호출** → sidecar가 TLS origination. 이래야 Envoy가 upstream을 HTTP conn pool(L7)로 관리 → host 제거 시 draining이 L7(GOAWAY/close)로 관측된다. (client가 https 직접 전송이면 SNI passthrough=L4라 L7 draining이 안 보임.) 2. **lab-dns는 gslb.lab.internal만 잡고 나머지는 forward.** 안 그러면 client sidecar가 istiod(xDS 대상)를 resolve 못 해 프록시가 죽는다(자기완결형의 함정). 3. **CoreDNS는 distroless라 shell이 없어** exec-write 불가 → busybox `writer` 사이드카가 shared emptyDir의 hosts 파일을 재작성하는 방식으로 flip. 4. **backend는 sidecar 미주입**(외부 서버 흉내). 붙으면 mTLS가 origination에 끼어든다. --- ## 3. 실행 순서 ```bash # 0) 전제: 클러스터 가동 + istioctl 1.30 + 이 repo # 1) 랩 부트스트랩(멱등): 인증서/secret, lab-dns, backends, client, 초기 GSLB=A bash scripts/dns-lab-setup.sh # 2) DNS 경로 확인 kubectl -n dns-lab exec deploy/netshoot -- dig +short gslb.lab.internal # 3) 실험 (리포트는 docs/test-reports/ 로 저장) bash scripts/dns-flip-test.sh strict mode1 # STRICT: flip 시 롱세션 drain bash scripts/dns-flip-test.sh logical mode1 # LOGICAL: flip 후 세션 유지 (대조) bash scripts/dns-flip-test.sh strict mode2 # dead IP 유실 -> outlier+retry 복구 bash scripts/dns-flip-test.sh logical mode3 # LOGICAL 트레이드오프(뒤늦은 재연결) ``` --- ## 4. 판정 관점 (construction 로그로 기록) "합격/불합격"이 아니라 **"무엇을 했고 그때 무엇이 보였나"**를 남긴다. 핵심 관측 지표: - `istioctl proxy-config endpoints deploy/fortio.dns-lab --cluster "outbound|443||gslb.lab.internal"` → STRICT는 endpoint 집합이 flip 따라 A→B, LOGICAL은 논리 1개. - Envoy stats `upstream_cx_destroy` / `membership_change` → STRICT는 flip 시점에 증가(기존 커넥션 정리), LOGICAL은 미증가. - `fortio -keepalive`의 *Sockets used* 와 loop의 `who=backend-a/b` 타임라인 → 세션 유지/드롭이 언제 넘어가는지. - Mode2: loop의 `code!=200` 비율(=유실)이 outlier+retry 적용 후 0으로 수렴. --- ## 5. 알려진 함정 / 리스크 - **LOGICAL_DNS + multi-IP A record = CDS NACK** 위험(memory: dual-gateway-egress-lab). Mode1/Mode3 flip은 단일 IP→단일 IP로만. Mode2(멀티 IP)는 STRICT에서만. 전 변형 `exportTo:["."]`로 dns-lab 내부 한정 → gateway 전역 영향 원천 차단. - 두 SE 변형은 host가 같아 **동시 적용 불가** — 하네스가 하나만 apply(나머지 delete). - CoreDNS 이미지 태그(`v1.11.3`)는 클러스터 보유본과 다르면 pull 필요 — 필요 시 조정. - `insecureSkipVerify:true`는 랩 단순화용. prod는 caCertificates/credentialName로 CA 핀.