🏠 목록 Istio CR 멘탈 모델 — CR은 입력, Envoy config가 진실 (출처: LLM 답변 + Istio 1.30 공식 문서) 📄 MD 원본 🌓 테마
istioservice-meshenvoyxdsmental-model

Istio CR 멘탈 모델 — CR은 입력, Envoy config가 진실 (출처: LLM 답변 + Istio 1.30 공식 문서)

ℹ 이 문서의 목표

Istio CR을 외우는 가장 빠른 길은 각 CR을 따로 암기하는 게 아니라, "CR을 만들면 Envoy 설정의 어느 칸이 채워지는가" 를 하나의 데이터 흐름으로 이해하는 것. 이 문서는 그 한 축으로 모든 CR을 꿰어, CR을 보면 Envoy에 무엇이 박힐지 자연스럽게 떠오르게 만드는 게 목표.


01. 배경 — 왜 CR을 외워도 동작이 안 보이나

Istio를 처음 다룰 때의 좌절은 거의 항상 같은 모양이다. VirtualService·DestinationRule·Gateway·PeerAuthentication을 문서대로 썼는데, "이게 정확히 무슨 일을 일으키는지" 가 머릿속에 그려지지 않는다. 그래서 안 되면 디버깅할 방향이 없고, CR마다 필드를 따로 외우다 양에 깔린다.

근본 원인은 한 가지다. 데이터 플레인을 실제로 굴리는 건 Istio가 아니라 Envoy인데, Envoy는 Istio CR이라는 개념 자체를 모른다. Envoy 입장에서 VirtualService도 DestinationRule도 세상에 존재하지 않는다. Envoy가 아는 건 오직 자기 네이티브 설정 — listener, route, cluster, endpoint, secret뿐이다. 그 사이를 잇는 게 istiod(컨트롤 플레인): CR을 watch하다가 Envoy 네이티브 설정(xDS)으로 번역해서 각 프록시에 gRPC로 push한다.

  사람이 쓰는 의도          번역기            Envoy가 실제로 따르는 것
  (Kubernetes CR)   --->   istiod    --->   (xDS native config)
  VirtualService            watch            route(RDS)
  DestinationRule           translate        cluster(CDS)
  Gateway                   push(gRPC)       listener(LDS)
  ...                                        endpoint(EDS), secret(SDS)

이 구조를 모르면 CR과 동작이 두 개의 분리된 세계로 보인다. 이 구조를 알면 둘이 하나의 데이터 흐름으로 합쳐진다 — 그게 이 문서가 세우려는 멘탈 모델이고, 디버깅·활용·학습이 전부 같은 한 질문으로 통일되는 이유다.


02. 핵심 멘탈 모델 — CR은 입력일 뿐, 진실은 Envoy config에 있다

✓ 한 문장 앵커 (이 그림 하나만 머리에 박아라)

Envoy는 Istio CR을 전혀 모른다. istiod가 CR들을 읽어 Envoy 네이티브 설정(xDS)으로 번역해 각 프록시에 push할 뿐. 그러니 "CR을 어떻게 쓰지?"·"왜 안 되지?"의 답은 항상 "이 CR이 Envoy의 어느 xDS 칸을 채우는가?" 로 환원된다.

이 앵커 하나에서 나머지가 전부 따라 나온다. 디버깅이 통일되는 게 그 첫 결실이다 — 증상이 무엇이든 "어느 칸"을 묻는 질문으로 바뀐다.

CR 이름이 아니라 xDS 칸으로 생각하면, 처음 보는 CR을 만나도 "얘는 어느 칸을 건드리지?"만 물으면 된다. 그래서 학습 순서도 거꾸로 간다 — 결과물(xDS)을 먼저 보고, 그다음 그걸 만드는 입력(CR)을 본다.

