본문으로 건너뛰기
Istio의 Envoy 조립

13 · Istio의 Envoy 조립 — proxyv2 이미지부터 CRD 번역까지

  • proxyv2에는 바이너리가 둘 들어 있고(Envoy · pilot-agent), 그 Envoy조차 upstream 그대로가 아닙니다 — istio/proxy가 확장을 함께 컴파일해 만든 빌드입니다.
  • istio_requests_totalsource_*/destination_* 라벨은 프록시 안에 컴파일된 확장이 만듭니다. 컨트롤 플레인은 관여하지 않습니다. Mixer가 사라졌다는 말의 실질이 여기 있습니다.
  • CRD는 xDS 리소스로 번역됩니다 — VirtualService→route, DestinationRule→cluster, Gateway→listener. 번역 결과는 추측하지 말고 istioctl proxy-config로 봅니다.
  • Istio 빌드는 Envoy의 특정 커밋에 pin됩니다. 사이드카 업그레이드가 곧 Envoy 업그레이드입니다. 그 버전은 릴리스 노트가 아니라 파드에 물어봐야 압니다.
  • 이미지에 박힌 내장 확장과 CRD로 추가하는 사용자 확장(08)은 바꾸는 방법이 다릅니다. 전자는 이미지 교체로만, 후자는 설정으로.

“Istio는 Envoy를 쓴다"는 문장은 어디에나 있지만 ‘쓴다’가 무엇인지는 잘 안 적혀 있습니다. 12가 Envoy 자체의 능력을 훑었다면, 이 문서는 그 위에 Istio가 무엇을 더했는지를 봅니다. 층이 둘입니다. 아래층은 컴파일 시점 — Istio는 upstream Envoy 바이너리를 받아 쓰지 않고 자기 확장을 넣어 다시 빌드합니다. 위층은 런타임 — istiod가 CRD를 Envoy 설정으로 번역해 xDS로 내려보냅니다.

파드 안에서 그 둘이 어떻게 배선되는지(부트스트랩·SDS·xDS 중계)는 11에 있습니다. 여기서 다루는 것은 부품의 출처입니다.

관련 문서: 11 요청 경로 해부(파드 안의 배선) · 12 Envoy의 능력 · 06 관측성(여기서 만들어진 지표를 쓰는 쪽) · 08 EnvoyFilter(사용자 확장) · 02 컨트롤 플레인

1. proxyv2 해부 — 컨테이너 하나에 든 두 바이너리

사이드카 컨테이너의 이미지 이름은 proxy가 아니라 proxyv2입니다. 그 안에는 바이너리가 둘 들어갑니다. istio/istio 레포의 Dockerfile.proxyv2에 그대로 적혀 있습니다.

COPY ${TARGETARCH:-amd64}/${SIDECAR} /usr/local/bin/${SIDECAR}
COPY ${TARGETARCH:-amd64}/pilot-agent /usr/local/bin/pilot-agent
ENTRYPOINT ["/usr/local/bin/pilot-agent"]

SIDECARenvoy입니다. 이미지는 Envoy와 pilot-agent를 함께 담지만 컨테이너가 뜰 때 실행되는 것은 pilot-agent 쪽입니다. Envoy는 PID 1이 아닙니다. agent가 기동하고 관리하는 프로세스입니다. 11에서 “Envoy는 istiod에 직접 붙지 않는다"고 한 구조가 이미지 레벨에서부터 이렇게 정해져 있습니다.

여기 실린 envoy도 upstream 바이너리가 아닙니다. istio/proxy 레포의 README가 자기 정의를 이렇게 씁니다.

The Istio Proxy is a microservice proxy that can be used on the client and server side, and forms a microservice mesh. It is based on Envoy with the addition of several policy and telemetry extensions.

istio.io 아키텍처 문서도 같은 말을 한 줄로 합니다 — “Istio uses an extended version of the Envoy proxy.” 그 “extended"의 구현이 istio/proxy의 빌드입니다. WORKSPACE 파일이 ENVOY_SHA / ENVOY_SHA256으로 envoyproxy/envoy의 특정 커밋을 지정해 받아옵니다. 거기에 Istio 확장 소스를 함께 컴파일합니다.

proxyv2 이미지가 만들어지는 경로 — Envoy는 커밋 단위로 pin되어 확장과 함께 컴파일되고, pilot-agent는 별도로 실려 엔트리포인트가 됩니다.
도식 텍스트
  • upstream Envoy — envoyproxy/envoy 특정 커밋
  • Istio 확장 — metadata exchange · stats · ALPN
  • istio/proxy 빌드 — WORKSPACE의 ENVOY_SHA로 고정
  • pilot-agent — istio/istio에서 빌드
  • proxyv2 이미지 — ENTRYPOINT = pilot-agent
  • pin
  • 함께 컴파일
  • envoy
  • 그대로 COPY

