🏠 목록 xDS 5계층과 istioctl 진단 — LDS/RDS/CDS/EDS/SDS (출처: Istio 1.30 공식 문서 + 홈랩 검증) 📄 MD 원본 🌓 테마
istioxdsenvoyistioctlproxy-statusenvoyfilterdiagnosis

xDS 5계층과 istioctl 진단 — LDS/RDS/CDS/EDS/SDS (출처: Istio 1.30 공식 문서 + 홈랩 검증)

ℹ 이 문서가 다루는 것

istiod는 컴파일러다. K8s 상태 + Istio CRD를 입력으로 받아 Envoy 설정(Listener/Route/Cluster/Endpoint/Secret)으로 컴파일하고, xDS 채널로 각 프록시에 push한다. 그래서 모든 트래픽 장애는 "이 5계층 중 어디서 끊겼나"로 환원되고, 진단은 Listener→Route→Cluster→Endpoint(+Secret) 순으로 한 칸씩 내려가며 빈 칸을 찾는 일이 된다. 이 문서는 그 컴파일 산출물 5계층을 운영·진단 관점(무엇을 내려주나 / 확인 명령 / 영향 주는 리소스 / 고장 증상)으로 정리하고, ADS 적용 순서·장애 분석 순서·proxy-status·x describe·EnvoyFilter·CRD↔Envoy 번역표·매일 쓰는 명령 세트로 닫는다.

대상환경 Istio 1.30 / Envoy sidecar mode · 대상독자 xDS를 "YAML이 아니라 Envoy 설정"으로 사고하려는 DevOps/SRE · 범위 진단·운영(개념 원론은 아래 링크) · 선행개념 Envoy listener/cluster, K8s Service/EndpointSlice 개념 원론은 xDS API 계층, sync 상태값은 데이터플레인 sync 상태. 본 문서는 그 둘의 운영·진단 상세판이다.

⚠️ 버전 skew 주의: 이 환경은 istiod 1.30.0 ↔ 로컬 istioctl 1.27.0이다. proxy-status의 VERSION 컬럼이나 proxy-config 일부 필드가 어긋나 보일 수 있으나 이는 client 표시 한계이며 메시 동작 이상이 아니다. 정밀 진단은 버전을 맞춘 1.30 istioctl 사용 권장.


01. 배경 — 왜 xDS인가: Envoy는 "빈 상자"이고 istiod가 채운다

Envoy는 그 자체로는 아무 라우팅도 모르는 고성능 L4/L7 프록시 엔진입니다. "reviews로 가는 요청은 v1/v2로 나눠라" 같은 지식이 0이고, 그 지식은 전부 설정(configuration)으로 외부에서 주입돼야 합니다. 문제는 메시 환경이 정적이지 않다는 데 있습니다 — Pod는 뜨고 죽고, Service endpoint는 분 단위로 바뀌고, VirtualService 한 줄 고치면 수백 개 프록시가 동시에 새 route를 알아야 합니다. 설정 파일을 디스크에 깔고 프로세스를 재시작하는 방식으로는 이걸 못 따라갑니다.

그래서 Envoy는 설정을 런타임에 원격으로 받아오는 메커니즘을 가집니다. 이것이 xDS입니다. xDS = "무언가 Discovery Service"들의 묶음이고, x 자리에 Listener, Route, Cluster, Endpoint, Secret이 들어갑니다. Envoy는 이 동적 설정들을 xDS management server로부터 streaming gRPC로 받고, Istio에서는 그 management server가 바로 istiod입니다.

여기서 멘탈모델을 한 단계 끌어올려야 합니다. Istio를 "서비스 메시"라는 흐릿한 말로 두지 말고, "K8s 상태 + Istio CRD를 Envoy 설정으로 컴파일해 모든 프록시에 배포하는 제어 시스템"으로 보십시오. istiod = 컴파일러, K8s/CRD = 소스 코드, Envoy 설정 5계층 = 컴파일된 바이너리, xDS = 그 바이너리를 프록시 메모리에 적재하는 로더. 이 관점이 서면 진단이 단순해집니다 — 트래픽이 깨졌다는 건 소스(CRD)가 잘못됐거나, 컴파일이 안 됐거나, 로드가 안 됐거나 셋 중 하나이고, 그걸 가르는 게 아래 도구들입니다.