YAML에 쓰는 것 (CR)ServiceEntryGatewayVirtualServiceDestinationRulePeerAuth/AuthzistiodCR 읽고 번역→ xDS pushEnvoy가 받는 것 (xDS)LDS — listenerRDS — routeCDS — clusterEDS — endpointSDS — secretwatchpush
그림 1. 사용자는 CR(ServiceEntry/Gateway/VS/DR/PeerAuth)을 작성, istiod가 watch·번역해 xDS(LDS/RDS/CDS/EDS/SDS)로 Envoy에 push. CR과 xDS는 다른 추상화 — 진단은 둘 다 봐야 함.

왼쪽(CR)은 사람이 쓰는 의도, 오른쪽(xDS)은 Envoy가 실제로 따르는 설정. istiod가 둘을 잇는 번역기다.

ℹ 이 문서의 진행 (멘탈 모델 구축 순서)
  1. 먼저 도착지를 본다 — Envoy의 5가지 설정 칸(xDS)이 무엇인지 (섹션 03)
  2. 그다음 다리를 놓는다 — 어떤 CR이 어느 칸을 채우는지 매핑 (섹션 04)
  3. 칸을 채우는 도구를 하나씩 — CR별 기능·설정·실제 반영 (섹션 05)
  4. 흐름으로 통합 — 요청 하나가 5칸을 어떻게 지나는지 (섹션 06)
  5. 직접 확인istioctl proxy-config로 진실을 보는 법 (섹션 07)

03. 도착지 먼저 — Envoy의 5가지 설정 (xDS)

CR을 이해하려면 "CR이 만들어 내는 결과물"부터 알아야 한다. 그 결과물이 xDS다. xDS는 "x Discovery Service"의 묶음으로, Envoy가 동적으로 설정을 받아오는 API 종류들이다. Envoy는 이 설정만 보고 동작하지 Istio CR은 모른다.

5칸을 따로 외우려 하면 안 외워진다. 요청 하나가 Envoy를 통과하는 순서대로 보면 자연스럽게 묶인다: 들어와서(LDS) → 어디로 보낼지 정하고(RDS) → 대상 그룹을 고르고(CDS) → 실제 IP를 찾고(EDS) → 필요하면 인증서를 꺼낸다(SDS). 이 순서가 곧 섹션 06의 요청 흐름이자 디버깅 순서가 된다.

xDS 칸 이름 무엇을 담나 (요청 처리 순서대로)
LDS listener Envoy가 어떤 포트로 받고, 그 포트에 어떤 필터 체인(TLS 종단/통과, HTTP 파싱, RBAC 등)을 걸지. "입구"를 정의
RDS route HTTP 요청을 어느 cluster로 보낼지 규칙(호스트·경로·헤더 매칭, 가중치, 재시도, 타임아웃). "길 안내"
CDS cluster 보낼 대상 그룹(upstream) 정의 + 그 그룹에 대한 정책(로드밸런싱, 커넥션 풀, 서킷 브레이커, upstream TLS). "목적지 묶음"
EDS endpoint 각 cluster의 실제 Pod IP 목록. Pod가 뜨고 죽을 때마다 가장 자주 갱신되는 칸
SDS secret TLS 인증서·키를 동적으로 전달(mTLS 인증서, gateway용 cert). Envoy가 디스크 대신 SDS로 받음
⚠ 전제 교정 — listener와 cluster의 TLS는 다른 TLS다

초보가 가장 헷갈리는 지점이자 이 문서 전체에서 반복될 구분: LDS(listener)의 TLS = "내가 받는(downstream) 연결", CDS(cluster)의 TLS = "내가 맺는(upstream) 연결". 같은 게이트웨이 Pod에서 이 둘은 완전히 독립이라, "들어오는 건 passthrough(listener), 나가는 건 mTLS(cluster)"처럼 따로 설정된다. egress 문서에서 "Gateway server tls.mode와 DestinationRule tls.mode가 다르다"고 한 게 바로 이 LDS/CDS 구분이다.


04. 다리 놓기 — 어떤 CR이 어느 xDS를 채우나

이제 CR과 xDS를 연결한다. 이 표 하나가 이 문서의 척추다.