istio/proxy README에는 2024-04-17 워크그룹 결론으로 “New extensions are not added unless they are part of core APIs"라는 방침이 명시돼 있습니다. 확장을 무한정 늘리기보다, 이미 들어간 것을 유지하고 신규 추가에는 심사를 거는 쪽입니다.

2. Istio가 심는 확장 — 표준 메트릭이 만들어지는 곳

06istio_requests_total과 그 표준 라벨을 “공짜로 나온다"고 정리했습니다. 그 공짜가 어디서 만들어지는지를 이 절에서 봅니다. 만드는 주체는 프록시 안에 컴파일된 필터들입니다. 컨트롤 플레인 쪽이 아닙니다.

계층필터하는 일
네트워크(TCP)envoy.filters.network.metadata_exchange커넥션 위에서 피어 메타데이터를 주고받는다
HTTPenvoy.wasm.metadata_exchange헤더로 피어 메타데이터를 주고받는다
HTTPenvoy.wasm.stats표준 메트릭을 생성한다

네트워크 계층 필터의 설정 이름은 istio.metadata_exchange입니다. HTTP 계층의 두 필터는 envoy.filters.http.wasm으로 래핑된 같은 wasm 필터입니다.

peer metadata exchange — source_* / destination_* 라벨의 출처

메트릭에 destination_workload 같은 라벨이 붙으려면 프록시가 상대가 누구인지 알아야 합니다. 그런데 프록시가 직접 아는 것은 자기 워크로드 정보뿐입니다. 그래서 두 사이드카가 요청 경로 위에서 서로의 메타데이터를 교환합니다. 그 채널은 계층마다 다릅니다.

  • 협상 — mTLS 핸드셰이크에서 istio-peer-exchange라는 ALPN 프로토콜을 클라이언트·서버 사이드카가 우선협상합니다. 이걸 끄는 스위치(PILOT_DISABLE_MX_ALPN)가 Istio 1.20에서 도입됐습니다.
  • TCP — magic byte 뒤에 length-prefixed protobuf payload를 실어 보냅니다.
  • HTTP — x-envoy-peer-metadata-id / x-envoy-peer-metadata 헤더로 나릅니다.
두 사이드카가 서로의 정체를 교환하고, 그 결과가 각자의 스탯 필터에서 라벨이 됩니다.
도식 텍스트
  • 소스 사이드카
  • 목적지 사이드카
  1. 둘 다 같은 proxyv2 — 같은 확장이 컴파일되어 있다
  2. 1. mTLS 핸드셰이크에서 ALPN istio-peer-exchange 협상
  3. 2. HTTP면 x-envoy-peer-metadata 헤더로 자기 메타데이터
  4. 3. 상대 메타데이터
  5. TCP는 magic byte + length-prefixed protobuf로 · 같은 것을 교환한다
  6. stats 필터가 양쪽에서 각자 메트릭을 만든다 · source_* / destination_* 라벨이 여기서 채워진다

11에서 “mTLS가 꺼져 있으면 TCP 지표의 상대 워크로드 라벨이 빈다"고 한 것의 이유가 이 그림입니다. 협상이 mTLS 핸드셰이크에 붙어 있습니다. 핸드셰이크가 없으면 교환도 없고 교환이 없으면 라벨을 채울 재료가 없습니다.

telemetry v2 — 메트릭 생성이 프록시 안으로 들어온 것

envoy.wasm.stats 필터가 표준 메트릭을 프록시 안에서 만듭니다. 이름이 “v2"인 이유는 v1이 따로 있었기 때문입니다. 예전에는 Mixer라는 별도 텔레메트리 컴포넌트가 그 역할을 했습니다. 그것이 제거되면서 생성 지점이 프록시로 옮겨왔습니다. 운영에서는 이 이동으로 메트릭이 나오는 데 컨트롤 플레인 쪽 컴포넌트가 관여하지 않게 됐습니다. 지표가 안 보이면 istiod가 아니라 그 프록시를 봐야 합니다.

Mixer가 제거된 릴리스는 이 문서에서 확정하지 않습니다. 참고한 자료들이 Istio 1.4와 1.5를 엇갈려 지목합니다. 사내 문서에 버전을 적어야 한다면 istio.io의 해당 릴리스 upgrade/change notes 원문에서 직접 대조할 것. 확실한 것은 제거됐다는 사실과 지금의 생성 지점이 프록시라는 것뿐입니다.