★ 한 문장 멘탈모델

xDS = istiod가 Kubernetes/Istio 상태를 Envoy 설정(Listener/Route/Cluster/Endpoint/Secret)으로 컴파일해서 각 프록시에 push하는 5개 API. 진단 = 이 5계층을 순서대로 따라가며 빈 칸을 찾는 일.


02. 아키텍처 — 5계층이 "요청 처리 경로" 그대로인 이유

핵심 통찰은 이것입니다. 5개 계층은 임의로 나눈 게 아니라, 요청 하나가 Envoy 내부에서 resolve되는 경로 그 자체입니다. 요청이 들어오면 Envoy는 항상 같은 질문을 순서대로 던지고, 각 질문에 답하는 설정 조각이 곧 xDS 한 계층입니다.

LDS  "이 트래픽을 어디서 받나?"        → Listener  (소켓 + filter chain)
RDS  "그래서 어느 cluster로 보내나?"   → Route     (host/path/header → cluster 이름)
CDS  "그 cluster는 어떤 pool인가?"     → Cluster   (LB/timeout/circuit breaker/TLS)
EDS  "그 pool의 실제 Pod IP는?"        → Endpoint  (IP:port 목록)
SDS  "mTLS 인증서는?"  (가로지름)      → Secret    (cert/key/CA)
Listener (LDS)어디서 받나Route (RDS)어느 cluster로Cluster (CDS)upstream poolEndpoint (EDS)실제 Pod IPSecret (SDS)mTLS cert/key/CAmTLS
그림 2. 진단 관점의 xDS 계층: Listener(수신)→Route(cluster)→Cluster(pool)→Endpoint(Pod IP), CDS는 mTLS 시 Secret(SDS) 참조. proxy-config의 routes/clusters/endpoints/secret이 각 계층에 대응.

이 그림 한 장이 이 문서의 앵커입니다. 진단도, 적용 순서도, 응답 플래그도 전부 이 사슬에서 파생됩니다. 사슬의 어느 고리가 끊겼는지가 곧 장애의 위치입니다.

02.1 다섯 고리를 하나씩 — 무엇을 내려주고, 어떻게 보고, 왜 깨지나

LDS — Listener Discovery Service. Listener는 "Envoy가 어디에서 트래픽을 받을 것인가"입니다. sidecar에는 두 종류가 있습니다 — capture listener와 port별 listener.

0.0.0.0:15001  outbound capture listener
0.0.0.0:15006  inbound capture listener
0.0.0.0:9080   outbound HTTP listener (port별, 대부분 0.0.0.0 wildcard)
10.96.0.10:53  service-IP별 listener (조건부 — 아래 주석)

sidecar outbound listener는 대부분 0.0.0.0:PORT wildcard로 capture됩니다. 10.96.0.10:53처럼 service IP별 dedicated listener는 protocol 충돌을 피해야 하는 경우(같은 포트에 여러 프로토콜)나 TCP/특정 케이스에서만 생성되며, 일반 HTTP outbound에서는 보통 만들어지지 않습니다.

15001/15006이 capture listener인 이유는 iptables가 모든 outbound/inbound를 이 두 포트로 redirect하기 때문입니다(메커니즘은 → Sidecar 트래픽 캡처). LDS가 반영되면 Envoy 안에 listener, filter chain, protocol inspector, HTTP connection manager, TCP proxy 같은 처리 구조가 생깁니다.

istioctl proxy-config listener <pod> -n <namespace>

RDS — Route Discovery Service. Route는 "이 HTTP 요청을 어느 cluster로 보낼 것인가"입니다. ⚠️ RDS의 D는 흔히 "Direct"로 오해되지만 정확히는 Route Discovery Service 입니다(xDS 전체가 x + Discovery Service 패턴 — 이것만 기억하면 안 헷갈림). route configuration 안에는 virtual host(어떤 host 묶음인가), route entry(path/header match → 어느 cluster), header modification이 들어갑니다.