CR 주로 채우는 xDS 칸 한 줄 의미
Gateway LDS (listener) 게이트웨이가 어떤 포트로 받을지 + 그 포트의 TLS 모드(종단/통과)
VirtualService RDS (route) 매칭된 트래픽을 어느 cluster로 보낼지 규칙
DestinationRule CDS (cluster) 그 대상 그룹의 정책: subset, 로드밸런싱, 커넥션풀, upstream TLS
ServiceEntry CDS + EDS 외부 서비스를 레지스트리에 등록 → cluster + endpoint 생성
PeerAuthentication LDS filter chain 받는 연결에 mTLS 요구(STRICT/PERMISSIVE)를 listener 필터로 주입
AuthorizationPolicy LDS filter chain listener에 RBAC 필터 추가(허용/거부 규칙)
(인증서 일반) SDS (secret) mTLS·gateway 인증서가 SDS로 전달됨(특정 CR이 참조)

표를 외우는 게 아니라 여기서 두 가지 설계 패턴을 읽어내는 게 핵심이다. 이 두 패턴이 "CR이 왜 이렇게 나뉘어 있나"를 설명한다.

  1. "라우팅(VirtualService=RDS)"과 "대상 정책(DestinationRule=CDS)"은 항상 분리돼 있다 — 관심사 분리의 결과다. VirtualService가 "어디로 보낼지"라면 DestinationRule은 "거기로 보낼 때의 규칙". route(RDS)와 cluster(CDS)가 Envoy에서 원래 다른 칸이기 때문에 CR도 둘로 갈린 것. 그래서 둘은 거의 짝으로 다닌다(한쪽이 subset 이름을 만들면 다른 쪽이 그 이름을 가리킨다).
  2. 보안 CR(PeerAuthentication·AuthorizationPolicy)은 새 칸을 만들지 않는다. 기존 listener(LDS)에 필터를 끼워 넣는다. 트래픽 CR이 "어느 칸을 채우나"라면 보안 CR은 "기존 칸의 필터 체인 안에서" 동작한다. 그래서 보안 설정은 cluster/route를 바꾸지 않고 입구의 검문만 강화한다.
ℹ What you might be missing

이 매핑은 "주로"이지 1:1 고정이 아니다. 실제로는 여러 CR이 합쳐져 하나의 xDS가 만들어진다 — 한 cluster(CDS)에는 Kubernetes Service + DestinationRule subset + ServiceEntry가 모두 기여할 수 있다. 또 VirtualService도 listener(LDS)에 영향을 줄 때가 있고(TLS 라우팅의 SNI 매칭은 listener filter chain match에 반영), Gateway도 route 생성에 관여한다. 그래서 "CR→xDS"는 학습용 1차 근사이고, 정확한 진실은 항상 proxy-config(섹션 07)로 확인해야 한다. 멘탈 모델은 빠른 추론용, config 덤프는 검증용 — 이 둘을 같이 써야 한다.


05. CR 하나씩 — 기능 · 설정 · 실제 반영

각 CR을 같은 틀로 본다: 무엇을 위한 것인가 → 핵심 필드 → Envoy에 어떻게 박히나 → 확인 명령. 순서는 요청 처리 흐름(입구→길→대상)을 따른다 — 섹션 03의 5칸 순서 그대로다.

Gateway → LDS

기능: 게이트웨이 Envoy(ingress/egress)가 어떤 포트·프로토콜로 트래픽을 받을지와 그 포트의 TLS 처리(종단/통과)를 정의. 사이드카가 아니라 게이트웨이 Pod의 입구(listener) 를 만든다는 게 핵심.

spec:
  selector: { istio: egressgateway }   # 어느 게이트웨이 Pod에 적용할지 (label)
  servers:
    - port: { number: 443, name: tls, protocol: TLS }  # 받을 포트/프로토콜 → listener
      hosts: [api.partner.example.com]                 # 이 listener가 받을 호스트
      tls: { mode: PASSTHROUGH }                        # downstream TLS 처리 (LDS의 TLS)

Envoy 반영: servers[].port가 LDS의 listener로, tls.mode가 그 listener의 TLS 컨텍스트(종단할지 통과할지)로 박힌다. selector로 고른 Pod에만 push됨.