이 확장들이 만드는 것을 조정하는 창구는 Telemetry API입니다. 레퍼런스의 자기 정의가 그대로 들어맞습니다 — “Telemetry defines how telemetry (metrics, logs and traces) is generated for workloads within a mesh.” 워크로드·네임스페이스·메시 전역 계층으로 적용됩니다. 06의 카디널리티 정리 작업이 건드리는 대상도 이 절의 stats 필터입니다.

3. CRD → Envoy 설정 번역표

두 번째 층은 런타임입니다. 아키텍처 문서가 번역의 주체와 시점을 한 문장으로 말합니다.

Istiod converts high level routing rules that control traffic behavior into Envoy-specific configurations, and propagates them to the sidecars at runtime.

xDS가 어떤 종류로 나뉘고 그 push가 왜 istiod CPU가 되는지는 02에 있습니다. 여기서는 어느 CRD가 어느 리소스가 되는가만 봅니다.

Istio CRD번역되는 Envoy 리소스무엇이 정해지나번역 결과 확인
VirtualServiceroute매칭 규칙, 가중치 분배, 재시도·타임아웃istioctl proxy-config route <pod>
DestinationRulecluster로드밸런싱, 커넥션 풀 상한, outlier detectionistioctl proxy-config cluster <pod>
Gatewaylistener받을 포트·프로토콜·호스트·TLSistioctl proxy-config listener <pod>
PeerAuthenticationtransport socket인바운드가 mTLS를 요구하는가listener·cluster 덤프를 -o json으로

istioctl proxy-config의 서브커맨드 이름부터가 그대로 route·cluster·listener입니다. 이 표는 거기서 나왔습니다. Istio가 만드는 설정을 Envoy 쪽 리소스 타입 이름으로 부르는 방식이 공식적으로 통용됩니다. “이 CRD가 정말 반영됐나"를 묻는 가장 짧은 답은 해당 서브커맨드를 한 번 치는 것입니다.

# "내 VirtualService가 반영됐나" — 매칭 규칙과 가중치가 route에 있는지
istioctl proxy-config route <pod>.<ns>

# "DestinationRule의 커넥션 풀·outlier가 걸렸나" — cluster 쪽을 본다
istioctl proxy-config cluster <pod>.<ns>

# "Gateway가 이 포트를 정말 열었나" — listener 목록
istioctl proxy-config listener <pod>.<ns>

# 필드 수준까지 봐야 하면(mTLS의 transport socket 등) JSON으로
istioctl proxy-config listener <pod>.<ns> -o json

순서가 곧 진단 순서이기도 합니다. route에 규칙이 없으면 VirtualService가 이 프록시까지 오지 않은 것입니다(11gateways 필드 실수가 대표적입니다). route는 맞는데 동작이 다르면 그 다음 층인 cluster를 봅니다.

DestinationRule이 특히 12와 직결됩니다. 레퍼런스의 정의는 “DestinationRule defines policies that apply to traffic intended for a service after routing has occurred"입니다. 거기서 구성하는 것이 load balancing·connection pool·outlier detection입니다. 12에서 Envoy의 능력으로 소개한 서킷브레이커와 outlier detection은 새로 만들어지는 기능이 아닙니다. 이미 프록시에 있는 기능을 켜는 스위치이고 그 스위치가 DestinationRule입니다. 라우팅이 끝난 뒤에 적용된다는 순서까지 정의에 들어 있습니다. 어느 cluster로 갈지는 route가 정하고 그 cluster를 어떻게 다룰지는 DestinationRule이 정합니다.

이 표의 두 칸은 방증입니다. ① ‘VirtualService→route, DestinationRule→cluster, Gateway→listener’를 1:1로 명시한 istio.io 문장은 찾지 못했습니다. 개념 문서(architecture · traffic-management · destination-rule)와 istioctl proxy-config 서브커맨드 이름을 겹쳐 얻은 대응입니다. ② PeerAuthentication이 Envoy의 transport socket으로 번역된다는 것도 마찬가지입니다. ’transport socket’(envoy.transport_sockets.tls)은 envoyproxy.io 쪽 용어입니다. istio.io는 “Envoy requests the certificate and key from the Istio agent… via the SDS API”, “client side Envoy starts a mutual TLS handshake with the server side Envoy"까지만 말합니다. 필드 이름 수준의 연결은 실제 설정 덤프로 확인해야 합니다.

4. 버전 결합 — 사이드카 업그레이드가 곧 Envoy 업그레이드

