# 2026-07-09 dns-logical-multia — LOGICAL_DNS + 복수 A레코드 실측 검증 대상: 문서 주장 "LOGICAL_DNS SE host에 복수 A레코드 오면 CDS NACK" vs Envoy 공식 문서 "logical DNS cluster only uses the first IP". ## 0. 사전 상태 ``` Thu Jul 9 09:13:26 PM KST 2026 --- pods --- NAME READY STATUS RESTARTS AGE backend-a-b69577fc-kmbvp 1/1 Running 0 7h38m backend-b-69bd4df99b-jlnpn 1/1 Running 0 8h fortio-6bbcfcc5ff-jzfht 2/2 Running 0 8h lab-dns-666f84996b-64d88 2/2 Running 0 8h netshoot-76c869b576-h4fqh 2/2 Running 0 8h --- SE --- NAME HOSTS LOCATION RESOLUTION AGE gslb-passthrough ["gslb-pt.lab.internal"] MESH_EXTERNAL DNS 6h34m gslb-strict ["gslb.lab.internal"] MESH_EXTERNAL DNS 8h --- backend svc ClusterIP --- NAME CLUSTERIP backend-a 10.250.161.145 backend-b 10.250.183.248 --- backend pod IP (참고용, 이번 실험은 svc ClusterIP 사용) --- NAME IP backend-a-b69577fc-kmbvp 10.255.194.124 backend-b-69bd4df99b-jlnpn 10.255.126.38 --- current /hosts/addn --- 10.250.161.145 gslb.lab.internal 10.255.194.124 gslb-pt.lab.internal ``` ## 1. SE를 41(logical)로 전환 ``` $ kubectl --context=homelab -n dns-lab delete serviceentry gslb-strict serviceentry.networking.istio.io "gslb-strict" deleted from dns-lab namespace $ kubectl --context=homelab apply -f scenarios/50-dns-resolution/41-serviceentry-logical.yaml serviceentry.networking.istio.io/gslb-logical created $ kubectl --context=homelab -n dns-lab get serviceentry NAME HOSTS LOCATION RESOLUTION AGE gslb-logical ["gslb.lab.internal"] MESH_EXTERNAL DNS_ROUND_ROBIN 4s gslb-passthrough ["gslb-pt.lab.internal"] MESH_EXTERNAL DNS 6h34m ``` ## 2. LOGICAL_DNS cluster type 확인 + 단일 A레코드 baseline curl ``` $ istioctl proxy-config cluster deploy/fortio.dns-lab --fqdn gslb.lab.internal SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE gslb.lab.internal 80 - outbound LOGICAL_DNS gslb-tls-origination.dns-lab gslb.lab.internal 443 - outbound LOGICAL_DNS gslb-tls-origination.dns-lab $ istioctl proxy-status (요약) NAME CLUSTER ISTIOD VERSION SUBSCRIBED TYPES $ dig +short gslb.lab.internal (netshoot) 10.250.161.145 $ baseline curl x3 (fortio sidecar 경유, http origination) backend-a [http_code=200] backend-a [http_code=200] backend-a [http_code=200] ``` ## 3. lab-dns에 gslb.lab.internal 2-A레코드 주입 ``` IP_A(backend-a svc)=10.250.161.145 IP_B(backend-b svc)=10.250.183.248 $ printf 'IP_A gslb.lab.internal IP_B gslb.lab.internal IP_A gslb-pt.lab.internal ' > /hosts/addn $ cat /hosts/addn 10.250.161.145 gslb.lab.internal 10.250.183.248 gslb.lab.internal 10.250.161.145 gslb-pt.lab.internal ``` ## 3-1. dig 확인: gslb.lab.internal이 실제로 2개 IP를 반환하는가 ``` -- dig 시도 1 -- gslb.lab.internal. 4 IN A 10.250.161.145 gslb.lab.internal. 4 IN A 10.250.183.248 -- dig 시도 2 -- gslb.lab.internal. 4 IN A 10.250.161.145 gslb.lab.internal. 4 IN A 10.250.183.248 -- dig 시도 3 -- gslb.lab.internal. 4 IN A 10.250.161.145 gslb.lab.internal. 4 IN A 10.250.183.248 -- dig 시도 4 -- gslb.lab.internal. 4 IN A 10.250.161.145 gslb.lab.internal. 4 IN A 10.250.183.248 -- dig 시도 5 -- gslb.lab.internal. 3 IN A 10.250.161.145 gslb.lab.internal. 3 IN A 10.250.183.248 ``` ## 4. 35초+ 경과 후 관측 (LOGICAL_DNS + 2 A레코드 상태) ### 4-1. istioctl proxy-status ``` NAME CLUSTER ISTIOD VERSION SUBSCRIBED TYPES fortio-6bbcfcc5ff-jzfht.dns-lab Kubernetes istiod-858c784d94-98fhc 1.30.0 4 (CDS,LDS,EDS,RDS) netshoot-76c869b576-h4fqh.dns-lab Kubernetes istiod-858c784d94-98fhc 1.30.0 4 (CDS,LDS,EDS,RDS) ``` ### 4-2. istiod 로그 (최근 5분, nack/reject/error 검색) ``` (매치 없음) ``` ### 4-3. Envoy update 카운터 (⚠ 파드 기동 후 누적치 — 이 시점을 새 베이스라인 T_base로 스냅샷, 이후 델타로 판정) ``` $ fortio sidecar cds/lds counters @ T_base cluster_manager.cds.update_failure: 34 cluster_manager.cds.update_rejected: 0 cluster_manager.cds.update_success: 43 listener_manager.lds.update_failure: 34 listener_manager.lds.update_rejected: 0 listener_manager.lds.update_success: 43 $ netshoot sidecar cds/lds counters @ T_base cluster_manager.cds.update_failure: 34 cluster_manager.cds.update_rejected: 0 cluster_manager.cds.update_success: 43 listener_manager.lds.update_failure: 34 listener_manager.lds.update_rejected: 0 listener_manager.lds.update_success: 43 ``` ## 5. 트래픽 테스트 (2-A레코드 상태, LOGICAL_DNS) — curl 12회 ``` req#1: backend-a [http_code=200] req#2: backend-a [http_code=200] req#3: backend-a [http_code=200] req#4: backend-a [http_code=200] req#5: backend-a [http_code=200] req#6: backend-a [http_code=200] req#7: backend-a [http_code=200] req#8: backend-a [http_code=200] req#9: backend-a [http_code=200] req#10: backend-a [http_code=200] req#11: backend-a [http_code=200] req#12: backend-a [http_code=200] ``` ## 6. LOGICAL cluster 내부 상태 + 카운터 델타 재확인 ``` $ netshoot sidecar: pilot-agent request GET clusters | grep gslb outbound|443||gslb-pt.lab.internal::observability_name::outbound|443||gslb-pt.lab.internal; outbound|443||gslb-pt.lab.internal::default_priority::max_connections::4294967295 outbound|443||gslb-pt.lab.internal::default_priority::max_pending_requests::4294967295 outbound|443||gslb-pt.lab.internal::default_priority::max_requests::4294967295 outbound|443||gslb-pt.lab.internal::default_priority::max_retries::4294967295 outbound|443||gslb-pt.lab.internal::high_priority::max_connections::1024 outbound|443||gslb-pt.lab.internal::high_priority::max_pending_requests::1024 outbound|443||gslb-pt.lab.internal::high_priority::max_requests::1024 outbound|443||gslb-pt.lab.internal::high_priority::max_retries::3 outbound|443||gslb-pt.lab.internal::added_via_api::true outbound|443||gslb-pt.lab.internal::10.250.161.145:443::cx_active::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::cx_connect_fail::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::cx_total::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_active::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_error::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_success::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_timeout::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_total::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::hostname::gslb-pt.lab.internal outbound|443||gslb-pt.lab.internal::10.250.161.145:443::health_flags::healthy outbound|443||gslb-pt.lab.internal::10.250.161.145:443::weight::1 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::region:: outbound|443||gslb-pt.lab.internal::10.250.161.145:443::zone:: outbound|443||gslb-pt.lab.internal::10.250.161.145:443::sub_zone:: outbound|443||gslb-pt.lab.internal::10.250.161.145:443::canary::false outbound|443||gslb-pt.lab.internal::10.250.161.145:443::priority::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::success_rate::-1 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::local_origin_success_rate::-1 outbound|443||gslb.lab.internal::observability_name::outbound|443||gslb.lab.internal; outbound|443||gslb.lab.internal::default_priority::max_connections::4294967295 outbound|443||gslb.lab.internal::default_priority::max_pending_requests::4294967295 outbound|443||gslb.lab.internal::default_priority::max_requests::4294967295 outbound|443||gslb.lab.internal::default_priority::max_retries::4294967295 outbound|443||gslb.lab.internal::high_priority::max_connections::1024 outbound|443||gslb.lab.internal::high_priority::max_pending_requests::1024 outbound|443||gslb.lab.internal::high_priority::max_requests::1024 outbound|443||gslb.lab.internal::high_priority::max_retries::3 outbound|443||gslb.lab.internal::added_via_api::true outbound|443||gslb.lab.internal::10.250.161.145:443::cx_active::0 outbound|443||gslb.lab.internal::10.250.161.145:443::cx_connect_fail::0 outbound|443||gslb.lab.internal::10.250.161.145:443::cx_total::3 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_active::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_error::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_success::15 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_timeout::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_total::15 outbound|443||gslb.lab.internal::10.250.161.145:443::hostname::gslb.lab.internal outbound|443||gslb.lab.internal::10.250.161.145:443::health_flags::healthy outbound|443||gslb.lab.internal::10.250.161.145:443::weight::1 outbound|443||gslb.lab.internal::10.250.161.145:443::region:: outbound|443||gslb.lab.internal::10.250.161.145:443::zone:: outbound|443||gslb.lab.internal::10.250.161.145:443::sub_zone:: outbound|443||gslb.lab.internal::10.250.161.145:443::canary::false outbound|443||gslb.lab.internal::10.250.161.145:443::priority::0 outbound|443||gslb.lab.internal::10.250.161.145:443::success_rate::-1 outbound|443||gslb.lab.internal::10.250.161.145:443::local_origin_success_rate::-1 outbound|80||gslb.lab.internal::observability_name::outbound|80||gslb.lab.internal; outbound|80||gslb.lab.internal::default_priority::max_connections::4294967295 outbound|80||gslb.lab.internal::default_priority::max_pending_requests::4294967295 outbound|80||gslb.lab.internal::default_priority::max_requests::4294967295 outbound|80||gslb.lab.internal::default_priority::max_retries::4294967295 outbound|80||gslb.lab.internal::high_priority::max_connections::1024 outbound|80||gslb.lab.internal::high_priority::max_pending_requests::1024 outbound|80||gslb.lab.internal::high_priority::max_requests::1024 outbound|80||gslb.lab.internal::high_priority::max_retries::3 outbound|80||gslb.lab.internal::added_via_api::true outbound|80||gslb.lab.internal::10.250.161.145:80::cx_active::0 outbound|80||gslb.lab.internal::10.250.161.145:80::cx_connect_fail::0 outbound|80||gslb.lab.internal::10.250.161.145:80::cx_total::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_active::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_error::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_success::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_timeout::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_total::0 outbound|80||gslb.lab.internal::10.250.161.145:80::hostname::gslb.lab.internal outbound|80||gslb.lab.internal::10.250.161.145:80::health_flags::healthy outbound|80||gslb.lab.internal::10.250.161.145:80::weight::1 outbound|80||gslb.lab.internal::10.250.161.145:80::region:: outbound|80||gslb.lab.internal::10.250.161.145:80::zone:: outbound|80||gslb.lab.internal::10.250.161.145:80::sub_zone:: outbound|80||gslb.lab.internal::10.250.161.145:80::canary::false outbound|80||gslb.lab.internal::10.250.161.145:80::priority::0 outbound|80||gslb.lab.internal::10.250.161.145:80::success_rate::-1 outbound|80||gslb.lab.internal::10.250.161.145:80::local_origin_success_rate::-1 $ fortio sidecar: pilot-agent request GET clusters | grep gslb outbound|443||gslb.lab.internal::observability_name::outbound|443||gslb.lab.internal; outbound|443||gslb.lab.internal::default_priority::max_connections::4294967295 outbound|443||gslb.lab.internal::default_priority::max_pending_requests::4294967295 outbound|443||gslb.lab.internal::default_priority::max_requests::4294967295 outbound|443||gslb.lab.internal::default_priority::max_retries::4294967295 outbound|443||gslb.lab.internal::high_priority::max_connections::1024 outbound|443||gslb.lab.internal::high_priority::max_pending_requests::1024 outbound|443||gslb.lab.internal::high_priority::max_requests::1024 outbound|443||gslb.lab.internal::high_priority::max_retries::3 outbound|443||gslb.lab.internal::added_via_api::true outbound|443||gslb.lab.internal::10.250.161.145:443::cx_active::0 outbound|443||gslb.lab.internal::10.250.161.145:443::cx_connect_fail::0 outbound|443||gslb.lab.internal::10.250.161.145:443::cx_total::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_active::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_error::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_success::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_timeout::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_total::0 outbound|443||gslb.lab.internal::10.250.161.145:443::hostname::gslb.lab.internal outbound|443||gslb.lab.internal::10.250.161.145:443::health_flags::healthy outbound|443||gslb.lab.internal::10.250.161.145:443::weight::1 outbound|443||gslb.lab.internal::10.250.161.145:443::region:: outbound|443||gslb.lab.internal::10.250.161.145:443::zone:: outbound|443||gslb.lab.internal::10.250.161.145:443::sub_zone:: outbound|443||gslb.lab.internal::10.250.161.145:443::canary::false outbound|443||gslb.lab.internal::10.250.161.145:443::priority::0 outbound|443||gslb.lab.internal::10.250.161.145:443::success_rate::-1 outbound|443||gslb.lab.internal::10.250.161.145:443::local_origin_success_rate::-1 outbound|443||gslb-pt.lab.internal::observability_name::outbound|443||gslb-pt.lab.internal; outbound|443||gslb-pt.lab.internal::default_priority::max_connections::4294967295 outbound|443||gslb-pt.lab.internal::default_priority::max_pending_requests::4294967295 outbound|443||gslb-pt.lab.internal::default_priority::max_requests::4294967295 outbound|443||gslb-pt.lab.internal::default_priority::max_retries::4294967295 outbound|443||gslb-pt.lab.internal::high_priority::max_connections::1024 outbound|443||gslb-pt.lab.internal::high_priority::max_pending_requests::1024 outbound|443||gslb-pt.lab.internal::high_priority::max_requests::1024 outbound|443||gslb-pt.lab.internal::high_priority::max_retries::3 outbound|443||gslb-pt.lab.internal::added_via_api::true outbound|443||gslb-pt.lab.internal::10.250.161.145:443::cx_active::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::cx_connect_fail::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::cx_total::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_active::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_error::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_success::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_timeout::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::rq_total::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::hostname::gslb-pt.lab.internal outbound|443||gslb-pt.lab.internal::10.250.161.145:443::health_flags::healthy outbound|443||gslb-pt.lab.internal::10.250.161.145:443::weight::1 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::region:: outbound|443||gslb-pt.lab.internal::10.250.161.145:443::zone:: outbound|443||gslb-pt.lab.internal::10.250.161.145:443::sub_zone:: outbound|443||gslb-pt.lab.internal::10.250.161.145:443::canary::false outbound|443||gslb-pt.lab.internal::10.250.161.145:443::priority::0 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::success_rate::-1 outbound|443||gslb-pt.lab.internal::10.250.161.145:443::local_origin_success_rate::-1 outbound|80||gslb.lab.internal::observability_name::outbound|80||gslb.lab.internal; outbound|80||gslb.lab.internal::default_priority::max_connections::4294967295 outbound|80||gslb.lab.internal::default_priority::max_pending_requests::4294967295 outbound|80||gslb.lab.internal::default_priority::max_requests::4294967295 outbound|80||gslb.lab.internal::default_priority::max_retries::4294967295 outbound|80||gslb.lab.internal::high_priority::max_connections::1024 outbound|80||gslb.lab.internal::high_priority::max_pending_requests::1024 outbound|80||gslb.lab.internal::high_priority::max_requests::1024 outbound|80||gslb.lab.internal::high_priority::max_retries::3 outbound|80||gslb.lab.internal::added_via_api::true outbound|80||gslb.lab.internal::10.250.161.145:80::cx_active::0 outbound|80||gslb.lab.internal::10.250.161.145:80::cx_connect_fail::0 outbound|80||gslb.lab.internal::10.250.161.145:80::cx_total::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_active::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_error::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_success::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_timeout::0 outbound|80||gslb.lab.internal::10.250.161.145:80::rq_total::0 outbound|80||gslb.lab.internal::10.250.161.145:80::hostname::gslb.lab.internal outbound|80||gslb.lab.internal::10.250.161.145:80::health_flags::healthy outbound|80||gslb.lab.internal::10.250.161.145:80::weight::1 outbound|80||gslb.lab.internal::10.250.161.145:80::region:: outbound|80||gslb.lab.internal::10.250.161.145:80::zone:: outbound|80||gslb.lab.internal::10.250.161.145:80::sub_zone:: outbound|80||gslb.lab.internal::10.250.161.145:80::canary::false outbound|80||gslb.lab.internal::10.250.161.145:80::priority::0 outbound|80||gslb.lab.internal::10.250.161.145:80::success_rate::-1 outbound|80||gslb.lab.internal::10.250.161.145:80::local_origin_success_rate::-1 $ istioctl proxy-config endpoints (LOGICAL_DNS는 EDS 미사용 — 참고용, 비어있을 것으로 예상) ENDPOINT STATUS OUTLIER CHECK CLUSTER 10.250.161.145:443 HEALTHY OK outbound|443||gslb.lab.internal $ cds/lds 카운터 델타 (T_base 대비 — 여전히 0이면 트래픽 12회+DNS refresh 중 NACK 없음) -- fortio -- cluster_manager.cds.update_failure: 34 cluster_manager.cds.update_rejected: 0 cluster_manager.cds.update_success: 43 listener_manager.lds.update_failure: 34 listener_manager.lds.update_rejected: 0 listener_manager.lds.update_success: 43 -- netshoot -- cluster_manager.cds.update_failure: 34 cluster_manager.cds.update_rejected: 0 cluster_manager.cds.update_success: 43 listener_manager.lds.update_failure: 34 listener_manager.lds.update_rejected: 0 listener_manager.lds.update_success: 43 ``` ## 7. 대조군: SE를 40(strict)으로 전환, 동일 2-A레코드 상태에서 확인 ``` $ kubectl delete serviceentry gslb-logical; kubectl apply -f 40-serviceentry-strict.yaml serviceentry.networking.istio.io "gslb-logical" deleted from dns-lab namespace serviceentry.networking.istio.io/gslb-strict created NAME HOSTS LOCATION RESOLUTION AGE gslb-passthrough ["gslb-pt.lab.internal"] MESH_EXTERNAL DNS 15h gslb-strict ["gslb.lab.internal"] MESH_EXTERNAL DNS 3s $ cluster type 확인 SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE gslb.lab.internal 80 - outbound STRICT_DNS gslb-tls-origination.dns-lab gslb.lab.internal 443 - outbound STRICT_DNS gslb-tls-origination.dns-lab ``` ## 8. STRICT_DNS 15초 경과 후 cluster 멤버 + 트래픽 분산 확인 ``` $ pilot-agent request GET clusters | grep gslb.lab.internal (netshoot) outbound|443||gslb.lab.internal::10.250.161.145:443::cx_total::0 outbound|443||gslb.lab.internal::10.250.161.145:443::rq_total::0 outbound|443||gslb.lab.internal::10.250.161.145:443::hostname::gslb.lab.internal outbound|443||gslb.lab.internal::10.250.183.248:443::cx_total::0 outbound|443||gslb.lab.internal::10.250.183.248:443::rq_total::0 outbound|443||gslb.lab.internal::10.250.183.248:443::hostname::gslb.lab.internal $ curl x12 (STRICT_DNS, 2-A레코드 — 분산 기대) req#1: backend-a [http_code=200] req#2: backend-a [http_code=200] req#3: backend-a [http_code=200] req#4: backend-b [http_code=200] req#5: backend-b [http_code=200] req#6: backend-a [http_code=200] req#7: backend-b [http_code=200] req#8: backend-a [http_code=200] req#9: backend-a [http_code=200] req#10: backend-a [http_code=200] req#11: backend-b [http_code=200] req#12: backend-b [http_code=200] ``` ## 9. 실측 B — SE에 endpoints 2개 명시 + DNS_ROUND_ROBIN ``` $ kubectl delete serviceentry gslb-strict (host 충돌 회피 — 51과 같은 host) serviceentry.networking.istio.io "gslb-strict" deleted from dns-lab namespace $ dry-run=server 로 51 적용 시도 ``` ## 10. 종료 상태 복원 ``` IP_A_SVC=10.250.161.145 POD_A(gslb-pt target)=10.255.194.124 $ /hosts/addn 을 원복 (gslb.lab.internal=단일 backend-a svc IP, gslb-pt.lab.internal=backend-a pod IP 유지) 10.250.161.145 gslb.lab.internal 10.255.194.124 gslb-pt.lab.internal $ dig 확인 (단일 A 복원됐는가) 10.250.161.145 10.255.194.124 $ SE를 40(strict)으로 재적용 serviceentry.networking.istio.io/gslb-strict created NAME HOSTS LOCATION RESOLUTION AGE gslb-passthrough ["gslb-pt.lab.internal"] MESH_EXTERNAL DNS 15h gslb-strict ["gslb.lab.internal"] MESH_EXTERNAL DNS 3s $ 51 SE가 클러스터에 존재하지 않음 확인 (dry-run만 했으므로 애초에 생성 안 됨) Error from server (NotFound): serviceentries.networking.istio.io "gslb-logical-2ep" not found ``` ## 11. 최종 sanity 확인 ``` $ cluster type (STRICT_DNS 확인) SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE gslb.lab.internal 80 - outbound STRICT_DNS gslb-tls-origination.dns-lab gslb.lab.internal 443 - outbound STRICT_DNS gslb-tls-origination.dns-lab $ 전 파드 READY NAME READY STATUS RESTARTS AGE backend-a-b69577fc-kmbvp 1/1 Running 0 16h backend-b-69bd4df99b-jlnpn 1/1 Running 0 17h fortio-6bbcfcc5ff-jzfht 2/2 Running 0 17h lab-dns-666f84996b-64d88 2/2 Running 0 17h netshoot-76c869b576-h4fqh 2/2 Running 0 17h $ sanity curl — HTTP 경로 (gslb.lab.internal, TLS origination) backend-a [http_code=200] $ sanity curl — passthrough 경로 (gslb-pt.lab.internal, SNI passthrough) backend-a [http_code=200] ``` ## 12. 결론 ### 실측 A (런타임 — DNS가 실제로 2개 IP를 반환) - `gslb.lab.internal`이 A레코드 2개(backend-a, backend-b 순서로 dig 응답 고정)를 반환하는 상태에서 LOGICAL_DNS(SE resolution: DNS_ROUND_ROBIN)는 **NACK 없이 정상 동작**했다. - `pilot-agent request GET clusters | grep gslb.lab.internal` 에 잡힌 멤버는 오직 `10.250.161.145:443`(backend-a, dig 응답의 첫 IP) 하나뿐 — `10.250.183.248`(backend-b)은 cluster 멤버 목록에 아예 나타나지 않았다. - curl 15회(baseline 3 + 본 실험 12) 전부 `backend-a`, `http_code=200`. backend-b 응답은 단 한 번도 관측되지 않음. - cds/lds update_rejected 카운터는 T_base(2-레코드 주입 후 스냅샷) 대비 **델타 0** — DNS refresh 주기(TTL 5s, 35초 이상 경과, dig 7회 연속 2-IP 응답 확인)를 지나는 동안 NACK/STALE 징후 전무. istiod 로그에도 nack/reject/error 매치 없음. - 결론: Envoy 공식 문서("a logical DNS cluster only uses the first IP address returned")가 **맞다**. 기존 문서(41-serviceentry-logical.yaml 주석, README §5)의 "A record가 multi-IP면 CDS NACK" 주장은 **이 시나리오(실제 DNS 응답의 복수 A레코드)에서는 실측으로 반증됨**. ### STRICT 대조 - 동일 2-레코드 상태에서 SE를 40(strict, resolution: DNS)으로 바꾸면 cluster 멤버에 `10.250.161.145:443`, `10.250.183.248:443` **둘 다** 잡힘. - curl 12회 결과 backend-a 7회, backend-b 5회로 **실제 분산** 확인(a,a,a,b,b,a,b,a,a,a,b,b). - STRICT_DNS는 A record 전체를 endpoint로 펼쳐 LB한다는 기존 이해와 일치. ### 실측 B (설정 레벨 — SE.endpoints 2개 명시 + DNS_ROUND_ROBIN) - `kubectl apply --dry-run=server` 단계에서 **즉시 거부**: `The ServiceEntry "gslb-logical-2ep" is invalid: spec: Invalid value: "object": DNS_ROUND_ROBIN mode cannot have multiple endpoints` - 이건 istiod validating webhook의 **admission-level 거부**다. CDS/xDS까지 가지도 않고, Envoy가 NACK할 기회조차 없이 API 서버 단에서 원천 차단됨. - 즉 "LOGICAL_DNS는 논리 endpoint 1개" 제약이 실제로 강제되는 지점은 **여기(SE.spec.endpoints를 명시적으로 여러 개 선언하는 경우)** 이지, "실제 DNS 조회가 여러 IP를 반환하는 경우"가 아니다. ### 종합 — 무엇이 혼동됐었나 기존 문서의 "multi-IP A record → LOGICAL_DNS NACK"라는 주장은 **메커니즘을 잘못 짚었다.** - 실제 DNS 응답의 다중 A레코드(실측 A) — Envoy가 스스로 c-ares로 질의하고 첫 결과만 쓰는 데이터플레인 동작이라 **xDS 왕복 자체가 없다.** NACK 발생 지점이 없음. - SE에 endpoints를 여러 개 "선언"하는 경우(실측 B)만이 istiod가 이를 그대로 Cluster.load_assignment에 실어 push하려는 시도가 되고, 이 조합은 **CDS 이전, admission webhook에서** 거부된다(Envoy 레벨의 "LOGICAL_DNS는 load-assignment endpoint 1개만 허용" 제약을 istiod가 API 검증으로 선반영한 것으로 보임). - 두 메커니즘 다 "NACK로 통신이 깨진다"는 결과에 이르지 않는다 — A는 정상 동작(첫 IP만 씀), B는 애초에 리소스 생성 자체가 실패(admission reject)해서 "이미 동작 중이던 통신이 깨지는" 상황이 발생할 수 없다. ⚠️ 스코프 밖 — 이 리포트는 `docs/test-reports/`에만 신규 기록했다. 41-serviceentry-logical.yaml의 함정 주석과 README.md §5는 위 실측과 배치되므로 **후속 작업으로 수정 필요**(이번 태스크 범위는 dns-lab 실측 + docs/test-reports 신규 파일로 한정되어 기존 scenario yaml/README 본문은 건드리지 않음).