istioctl proxy-config listener deploy/istio-egressgateway -n istio-system
# PORT 443에 해당 listener가 생겼는지, TLS 모드가 맞는지 확인

VirtualService → RDS

기능: "이 호스트로 온 트래픽을 어떤 조건으로 어디로 보낼지" 규칙. Kubernetes Service의 단순 라우팅을 세밀하게(헤더·경로·가중치·재시도·타임아웃) 대체.

spec:
  hosts: [reviews]                 # 이 규칙이 적용될 대상 호스트
  http:                            # L7(HTTP) 라우팅 — tls:/tcp: 도 가능
    - match: [{ headers: { x-canary: { exact: "true" } } }]  # 매칭 조건
      route:
        - destination: { host: reviews, subset: v2 }          # 어느 cluster(subset)로
    - route:
        - destination: { host: reviews, subset: v1 }
          weight: 90                                          # 가중치 분배
        - destination: { host: reviews, subset: v2 }
          weight: 10

Envoy 반영: match+route가 RDS의 route 규칙으로 번역됨. 가중치는 route의 weighted_clusters로, subset은 대상 CDS cluster 이름으로 연결된다(그 cluster는 DestinationRule이 만듦). http 대신 tls+sniHosts를 쓰면 L4 SNI 라우팅이 되고, 이때는 route가 아니라 listener filter chain match에도 영향을 준다.

istioctl proxy-config route deploy/myapp --name 80 -o json
# match 조건과 weighted_clusters, 대상 cluster 이름 확인

DestinationRule → CDS

기능: 특정 호스트로 보낼 때 적용할 정책. ① subset 정의(라벨로 v1/v2 등 그룹 분할), ② 로드밸런싱, ③ 커넥션 풀·서킷 브레이커, ④ upstream TLS(외부로 나갈 때 TLS를 어떻게 맺을지). VirtualService가 "어디로"라면 DestinationRule은 "어떻게".

spec:
  host: reviews                         # 정책 대상 호스트
  trafficPolicy:
    loadBalancer: { simple: ROUND_ROBIN }
    connectionPool:                     # 커넥션 풀/서킷브레이커
      tcp: { maxConnections: 100 }
      http: { http2MaxRequests: 1000 }
    tls: { mode: ISTIO_MUTUAL }         # upstream TLS (CDS의 TLS) — 내가 맺는 연결
  subsets:                              # 라벨로 cluster를 분할
    - name: v1
      labels: { version: v1 }
    - name: v2
      labels: { version: v2 }

Envoy 반영:subset이 별도의 CDS cluster로 생성됨. 실제 cluster 이름은 direction|port|subset|fqdn 규칙을 따른다 — 예: outbound|9080|v1|reviews.default.svc.cluster.local, outbound|9080|v2|reviews.default.svc.cluster.local (정확한 이름 규칙은 Cluster 해부 참조). trafficPolicy.tls는 그 cluster의 transportSocket(upstream TLS)으로, connectionPool은 cluster의 커넥션 설정으로 박힌다. subset을 만들었으면 VirtualService가 그 이름을 가리켜야 실제로 쓰인다(짝으로 다니는 이유).

istioctl proxy-config cluster deploy/myapp --fqdn reviews.default.svc.cluster.local
# 정상: SUBSET 칼럼에 v1/v2가 각각 한 줄씩, DESTINATION RULE 칼럼에 해당 DR 이름이 떠야 한다
istioctl proxy-config cluster ... -o json | grep -A6 transportSocket   # upstream TLS

ServiceEntry → CDS + EDS

기능: 메시 밖의 서비스(외부 API, 레거시 VM 등)를 Istio 레지스트리에 등록. 등록되면 그 외부 호스트도 메시 내부 서비스처럼 cluster·endpoint를 갖게 되어 라우팅·정책 대상이 된다.