Host:   reviews.default.svc.cluster.local
Path:   /api/v1/reviews
Header: x-user = jason
→ cluster: outbound|9080|v2|reviews.default.svc.cluster.local

route의 destination은 outbound|PORT|SUBSET|FQDN 형태의 cluster를 참조합니다. 이 이름 규칙과 subset의 의미는 → Cluster 해부.

istioctl proxy-config route <pod> -n <namespace>

CDS — Cluster Discovery Service. Cluster는 "upstream backend pool", 즉 Envoy가 요청을 보낼 논리적 목적지입니다(아직 실제 IP는 모름).

outbound|9080||reviews.default.svc.cluster.local      (subset 없음 = 전체 pool)
outbound|9080|v1|reviews.default.svc.cluster.local
outbound|9080|v2|reviews.default.svc.cluster.local

CDS에는 cluster 이름, EDS/DNS 사용 여부, connect timeout, circuit breaker, outlier detection, load balancing, upstream TLS 설정이 들어갑니다.

istioctl proxy-config cluster <pod> -n <namespace>

EDS — Endpoint Discovery Service. CDS가 "reviews v1으로 보내라"라는 논리라면, EDS는 "reviews v1 Pod IP는 이것들이다"라는 실체입니다. 왜 따로 받나? endpoint는 cluster 정의보다 훨씬 자주 바뀌기 때문입니다 — Pod가 죽고 살 때마다 무거운 cluster 설정(LB/circuit breaker/TLS)을 통째로 다시 보내는 건 낭비라, Istio는 변하지 않는 cluster(CDS)와 자주 변하는 endpoint(EDS)를 분리해서 endpoint만 가볍게 갱신합니다.

Cluster: outbound|9080|v1|reviews.default.svc.cluster.local
Endpoints:
  10.244.1.12:9080
  10.244.2.18:9080
istioctl proxy-config endpoint <pod> -n <namespace>

SDS — Secret Discovery Service. TLS 인증서, private key, root CA를 내려줍니다. 위 4계층이 "어디로 보내나"의 사슬이라면 SDS는 그 사슬을 가로지르는 보안 축입니다. 핵심 설계는 이것 — Istio sidecar mode에서 workload 인증서는 app container가 직접 들고 있지 않습니다. workload 시작 시 Envoy가 SDS API로 cert/key를 요청하고, Istio agent가 istiod CA에서 받은 cert를 Envoy 메모리로만 전달합니다(디스크 secret 마운트 없음 = blast radius 축소). 이 cert가 mTLS handshake에서 workload identity를 증명합니다.

istioctl proxy-config secret <pod> -n <namespace>

02.2 ADS와 적용 순서 — "route가 빈 cluster를 가리키는 순간"을 없애는 불변식

Envoy 공식 문서 기준 CDS/EDS/LDS/RDS/SDS 각각이 독립 streaming endpoint를 가지지만, ADS(Aggregated Discovery Service) 는 이 여러 xDS resource를 하나의 bidirectional gRPC stream으로 묶어 전달합니다. Istio가 ADS를 쓰는 이유는 단 하나, 적용 순서 보장입니다.

왜 순서가 생명인가. route를 cluster X에서 cluster Y로 바꿀 때, Envoy가 cluster Y와 그 endpoint를 먼저 알아야 route가 안 깨집니다. 채널이 5개로 따로 놀면 RDS가 CDS보다 먼저 도착해 "존재하지 않는 cluster를 가리키는 route"가 생기는 race가 발생합니다. ADS는 하나의 stream에서 CDS→EDS→RDS→LDS를 순서대로 흘려 이 race를 원천 차단합니다.

K8s API + CRDistiod compilerADS single streamEnvoy proxyapply: CDSEDSRDSLDSEnvoy apply order
그림 1. K8s/CRD→istiod 컴파일→ADS 단일 stream→Envoy. Envoy 내부 apply 순서는 CDS→EDS→RDS→LDS(make-before-break)로, 참조 대상이 먼저 존재하도록 보장.