1절의 ENVOY_SHA는 운영에서 이렇게 작용합니다. istio/proxy가 Envoy를 커밋 단위로 pin해서 빌드하므로 Istio 릴리스 하나는 Envoy 커밋 하나에 묶입니다. 결과는 단순합니다.

  • 사이드카 이미지 태그를 올리면 Istio 버전만 올라가지 않습니다. 데이터 플레인의 Envoy가 통째로 교체됩니다.
  • 그러니 Envoy 쪽 동작 변화(설정 필드 deprecation, 필터 이름 변경 등)는 컨트롤 플레인을 올릴 때가 아니라 워크로드 재시작 시점에 나타납니다.
  • 08이 “EnvoyFilter는 업그레이드에 깨진다"고 한 근거가 여기입니다. 날것의 Envoy 설정은 pin된 그 커밋의 스키마에 결합돼 있습니다.

그 Envoy 버전이 무엇인지는 릴리스 노트에서 찾을 수 없습니다. istio/istio 이슈 #43140이 “릴리스 노트에 Envoy 버전을 적어달라"고 요청했습니다. 결론은 릴리스 노트를 고치는 대신 진단 문서에 조회 방법을 추가하는 쪽이었습니다. 실제로 1.22·1.24·1.30 announcing 페이지 본문에는 Envoy 버전 숫자가 없습니다.

공식이 안내하는 방법은 돌고 있는 파드에 직접 묻는 것입니다.

kubectl exec <pod> -c istio-proxy -- pilot-agent request GET server_info

운영 관점에서 이건 오히려 나은 계약입니다. 문서에 적힌 숫자 대신 실제로 그 파드에서 돌고 있는 것을 읽기 때문입니다. 사이드카 이미지가 섞여 있는 클러스터(revision 카나리 중이거나, 재시작이 밀린 워크로드가 남았거나)에서는 “우리 메시의 Envoy 버전"이 단수가 아닙니다.

5. 확장의 두 계층 — 어디까지가 이 문서인가

지금까지 본 확장은 전부 이미지 안에 들어 있습니다. 사용자가 켜고 끄는 대상이 아닙니다. 그 proxyv2를 쓰는 이상 이미 거기 있습니다. 반대편에 08이 다루는 계층이 있습니다.

내장 확장 (이 문서)사용자 확장 ([08])
무엇metadata exchange · stats · ALPNEnvoyFilter 패치 · WasmPlugin
언제 들어가나istio/proxy 빌드 시점(컴파일)런타임(xDS로 내려가는 설정)
바꾸는 방법이미지 교체 = Istio 업그레이드CRD 수정
조정 창구Telemetry API로 동작만 조정CRD 자체가 곧 정의
신규 추가워크그룹 심사(core API 여부)사용자 재량

경계는 이렇게 읽으면 됩니다. 내장 확장은 버전의 문제이고 사용자 확장은 설정의 문제입니다. 표준 메트릭이 이상하면 되돌릴 레버는 CRD 쪽에 없습니다. 이미지 버전과 Telemetry API를 봐야 합니다. EnvoyFilter로 추가한 것이 깨졌다면 원인은 대개 4절의 결합입니다. 이미지가 바뀌면서 그 아래 Envoy 스키마가 같이 바뀐 것입니다.

이 경계가 [08]의 선택 사다리에 한 칸을 더해 줍니다. 표준 CRD → 상위 API → EnvoyFilter라는 순서 앞에, “이미 프록시 안에 있는 것으로 되는가"를 먼저 묻는 칸입니다. 관측성 요구의 상당수가 여기서 끝납니다. 이미 도는 stats 필터를 Telemetry API로 조정하면 되지, 새 필터를 붙일 일이 아니기 때문입니다.

이 문서에서 가져갈 것

  • Istio가 Envoy를 “쓴다"는 것은 커밋을 pin해서 자기 확장과 함께 다시 빌드한다는 뜻입니다. proxyv2는 그 결과물과 pilot-agent를 한 이미지에 담은 것입니다. 엔트리포인트는 pilot-agent입니다.
  • 표준 메트릭과 그 source_*/destination_* 라벨은 프록시 안의 확장이 만듭니다. 라벨을 채우는 재료는 mTLS 위에서 교환되는 피어 메타데이터입니다. mTLS가 없으면 라벨도 없습니다.
  • CRD는 xDS 리소스로 번역됩니다. 반영 여부를 묻는 가장 짧은 답은 istioctl proxy-config route|cluster|listener입니다. DestinationRule12의 기능들을 켜는 cluster 쪽 스위치입니다.
  • 사이드카 업그레이드는 Envoy 업그레이드입니다. 실제 버전은 릴리스 노트에 없습니다. pilot-agent request GET server_info로 파드에 물어봅니다.

소스

마지막 수정 일자