spec:
  hosts: [api.partner.example.com]
  ports:
    - { number: 443, name: tls, protocol: TLS }
  location: MESH_EXTERNAL
  resolution: DNS                # 호스트→IP 해석 방식: DNS / STATIC / NONE
  # resolution: STATIC 이면 endpoints: 로 IP 직접 명시

Envoy 반영: 외부 호스트용 CDS cluster가 생기고, resolution에 따라 EDS endpoint가 채워짐(DNS면 istiod/Envoy가 해석, STATIC이면 명시 IP, NONE이면 원래 목적지 IP). ServiceEntry가 없으면 REGISTRY_ONLY 환경에서 그 외부는 BlackHole로 막힌다.

istioctl proxy-config cluster deploy/myapp --fqdn api.partner.example.com
istioctl proxy-config endpoint deploy/myapp | grep partner
# 정상: outbound|443||api.partner.example.com cluster에 해석된 IP:443 ENDPOINT가 한 줄 이상 떠야 한다 (DNS resolution)

PeerAuthentication → LDS filter chain

기능: 워크로드가 받는(inbound) 연결에 mTLS를 요구할지를 정함. STRICT(반드시 mTLS), PERMISSIVE(mTLS·평문 둘 다 수용), DISABLE(평문). 명시적 PeerAuthentication이 없으면 메시는 PERMISSIVE로 동작하며, 보내는 쪽이 가능하면 auto-mTLS로 mTLS를 우선 맺는다(단 install profile/메시 정책에 따라 달라질 수 있음). 받는 쪽 정책이라는 게 핵심 — 보내는 쪽 TLS는 DestinationRule이다(자세한 LDS/CDS TLS 구분은 섹션 03 warning 참조).

spec:
  selector: { matchLabels: { app: reviews } }  # 없으면 네임스페이스 전체
  mtls: { mode: STRICT }                        # STRICT / PERMISSIVE / DISABLE

Envoy 반영: 대상 워크로드의 inbound LDS listener filter chain에 mTLS 요구가 주입됨. STRICT면 평문 연결이 listener 단에서 거부된다. 새 cluster/route를 만드는 게 아니라 기존 입구의 필터를 바꾸는 것.

istioctl proxy-config listener deploy/reviews --port 15006 -o json | grep -B2 -A5 transport_socket
# inbound는 15006(inbound capture 포트)으로 캡처된다. mTLS는 HTTP 필터가 아니라
# filterChainMatch+transport_socket 레벨이라 --type HTTP로는 안 보일 수 있다.
# 정상: STRICT면 filter chain의 transport_socket에 tls_context(또는 require_client_certificate)가 박혀 있어야 한다

AuthorizationPolicy → LDS filter chain (RBAC)

기능: "누가 누구에게 무엇을 할 수 있는지" 허용/거부 규칙. source(어느 워크로드/네임스페이스/principal)와 operation(메서드·경로)을 조건으로 ALLOW/DENY를 정함.

spec:
  selector: { matchLabels: { app: reviews } }
  action: ALLOW                                  # ALLOW / DENY / AUDIT / CUSTOM
  rules:
    - from: [{ source: { principals: ["cluster.local/ns/default/sa/ratings"] } }]
      to:   [{ operation: { methods: ["GET"], paths: ["/reviews/*"] } }]

Envoy 반영: 대상 listener filter chain에 RBAC 필터가 추가됨(LDS). principal 매칭은 mTLS로 검증된 신원(SPIFFE)을 쓰므로 PeerAuthentication mTLS가 사실상 전제 — 평문이면 principal을 신뢰할 수 없으니까.