순서에는 비대칭이 있고, 이게 진짜 포인트입니다. add(추가)CDS→EDS→RDS→LDSbottom-up(참조 대상부터 만들고 나중에 참조)으로 적용됩니다. 반대로 remove(삭제)나 warm shutdownLDS/RDS 먼저 → 그 다음 CDS/EDStop-down(참조하는 쪽을 먼저 떼고 나중에 대상 제거)으로 적용됩니다.

add    : CDS → EDS → RDS → LDS   (bottom-up, 대상 먼저)
remove : LDS / RDS → CDS / EDS   (top-down, 참조자 먼저)

방향은 정반대지만 두 경우 모두 같은 불변식 하나를 지키기 위함입니다 — "route가 아직 없거나 이미 사라진 cluster를 가리키는 순간이 절대 생기지 않는다." 추가든 삭제든 이 불변식이 유지되므로 트래픽이 깨지지 않습니다. Envoy는 update를 받은 뒤 ACK/NACK로 적용 여부를 control plane에 알리고, 이 ACK/NACK가 proxy-statusSYNCED/STALE로 드러납니다(§04).

02.3 응답 플래그 = 사슬의 끊긴 고리 좌표

위 사슬의 각 고리가 끊기면 Envoy access log에 고유한 response flag가 찍힙니다. 이게 진단의 출발점입니다 — flag만 보면 5계층 중 몇 번째부터 보면 되는지 바로 압니다(flag 전체표는 → Envoy 응답 플래그).

flag 끊긴 고리 먼저 볼 계층
NR (NoRoute) RDS route (1단계)
NC (NoCluster) CDS cluster (2단계)
UH (NoHealthyUpstream) EDS endpoint (3단계)
UF (UpstreamFailure) SDS/mTLS secret (4단계)

03. 적용 예시 — 끊긴 고리 찾아 내려가기 (worked diagnosis)

이제 멘탈모델을 실제 명령으로 돌립니다. 시나리오: "reviews로 보낸 요청이 503이다." 분석은 사슬을 위에서 아래로 내려가며 빈 칸을 찾는 일입니다.

03.1 0단계 게이트 — 먼저 "전달은 됐나"부터

layer trace 전에 proxy-status로 설정이 Envoy에 도착했는지부터 확인합니다. NOT SENT/STALE이면 설정이 아직 프록시에 없는 것이라, 그 위에서 layer를 따라가 봐야 의미가 없습니다(sync 상태의 1단계 게이트).

istioctl proxy-status

예상 출력:

NAME                                                CLUSTER     CDS     LDS     EDS     RDS       ECDS      ISTIOD                   VERSION
details-v1-558b8b4b76-qzqsg.default                 Kubernetes  SYNCED  SYNCED  SYNCED  SYNCED    NOT SENT  istiod-6cf8d4f9cb-wm7x6  1.30.1
productpage-v1-6987489c74-nc7tj.default             Kubernetes  SYNCED  SYNCED  SYNCED  SYNCED    NOT SENT  istiod-6cf8d4f9cb-wm7x6  1.30.1
istio-ingressgateway-66c994c45c-cmb7x.istio-system  Kubernetes  SYNCED  SYNCED  SYNCED  NOT SENT  NOT SENT  istiod-6cf8d4f9cb-wm7x6  1.30.1
reviews-v1-7f99cc4496-rtsqn.default                 Kubernetes  SYNCED  SYNCED  SYNCED  SYNCED    NOT SENT  istiod-6cf8d4f9cb-wm7x6  1.30.1

여기서 모두 SYNCED(또는 정상 NOT SENT)이고 reviews proxy가 목록에 보이면 전달은 정상 — 이제 layer를 내려가도 됩니다. 만약 reviews proxy가 목록에서 빠졌다면 그건 istiod 미연결이고, 그 즉시 원인이 좁혀집니다(layer 추적 불필요).

03.2 1~4단계 — 사슬을 따라 내려가기

# 1. route가 원하는 cluster를 가리키는지   (NR이면 여기)
istioctl proxy-config route <pod> -n <ns>

# 2. 그 cluster가 존재하는지              (NC이면 여기)
istioctl proxy-config cluster <pod> -n <ns>

