================================================================================ T92 — DNS/GSLB 재현 랩: 번들 킷 전체 생애주기 라이브 실측 실측일 2026-07-05 · cluster homelab (k8s 1.30.6, Calico) · Istio 1.30.0 (client/CP/DP 일치) kubectl context = homelab (setup/flip-test/cleanup 모두 현재 컨텍스트 자동 감지로 동작) 번들 킷: content/docs/istio/egress/dns-gslb-repro-lab/files/lab/ 본 실측은 "독자가 files/lab/ 만 받아 처음부터 재현 가능한가"를 검증한다(=T92 본질). ================================================================================ [0] 기존 잔재 철거 — 번들 cleanup.sh (이전 실측 잔재가 dns-lab 에 살아있는 상태에서 시작) -------------------------------------------------------------------------------- $ bash files/lab/cleanup.sh == [1] SE/VS/DR 삭제 (dns-lab, ns 삭제 전에 트래픽 라우팅 먼저 회수) == serviceentry.networking.istio.io "gslb-strict" deleted from dns-lab namespace virtualservice.networking.istio.io "gslb-80-to-443" deleted from dns-lab namespace destinationrule.networking.istio.io "gslb-tls-origination" deleted from dns-lab namespace == [2] namespace 삭제 == namespace "dns-lab" deleted == [3] 확인 == ns/dns-lab NotFound — 정리 완료. # 소요 ~37s. (cleanup.sh 를 "랩이 살아있는 중" 실행해도 정상 동작 = mid-lab 멱등성도 확인) [1] 번들 setup.sh — fresh build (context 자동 감지) -------------------------------------------------------------------------------- $ bash files/lab/setup.sh == [0] preflight (context=homelab) == == [1] server cert(self-signed, SAN=gslb.lab.internal) == == [2] namespace + secret == namespace/dns-lab created secret/gslb-tls created == [3] lab-dns (cluster DNS ClusterIP 치환) == cluster DNS = 10.250.0.3 configmap/coredns-corefile created deployment.apps/lab-dns created service/lab-dns created deployment "lab-dns" successfully rolled out == [4] backends == deployment "backend-a" successfully rolled out deployment "backend-b" successfully rolled out == [4b] GSLB 초기값 = backend-a == backend-a=10.250.4.141 backend-b=10.250.196.43 (gslb.lab.internal -> 10.250.4.141) == [5] client (lab-dns ClusterIP + cluster domain 치환) == lab-dns = 10.250.47.57 · cluster domain = homelab.local <== 자동 발견(하드코딩 아님) deployment "fortio" successfully rolled out deployment "netshoot" successfully rolled out == ready == # ★ 핵심: cluster domain 이 coredns Corefile 에서 homelab.local 로 자동 발견·치환됐고, # fortio/netshoot sidecar 가 문제없이 rollout 됨 = §7 "cluster domain 하드코딩 트랩"을 # 번들 킷이 처음부터 회피. 2026-07-01 첫 실측에서 밟았던 sidecar 미기동(15021 probe # connection refused)이 이번엔 재발하지 않음. $ kubectl -n dns-lab get pods -o wide backend-a-c7987cbc8-h2z7w 1/1 Running 10.255.194.86 k8s-worker1 backend-b-66c75d5fc6-mkslh 1/1 Running 10.255.126.39 k8s-worker2 fortio-5658d6cf66-hn7kq 2/2 Running 10.255.194.97 k8s-worker1 lab-dns-5747479844-h2l6z 2/2 Running 10.255.194.110 k8s-worker1 netshoot-8466947d49-lwlhr 2/2 Running 10.255.126.40 k8s-worker2 $ kubectl -n dns-lab exec deploy/netshoot -- dig +short gslb.lab.internal 10.250.4.141 # = backend-a ClusterIP (GSLB 초기값) # 참고: setup 직후 SE/VS/DR 미적용 상태에서 http://gslb.lab.internal/ 호출은 503 # (평문 80 -> backend 는 443 TLS 만 listen). 정상 — flip-test.sh 가 SE+VS+DR 을 apply. [2] flip-test strict mode1 — STRICT_DNS: flip 시 기존 커넥션 drain (세션 끊김) -------------------------------------------------------------------------------- 적용: SE=gslb-strict(resolution:DNS), VS=80to443, DR=tls-origination FLIP A->B @ 21:04:12 (host KST) · fortio -c2 -qps8 -t40s -keepalive · netshoot who= 루프 40s ENDPOINT 덤프: 3a(pre-flip) 10.250.4.141(backend-a) -> 3b(post-flip) 10.250.196.43(backend-b) [교체] cluster|443 cx 카운터 (3a MID-PRE-FLIP -> 3b MID-POST-FLIP = 델타): membership_change 1 -> 2 = +1 upstream_cx_active 2 -> 2 = 0 upstream_cx_destroy 0 -> 2 = +2 upstream_cx_destroy_local 0 -> 2 = +2 (Envoy 능동 drain) upstream_cx_destroy_with_active_rq 0 -> 0 = 0 upstream_cx_total 2 -> 4 = +2 (backend-b 로 신규 재연결) endpoint|443 cx: 3a 10.250.4.141::cx_active=2 -> 3b 10.250.196.43::cx_active=2 who= 타임라인(발췌, 컨테이너 UTC = host KST-9): 12:04:04..12:04:14 who=backend-a (flip 은 host 21:04:12 = 컨테이너 12:04:12) 12:04:15 who=backend-b <== flip 후 ~3s 에 전환 12:04:16..12:04:43 who=backend-b fortio(keepalive): Sockets used: 2 · Code 200 : 320 (100.0 %) · 8.0 qps 판정: STRICT_DNS = flip 에 사라진 host(backend-a) 커넥션을 destroy_local 로 능동 drain, backend-b 로 재연결. membership_change +1, cx_destroy +2 = 2026-07-01 기대치와 일치. [3] flip-test logical mode1 — LOGICAL_DNS: flip 시 기존 세션 보존 -------------------------------------------------------------------------------- 적용: SE=gslb-logical(resolution:DNS_ROUND_ROBIN), VS/DR 동일 FLIP A->B @ 21:05:21 · fortio -c2 -qps8 -t40s -keepalive · who= 루프 40s ※ cx 카운터는 Envoy 기동 이래 누적값 — 판정은 "이 run 내 3a->3b 델타"로 본다(직전 strict run 잔재로 절대값은 3/4 에서 시작). ENDPOINT 덤프: 3a 10.250.4.141(backend-a) -> 3b 10.250.196.43(backend-b) [라벨만 갱신] cluster|443 cx 카운터 (3a -> 3b = 델타): membership_change 3 -> 3 = 0 upstream_cx_active 2 -> 2 = 0 upstream_cx_destroy 4 -> 4 = 0 (기존 세션 보존) upstream_cx_destroy_local 4 -> 4 = 0 upstream_cx_total 6 -> 6 = 0 (신규 재연결 없음) endpoint|443 cx: 3a 10.250.4.141::cx_active=2 -> 3b 10.250.196.43::cx_active=2 who= 타임라인(발췌): 12:05:13 .. 12:05:52 who=backend-a (40초 내내 유지, backend-b 로 안 감) fortio(keepalive): Sockets used: 2 · Code 200 : 320 (100.0 %) · 8.0 qps 판정: LOGICAL_DNS = flip 에 델타 전부 0(세션 보존), who=backend-a 40초 유지. ★ §7 관측 함정 재현: endpoint 덤프/per-endpoint cx 는 backend-b(새 resolved IP, 10.250.196.43)로 라벨을 갱신했지만, 실제 응답 body(who=)는 40초 내내 backend-a. "덤프는 새 IP, 바이트는 옛 IP" — 관측 도구만 믿으면 오판. 2026-07-01 과 동일 재현. [4] flip-test logical mode3 — LOGICAL 트레이드오프: flip 엔 유지, kill 순간 재연결 -------------------------------------------------------------------------------- 적용: SE=gslb-logical · fortio -c1 -qps5 -t60s -keepalive · who= 루프 60s 타임라인: FLIP A->B @ 21:06:36 · KILL backend-a @ 21:06:56 (flip +20s) who= 타임라인(발췌, 컨테이너 UTC): 12:06:26 .. 12:06:36 who=backend-a (load 시작) >> FLIP A->B @ 12:06:36 12:06:36 .. 12:06:56 who=backend-a <== flip 후에도 20초간 backend-a 유지 (세션 pin) >> KILL backend-a @ 12:06:56 12:06:56 who=backend-b <== kill 즉시(같은 초) backend-b 로 전환 12:06:57 .. 12:07:25 who=backend-b 전 구간 code=200 — code!=200 = 0건 (무손실) cluster|443 cx 카운터 (BEFORE run -> AFTER run): upstream_cx_destroy 4 -> 6 = +2 upstream_cx_destroy_local 4 -> 4 = 0 upstream_cx_destroy_remote 0 -> 2 = +2 (kill 로 backend-a 가 원격 종료) upstream_cx_destroy_with_active_rq 0 -> 0 = 0 (활성 요청 중 절단 없음) upstream_cx_total 6 -> 7 = +1 (backend-b 로 1회 재연결) endpoint|443 cx AFTER: 10.250.196.43::cx_active=1 cx_total=3 health_flags=healthy fortio(keepalive): Sockets used: 1 · Code 200 : 300 (100.0 %) 판정: flip 만으로는 backend-a 에 pin 유지(LOGICAL) -> backend-a KILL 순간에야 backend-b 로 재연결. 재연결 blip 없음, destroy_with_active_rq=0(요청이 1ms 로 짧아 커넥션 사망을 요청 '사이'에 감지). 추가 destroy 는 destroy_remote(+2) = 죽은 backend-a 쪽이 커넥션을 끊음. 2026-07-01 과 일치. [5] 번들 cleanup.sh — 철거 -------------------------------------------------------------------------------- $ bash files/lab/cleanup.sh == [1] SE/VS/DR 삭제 == serviceentry.networking.istio.io "gslb-logical" deleted from dns-lab namespace virtualservice.networking.istio.io "gslb-80-to-443" deleted from dns-lab namespace destinationrule.networking.istio.io "gslb-tls-origination" deleted from dns-lab namespace == [2] namespace 삭제 == namespace "dns-lab" deleted == [3] 확인 == ns/dns-lab NotFound — 정리 완료. # 소요 ~37s. [6] NotFound 재확인 + 멱등 재실행 -------------------------------------------------------------------------------- $ kubectl get ns dns-lab Error from server (NotFound): namespaces "dns-lab" not found $ bash files/lab/cleanup.sh # 이미 없는 상태에서 재실행 == [1] SE/VS/DR 삭제 == # (출력 없음 = --ignore-not-found 로 조용히 통과) == [2] namespace 삭제 == == [3] 확인 == ns/dns-lab NotFound — 정리 완료. # exit 0, 에러 없음 = 멱등성 확인 ================================================================================ 종합: 번들 킷(files/lab/) 만으로 setup -> strict mode1 -> logical mode1 -> logical mode3 -> cleanup 전체 생애주기를 라이브로 재현 완료. 3모드 실측값 모두 2026-07-01 기대치와 일치(편차: STRICT who 전환 +3s vs 원 +5s — DNS ttl5s+reload2s 창 내 정상 변동). Mode1 대조표 (이 run 내 3a->3b 델타): 지표 STRICT_DNS LOGICAL_DNS membership_change +1 0 upstream_cx_destroy +2 (local) 0 upstream_cx_total +2 0 endpoint 덤프 A->B 교체 A->B 라벨만(body 는 A) who=(응답 backend) flip +3s 에 B 40초 내내 A fortio Sockets2 100% Sockets2 100% 미검증/범위 밖: - SE.resolution -> STRICT_DNS/LOGICAL_DNS "type" 필드는 이번 run 에서 istioctl 로 재-grep 하지 않음(cluster 철거됨). 단 거동(STRICT=endpoint 교체+membership_change / LOGICAL=논리 1개+델타 0)이 type 을 간접 입증하며, 이 매핑(C3)은 문헌 + T49 실측으로 이미 확인됨. - mode2(죽은 IP 유실+outlier/retry 복구)는 T92 범위 밖(44/45 매니페스트 미적용). - LOGICAL 런타임 다중-IP CDS NACK(§7/C4)은 이 실측으로 확정 불가(단일 IP flip 만 수행). ================================================================================