istioctl proxy-config listener deploy/reviews -o json | grep -i rbac
# 정상: envoy.filters.http.rbac (또는 network rbac) 필터 이름이 한 줄 이상,
# action에 따라 shadow_rules/rules 안에 위 principals·operation 규칙이 떠야 한다
ℹ What you might be missing
  • "받는 TLS(PeerAuthentication=LDS)"와 "맺는 TLS(DestinationRule=CDS)"는 짝을 맞춰야 한다(이 LDS/CDS 구분 자체는 섹션 03 warning이 정본). 받는 쪽을 STRICT로 했는데 보내는 쪽이 mTLS를 안 맺으면 연결이 깨진다. Auto mTLS가 대개 자동으로 맞춰주지만, DestinationRule에서 TLS를 명시하면 그 자동 조정이 꺼질 수 있어 충돌이 난다.
  • AuthorizationPolicy는 평가 순서가 있다(CUSTOM → DENY → ALLOW). DENY가 ALLOW보다 먼저라, "허용했는데 막힌다"면 다른 DENY 정책을 의심해야 한다.
  • 이 6개가 전부가 아니다 — Sidecar(워크로드별 설정 범위 축소, xDS push 양 감소), EnvoyFilter(xDS를 직접 패치하는 최후 수단), WorkloadEntry/WorkloadGroup(VM 통합), Telemetry, RequestAuthentication(JWT) 등이 더 있다. 하지만 위 6개 + xDS 매핑을 잡으면 나머지는 "얘는 어느 xDS 칸을 건드리지?"로 같은 틀에서 흡수된다.

06. 흐름으로 통합 — 요청 하나가 5개 xDS를 통과하는 길

멘탈 모델이 진짜로 굳는 건 "개별 CR"이 아니라 "요청이 xDS 칸들을 순서대로 지나는 그림"을 그릴 수 있을 때다. 여기가 앵커가 한 바퀴 도는 지점이다 — 섹션 03에서 본 5칸 순서가 실제 요청 한 건에서 어떻게 작동하는지 본다. 메시 내부 호출(reviews → ratings, 카나리 라우팅 + mTLS) 하나를 따라간다.

1. LDSlistener+mTLS2. RDSroute+subset3. CDScluster+LB4. EDSPod IP5. SDSmTLS 연결PeerAuth (+Gateway)VirtualServiceDestinationRule (외부→SE)Service/EndpointsDR tls + 인증서L의 inbound 인증서 → S
그림 2. 요청 처리 5단계(LDS→RDS→CDS→EDS→SDS)와 각 단계를 채우는 CR: PeerAuth(L), VirtualService(R), DestinationRule/ServiceEntry(C), Service/Endpoints(E), DR tls(S). 어느 CR이 어느 xDS를 만드는지 매핑.

요청은 LDS→RDS→CDS→EDS→SDS 순으로 흐르고, 각 칸은 위에서 본 CR들이 채운다. 디버깅은 "몇 번 칸에서 끊겼나"를 찾는 일이다. 단 SDS(인증서)는 순서상 마지막 칸이 아니다 — inbound mTLS 검증(1번 LDS)과 outbound 핸드셰이크(5번)에 모두 쓰여 양쪽에 걸친다(LDS→SDS 점선).

말로 풀면 이렇게 흐른다.

  1. reviews의 사이드카가 ratings로 가는 요청을 잡고, 받는 쪽(ratings) listenerPeerAuthentication이 STRICT면 mTLS를 검증한다 (LDS).
  2. 보내는 쪽 route 규칙(VirtualService)이 헤더를 보고 v2 subset을 고른다 (RDS).
  3. 그 subset에 해당하는 cluster(DestinationRule이 만든 outbound|8080|v2|ratings.default.svc.cluster.local 같은 direction|port|subset|fqdn cluster)가 확정되고 로드밸런싱·커넥션풀 정책이 적용된다 (CDS).
  4. 그 cluster의 실제 Pod IP를 EDS가 고른다 (EDS).
  5. upstream 연결을 맺을 때 DestinationRule의 tls 설정대로 mTLS 인증서(SDS)를 써서 암호화한다.
✓ 이 흐름이 주는 디버깅 루틴

장애가 나면 이 5칸을 순서대로 짚으면 된다. - "연결이 거부됨" → LDS (mTLS 미스매치?) - "엉뚱한 버전으로 감" → RDS (route 규칙) - "cluster를 못 찾음(NC)" → CDS (subset/DR 누락) - "healthy upstream 없음(UH)" → EDS (endpoint 0) - "핸드셰이크 실패" → SDS (인증서)