# 3. 그 cluster에 endpoint가 있는지        (UH이면 여기)
istioctl proxy-config endpoint <pod> -n <ns>

# 4. mTLS secret이 있는지                  (UF이면 여기)
istioctl proxy-config secret <pod> -n <ns>

# 5. (재동기 확인) proxy가 istiod와 sync됐는지
istioctl proxy-status

이 시나리오에서 1·2단계는 정상인데 3단계에서 endpoint가 비어 있다고 합시다 — access log의 UH와 일치합니다. cluster outbound|9080|v1|reviews...는 존재하지만 endpoint 목록이 0이면 "cluster 있음 ≠ 정상"의 전형입니다. 원인은 readiness 실패 / outlier detection eject / selector mismatch 중 하나로 좁혀지고, kubectl get endpointslice로 K8s 쪽 실체를 대조하면 끝납니다.

✓ 시작점 좁히기

response flag로 trace를 단축할 수 있음. NR→1단계, NC→2단계, UH→3단계, UF→4단계부터 보면 빠름. 처음부터 1단계로 내려갈 필요 없이 flag가 가리키는 고리로 점프하면 됨.

03.3 한 단계 위 시점 — istioctl x describe pod

proxy-config가 Envoy raw 설정이라면, istioctl x describe pod(정식 istioctl experimental describe pod)는 "이 Pod에 영향을 주는 Istio 설정"을 사람이 읽기 쉽게 요약합니다. 보통 이걸로 큰 그림을 잡고 proxy-config로 내려갑니다.

1.30에서 x describe는 여전히 experimental입니다(deprecated 트랙으로 분류 — 향후 변경/제거 가능). 동작은 하지만 운영 자동화·스크립트의 안정적 기반으로 삼지 말고, 결정적 진단은 proxy-config/analyze로 하십시오.

장애 분석의 첫 질문 "이 Pod가 mesh 안에 있나?"를 즉답합니다. sidecar 없는 Pod:

istioctl x describe pod coredns-f9fd979d6-2zsxk -n kube-system
Pod: coredns-f9fd979d6-2zsxk
   Pod Ports: 53/UDP (coredns), 53 (coredns), 9153 (coredns)
WARNING: coredns-f9fd979d6-2zsxk is not part of mesh; no Istio sidecar
--------------------
Error: failed to execute command on sidecar ...

→ Envoy sidecar가 없으면 mesh policy, mTLS, AuthorizationPolicy, routing rule 대부분이 동작하지 않음.

mesh 안 Pod(DestinationRule 포함):

istioctl x describe pod ratings-v1-7dc98c7588-8jsbw -n default
Pod: ratings-v1-7dc98c7588-8jsbw
   Pod Ports: 9080 (ratings), 15090 (istio-proxy)
--------------------
Service: ratings
   Port: http 9080/HTTP targets pod port 9080
DestinationRule: ratings for "ratings"
   Matching subsets: v1
      (Non-matching subsets v2,v2-mysql,v2-mysql-vm)
   Traffic Policy TLS Mode: ISTIO_MUTUAL

→ container port, istio-proxy port(15090), service protocol, DestinationRule, matching subset, TLS mode를 한눈에 요약.

초기 진단 : istioctl x describe pod    ← "이 Pod에 적용되는 Istio 설정 요약" (추상화 관점)
깊은 분석 : istioctl proxy-config ...   ← "Envoy 내부 실제 설정" (raw)

03.4 proxy-status 상태값을 정확히 읽기

§03.1에서 본 컬럼들의 의미입니다. CLUSTER는 proxy가 속한 mesh cluster — 단일 클러스터에서는 모두 Kubernetes, multicluster일 때만 갈립니다.