CR 이름이 아니라 xDS 칸 번호로 생각하는 습관이 핵심.


07. 진실 확인 — proxy-config로 Envoy를 직접 들여다보기 (worked example)

멘탈 모델의 마지막 조각이자 가장 중요한 습관: "내가 쓴 CR이 Envoy에 실제로 어떻게 박혔는지"를 항상 config로 확인하는 것. 앵커가 "진실은 Envoy config에 있다"고 했으니, 그 진실을 직접 여는 창이 istioctl proxy-config다 — istiod가 그 프록시에 push한 실제 xDS를 그대로 보여준다.

명령 보는 xDS 언제 쓰나
proxy-config listener LDS 포트가 열렸나, TLS 모드·필터(mTLS/RBAC)가 박혔나
proxy-config route RDS VirtualService 매칭·가중치·대상 cluster가 맞나
proxy-config cluster CDS subset cluster 생성, upstream TLS, 커넥션풀 확인
proxy-config endpoint EDS 실제 IP가 채워졌나(0이면 UH의 원인)
proxy-config secret SDS 인증서가 로드됐나(핸드셰이크 실패 1차 점검)
proxy-status (전체) 프록시가 istiod와 sync 됐나(SYNCED/STALE/NOT SENT)

5칸을 순서대로 짚는 한 세션은 이렇게 흐른다 — 섹션 06의 요청 흐름과 같은 순서로 config를 연다.

# 0) 정적 검증 먼저 — CR끼리 충돌/오류 잡기
istioctl analyze -n my-namespace

# 1) 프록시가 최신 config를 받았나
istioctl proxy-status                       # 대상 Pod가 SYNCED인지

# 2) 내 CR이 만든 xDS를 순서대로 확인
istioctl proxy-config listener deploy/myapp -n my-namespace          # Gateway/PeerAuth
istioctl proxy-config route    deploy/myapp -n my-namespace --name 80 # VirtualService
istioctl proxy-config cluster  deploy/myapp -n my-namespace --fqdn reviews.default.svc.cluster.local  # DR/SE
istioctl proxy-config endpoint deploy/myapp -n my-namespace          # EDS
istioctl proxy-config secret   deploy/myapp -n my-namespace          # SDS

# 3) 더 깊게 — raw Envoy config 전체 덤프
istioctl proxy-config all deploy/myapp -n my-namespace -o json | less

proxy-config cluster를 실제로 돌리면 이런 표가 떠야 한다 — DestinationRule의 subset v1/v2가 각각 한 줄의 cluster로 박힌 걸 눈으로 확인하는 순간이 멘탈 모델이 닫히는 지점이다.

SERVICE FQDN                              PORT  SUBSET  DIRECTION  DESTINATION RULE
reviews.default.svc.cluster.local         9080  v1      outbound   reviews.default
reviews.default.svc.cluster.local         9080  v2      outbound   reviews.default
reviews.default.svc.cluster.local         9080  -       outbound   reviews.default

SUBSET 칼럼의 v1/v2가 곧 cluster 이름 outbound|9080|v1|reviews...의 그 subset 칸이고, DESTINATION RULE 칼럼이 "이 cluster를 누가 만들었나" 를 역으로 알려준다.

핵심은 -o json이다. 요약 테이블로 안 보이는 세부(transportSocket의 TLS 모드, route의 정확한 match, RBAC 규칙)는 JSON으로 봐야 한다. "CR을 이렇게 썼는데 왜 안 되지?"의 답은 거의 항상 이 JSON 안에 있다 — 내가 의도한 값과 실제 박힌 값의 차이를 눈으로 대조하는 게 가장 빠른 학습이자 디버깅.

ℹ config_dump의 출처 표시 읽는 법

proxy-config의 cluster 출력에는 DESTINATION RULE 칼럼이, route 출력에는 VIRTUAL SERVICE 칼럼이 있다 — 그 xDS 항목을 어느 CR이 만들었는지 역추적할 수 있다. 이게 "CR↔xDS" 멘탈 모델을 실전에서 닫는 고리: 의심나는 cluster를 보고 "아, 이건 저 DestinationRule이 만들었구나"를 즉시 연결할 수 있다.

ℹ What you might be missing
  • proxy-config그 프록시가 받은 설정이지 다른 프록시 것이 아니다 — 같은 CR도 워크로드마다 다르게 박힐 수 있어서(Sidecar 리소스, 네임스페이스 scope), 문제가 난 바로 그 Pod를 지정해야 한다.
  • config는 맞는데 트래픽이 안 되면 문제는 xDS 밖(노드 라우팅·방화벽·conntrack)이라, 그땐 Pod 안에서 openssl s_client·ss로 봐야 한다.
  • STALE 상태는 Envoy가 그 설정을 거부(NACK)했다는 신호 — 컨트롤/데이터 플레인 버전 불일치나 잘못된 CR 조합이 원인이고, istiod 로그에서 rejected 메시지를 확인해야 한다.
  • EnvoyFilter로 직접 패치한 항목은 proxy-config에 나타나지만 어느 CR이 만들었는지 추적이 어려우니, EnvoyFilter는 최후 수단으로만 쓰고 반드시 주석으로 의도를 남길 것.

정리 — 멘탈 모델 한 컷

CR은 사람이 쓰는 의도, xDS는 Envoy가 따르는 진실, istiod는 그 사이의 번역기. CR을 보면 "어느 xDS 칸을 채우나"를 묻고, 안 되면 "몇 번 칸에서 끊겼나"를 proxy-config로 확인한다 — 이 한 루프가 학습·활용·디버깅을 전부 덮는다.

핵심 정리

CR → xDS → 확인 명령 치트시트

CR 채우는 xDS 핵심 필드 확인 명령
Gateway LDS servers[].port, tls.mode proxy-config listener
VirtualService RDS http/tls.match, route.weight proxy-config route
DestinationRule CDS subsets, trafficPolicy.tls proxy-config cluster
ServiceEntry CDS+EDS hosts, resolution proxy-config cluster/endpoint
PeerAuthentication LDS mtls.mode proxy-config listener -o json
AuthorizationPolicy LDS action, rules proxy-config listener \| grep rbac

다음 단계 (홈랩 검증 루틴)

이 멘탈 모델을 체득하는 가장 빠른 길은 홈랩 클러스터에서 CR 하나 만들 때마다 proxy-config로 어느 xDS가 바뀌는지 직접 보는 것이다.

  1. DestinationRule에 subset 2개 추가 → proxy-config cluster에 cluster 2개 늘어나는지
  2. VirtualService 가중치 변경 → proxy-config route -o json의 weighted_clusters 숫자가 바뀌는지
  3. PeerAuthentication STRICT 적용 → proxy-config listener에 mTLS 요구가 생기고 평문 curl이 거부되는지

이 "CR 수정 → config diff 확인" 루프를 5~6번 돌리면 "CR을 보면 Envoy가 떠오르는" 상태가 된다.


What you might be missing


See also


ℹ 출처 (검증 기준 Istio 1.30 / `networking.istio.io/v1`)
  • CR→xDS 매핑(VirtualService→RDS, DestinationRule→CDS, Gateway→LDS, ServiceEntry→CDS+EDS, PeerAuthentication/AuthorizationPolicy→LDS filter chain) 및 Envoy가 Istio CR을 모르고 istiod가 xDS로 번역·push한다는 점 — Istio 아키텍처·xDS 해설 자료 및 Istio 공식 문서.
  • istioctl proxy-config {listener|route|cluster|endpoint|secret}proxy-status의 SYNCED/STALE/NOT SENT 의미 — Istio 디버깅 문서·proxy-config 레퍼런스. cluster 출력의 DESTINATION RULE 칼럼, route 출력의 VIRTUAL SERVICE 칼럼으로 역추적 가능.
  • ※ "주로 채우는 xDS"는 학습용 1차 근사이며, 실제로는 여러 CR이 합쳐져 하나의 xDS를 구성함. 정확한 결과는 항상 proxy-config -o json으로 확인할 것.