상태 의미
SYNCED Envoy가 istiod의 마지막 설정을 ACK함 (정상)
NOT SENT istiod가 보낼 것이 없었음 (예: gateway에 적용할 RDS route 없음 — 정상일 수 있음)
STALE istiod가 update를 보냈지만 Envoy ACK를 못 받음 (네트워크 또는 Istio 자체 문제 의심)
(proxy 누락) 그 proxy가 현재 istiod에 연결 안 됨 → 설정을 못 받는 중
CDS STALE  → cluster 설정 sync 문제      LDS STALE → listener 설정 sync 문제
EDS STALE  → endpoint 설정 sync 문제     RDS STALE → route 설정 sync 문제
ECDS       → WasmPlugin/EnvoyFilter의 extension config sync. extension 안 쓰면 보통 NOT SENT
proxy 없음 → sidecar가 istiod에 연결 안 됨 (injection 누락, agent crash, 네트워크 차단)

ECDS(Extension Config Discovery Service)는 WasmPlugin이나 extension을 inject하는 EnvoyFilter의 확장 설정을 별도로 push하는 채널입니다. extension이 없는 일반 proxy는 NOT SENT가 정상이고, 사용 중인데 STALE이면 해당 WasmPlugin/EnvoyFilter sync 문제로 좁혀집니다.

⚠ 함정

NOT SENT는 장애가 아닐 수 있음. ingress gateway의 RDS가 NOT SENT인 건 그 gateway에 적용할 HTTP route가 아직 없다는 뜻일 수 있음. 반면 proxy가 목록에서 아예 빠진 것은 거의 항상 문제(istiod 미연결).

03.5 diff 훈련 — "YAML이 아니라 Envoy 설정으로 사고하기"

xDS를 진짜로 이해하는 가장 빠른 길은 리소스 apply 전후proxy-config ... -o json을 dump해 diff를 떠 보는 것입니다. CRD 한 줄이 어느 Envoy 설정으로 컴파일되는지가 눈에 보입니다.

istioctl proxy-config route <pod> -n <ns> -o json   > before-route.json
kubectl apply -f virtualservice.yaml
istioctl proxy-config route <pod> -n <ns> -o json   > after-route.json
diff -u before-route.json after-route.json

§03.2의 layer-trace와 이 diff 워크플로를 한 번에 자동화한 스크립트가 있습니다(listeners/routes/clusters/endpoints/secret 일괄 덤프). - 📎 proxy-dump.sh (proxy-config 일괄 덤프)


04. CRD ↔ Envoy 번역표 + 매일 쓰는 명령 세트

운영자는 늘 "이 YAML이 어느 Envoy 설정으로 바뀌었나"를 묻습니다. 외우는 게 아니라 매번 proxy-config로 확인하는 용도의 매핑입니다.

Istio/K8s 리소스 Envoy 관점 장애 시 보는 곳
Service, EndpointSlice, ServiceEntry service registry / endpoint source proxy-config endpoint, proxy-config cluster
VirtualService HTTP/TCP/TLS route (RDS) proxy-config route
DestinationRule LB, subset, TLS, connection pool, outlier detection (CDS) proxy-config cluster
Gateway edge listener + server + TLS (LDS) proxy-config listener, proxy-config route
PeerAuthentication inbound mTLS requirement (LDS filter + SDS) proxy-config listener, proxy-config secret
AuthorizationPolicy Envoy RBAC filter proxy-config listener -o json
Sidecar config scope / import boundary proxy-config cluster, proxy-config listener
EnvoyFilter raw Envoy patch proxy-config listener/cluster/route -o json

매일 쓰는 디버깅 명령 10종:

# 1. 전체 proxy sync 상태
istioctl proxy-status
# 2. 특정 pod가 어떤 istiod에 붙었는지
istioctl proxy-status | grep <pod>
# 3. pod 관점 설명 (mesh 소속 + 적용 설정 요약) — experimental, 향후 변경/제거 가능
istioctl x describe pod <pod> -n <ns>
# 4. Envoy listener 확인 (어디서 받나)
istioctl proxy-config listener <pod> -n <ns>
# 5. HTTP/gRPC route 확인 (어느 cluster로)
istioctl proxy-config route <pod> -n <ns>
# 6. upstream cluster 확인 (backend pool)
istioctl proxy-config cluster <pod> -n <ns>
# 7. endpoint 확인 (실제 Pod IP)
istioctl proxy-config endpoint <pod> -n <ns>
# 8. mTLS secret 확인 (cert/key/CA)
istioctl proxy-config secret <pod> -n <ns>
# 9. bootstrap 확인 (어느 istiod, 어떤 metadata로 시작했는지)
istioctl proxy-config bootstrap <pod> -n <ns>
# 10. 구성 오류 사전 분석 (host mismatch, injection 누락, subset 불일치 등)
istioctl analyze -A

05. EnvoyFilter는 언제 쓰나 — escape hatch의 비용

EnvoyFilter는 istiod가 생성한 Envoy 설정을 직접 patch하는 escape hatch입니다. 특정 field를 바꾸거나, filter를 추가하거나, listener/cluster를 새로 추가할 수 있습니다. 존재 이유는 단순합니다 — Istio API가 Envoy의 모든 기능을 추상화하지는 않기 때문입니다. 그래서 운영 기준으로 일반 application team은 거의 안 쓰는 게 정상이고, platform team도 신중히 제한적으로만 씁니다.

쓰게 되는 경우:

- 특정 Envoy HTTP filter 삽입
- Lua/Wasm/ext_authz 같은 고급 확장
- mesh-wide idle timeout / connection option 세밀 조정
- 특정 vendor agent / observability / security filter 연동
- Istio API로 아직 지원 안 하는 Envoy 기능 임시 사용 / 긴급 workaround

우선순위 원칙은 "위에서부터 가능하면 그걸로, EnvoyFilter는 최후"입니다. EnvoyFilter는 컴파일러의 출력물을 손으로 후패치하는 것이라, 컴파일러(istiod)가 다음 버전에서 출력 형태를 바꾸면 조용히 깨집니다.

VirtualService로 가능하면  → VirtualService
DestinationRule로 가능하면 → DestinationRule
Telemetry API로 가능하면   → Telemetry
WasmPlugin으로 가능하면    → WasmPlugin
마지막에만                  → EnvoyFilter
⚠ 함정

잘못된 EnvoyFilter는 mesh 전체를 불안정하게 만들 수 있음. Istio networking 내부 구현 및 Envoy xDS API에 깊게 묶여 있어 proxy version upgrade마다 재검증 필요. 여러 EnvoyFilter가 충돌하면 동작은 undefined.

성숙도 신호와 가드레일:

[성숙도 신호]
Application namespace에 EnvoyFilter가 많다  → 운영 성숙도 낮음 또는 platform abstraction 부족 가능성
Root namespace의 global EnvoyFilter가 많다 → upgrade risk 큼. owner/test/rollback/version guard 필수

[운영 가드레일]
- EnvoyFilter는 platform team만 merge 가능
- workloadSelector 없는 root namespace EnvoyFilter 금지 또는 강한 승인
- 적용 전후 config_dump diff 필수
- canary namespace에서 먼저 검증
- Istio minor upgrade마다 재검증

핵심 정리

멘탈모델 : istiod=컴파일러. K8s/CRD → Envoy 설정 5계층 → xDS로 push. 장애 = 사슬의 끊긴 고리 찾기.

5계층   : LDS(어디서 받나) → RDS(어느 cluster) → CDS(어떤 pool) → EDS(실제 Pod IP) (+SDS mTLS cert)
ADS 적용 : add=CDS→EDS→RDS→LDS(bottom-up) / remove=top-down — 불변식: route가 빈 cluster를 안 가리킴
분석 순서 : (0) proxy-status 게이트 → route → cluster → endpoint → secret. flag로 시작점 점프(NR/NC/UH/UF)

proxy-status : SYNCED(ACK) / NOT SENT(보낼 것 없음) / STALE(보냈지만 ACK 못 받음) / 누락(미연결) / ECDS=확장설정
x describe   : 초기 진단(experimental)  |  proxy-config : 깊은 분석(Envoy raw)  |  diff로 CRD→Envoy 번역 체득
EnvoyFilter  : 컴파일러 출력 후패치(escape hatch). VS/DR/Telemetry/WasmPlugin 우선, EnvoyFilter 최후

RDS의 D는 Discovery(Direct 아님). 요청은 항상 Listener → Route → Cluster → Endpoint (+Secret) 순으로 resolve됨.

What you might be missing

관련 파일 · 참조