Pod 4,000개에 Sidecar를 붙이면 메모리 240Gi가 증발한다 — 채널톡이 Ambient mode를 선택한 이유
![]()
Service mesh. 개발자라면 한 번쯤은 "도입할까 말까" 고민해봤을 거예요. 채널코퍼레이션 DevOps팀도 마찬가지였는데, 여러 해 동안 매번 "지금은 아니다"로 미뤄왔다고 해요. 그러다 2025년 초에 드디어 시작했고, 3월부터 11월까지 약 8개월에 걸쳐 프로덕션에 Istio를 올렸습니다.
근데 그냥 Istio가 아니에요. Sidecar mode가 아닌 Ambient mode를 골랐거든요. 2024년 말 Istio 1.24에서 GA된 지 얼마 안 된 따끈따끈한 녀석을. 왜 그랬을까요?
Sidecar 모드의 수학이 안 맞는 순간
개별 기능만 놓고 보면 Service mesh 없이도 버틸 수 있어요. APM으로 네트워크 가시성 확보하고, Ingress 수준에서 카나리 배포 하고, 애플리케이션 레벨에서 rate-limit 걸면 되니까요. 근데 서비스 수가 계속 늘어나면 이야기가 달라집니다. 채널팀이 Service mesh에 기대했던 건 크게 두 가지 — L7 네트워크 가시성과 정교한 카나리 배포. 여기에 Distributed Tracing, Traffic Management, Circuit Breaking, mTLS까지 확장할 수 있다는 점이 매력적이었어요.
솔루션 선택은 단순했습니다. Linkerd는 가볍고 단순하지만 레퍼런스가 적었고, Cilium은 eBPF 기반 CNI로서의 성격이 더 강했어요. (채널팀은 CNI로 다른 솔루션을 쓰고 있었거든요.) Istio는 커뮤니티와 생태계가 가장 크고, 문제가 생겼을 때 함께 고민할 사람이 가장 많았습니다. 인프라의 핵심 요소니까, 이 기준이 결정적이었죠.
자, 그러면 Sidecar냐 Ambient이냐.
Sidecar mode에서는 Pod 하나당 Envoy proxy가 하나씩 붙어요. 채널팀은 약 4,000개의 Pod을 운영하고 있습니다. Istio 공식 기준으로 Envoy sidecar의 리소스 사용량이 1,000 RPS 기준 약 0.2 vCPU, 60Mi 메모리예요. 채널팀이 직접 측정한 수치는 더 높았는데 — idle 상태에서도 0.05~0.01 vCPU에 60Mi 메모리, 2,000 RPS 기준으로는 0.8~1.2 vCPU에 300~500Mi 메모리를 먹었어요.
4,000개 Pod에 전부 sidecar를 붙이면? idle 상태에서만 약 240Gi 메모리가 순수하게 프록시에 소모돼요. 서버 늘리는 비용이 아니라 프록시 유지 비용. 이건 좀 말이 안 됩니다.
Ambient mode가 뒤집어엎은 세 가지

Ambient mode는 ztunnel(노드당 1개, L4 처리)과 waypoint(namespace/service 단위, L7 처리)로 구성돼요. Pod마다 proxy를 붙이는 대신, 노드 단위로 묶는 거죠. 여기서 세 가지가 바뀝니다.
첫째, Control Plane 확장성 문제가 해소돼요. Sidecar mode에서는 모든 sidecar가 mesh 내 다른 모든 destination의 정보를 알아야 해요. destination 설정이 바뀌면 모든 sidecar에 한꺼번에 전파해야 하고, 클러스터가 커질수록 istiod의 부담이 비선형적으로 증가합니다. "전파해야 할 설정 크기 x 전파 대상 수"가 polynomial scaling problem으로 귀결되는 거예요.

Ambient mode에서도 ztunnel은 mesh 전체 정보를 알아야 하지만, 핵심은 단순히 전파 대상 수가 줄어드는 게 아니라 이 polynomial scaling 자체를 해결할 수 있다는 점이에요.
둘째, 리소스 절약. 앞서 말한 240Gi 이야기. Ambient mode에서는 ztunnel이 노드당 하나, waypoint가 namespace나 service 단위로 존재합니다. 1,000 RPS 기준 ztunnel 하나가 약 0.06 vCPU, 12Mi 메모리를 쓰니까 sidecar 대비 5분의 1 수준이에요. 노드 수가 늘어나면 ztunnel도 늘지만, Pod 수에 1:1로 비례하는 Sidecar보다는 증가폭이 훨씬 완만하죠.
셋째, K8s Gateway API 지원. 기존 Ingress 리소스의 업데이트가 freeze되고 Gateway API가 새 표준으로 자리잡고 있는데, Ambient mode의 공식 문서와 예제가 처음부터 Gateway API 기준으로 작성돼 있었어요. 함께 도입하기에 자연스러웠다고 합니다.
단점은 알고 골랐다
솔직히 파격적인 리소스 절약 외에는 Sidecar mode가 낫다고 채널팀도 인정해요.
장애 영향 범위가 넓어집니다. Sidecar에서는 proxy가 Pod과 lifecycle을 같이해서, 장애가 각 Pod에 그쳤어요. Ambient에서는 ztunnel이 노드 단위, waypoint가 namespace 단위이므로 하나가 죽으면 노드 전체 혹은 namespace 전체로 번져요. 이전에 없던 SPoF(Single Point of Failure)가 생긴 셈이죠.
디버깅 난이도도 올라갑니다. ztunnel, waypoint, HBONE 같은 새 개념을 익혀야 하고, proxy와 hop이 늘어나면서 문제 추적이 까다로워져요. GA 직후라 프로덕션 검증 사례도 적었고요.
팀 내 논의에서 나온 말들이 인상적이에요.
"2~3년 동안은 건드리지 않을 걸 선택하고 싶음."
"네트워크 트래픽에 대한 조작과 가시성이 핵심임. 그것만 잘 되면 Ambient도 상관없음."
"문제 발생 빈도보다는 장애 복구 속도에 집중해야 함."
결국 Sidecar로 시작한 뒤 나중에 Ambient으로 마이그레이션하는 상황을 피하고 싶었던 거예요. 차라리 팀 내 Istio와 Envoy 이해도를 높이면서 신중히 리서치하고, 처음부터 Ambient으로 가자. 그런 판단이었습니다.
HBONE — HTTP/2 + CONNECT + mTLS, 이름만 복잡하다

Ambient mode의 동작 원리를 이해하려면 HBONE이라는 걸 알아야 해요. HTTP-Based Overlay Network Environment. 이름만 들으면 뭔가 엄청난 프로토콜 같지만, 실체는 Envoy의 기존 기능들을 조합한 것에 가깝습니다.
HTTP CONNECT 메서드로 터널을 열고, 그 위에 TLS를 씌운 것. 이게 HBONE의 전부예요. HTTP/2, HTTP CONNECT, mTLS — 이 세 가지 표준을 포함하고 있어요. 이미 검증된 표준들을 Envoy config로 조립한 것이라고 이해하면 됩니다.

HBONE 이전에는 Istio가 트래픽에 고유 메타데이터를 삽입해야 했어요. 하지만 HBONE은 애플리케이션 트래픽의 원본 상태를 전혀 변경하지 않고도 프록시 처리를 수행할 수 있습니다.
다만 양날의 검이에요. TCP connection reset 이슈를 추적할 때, ztunnel·pod·waypoint에서 tcpdump를 떠봤지만 HBONE 구간에서는 암호화된 TLS 내용만 보였다고 해요. destination 측에서 모든 네트워크 인터페이스를 대상으로 캡처해야 ztunnel이 복호화한 트래픽을 확인할 수 있었다는데 — 이건 후속 편에서 다룬다고 합니다. (이 부분이 제일 궁금하긴 해요.)
ztunnel과 waypoint가 트래픽을 나눠 먹는 법

Control Plane(istiod)은 data plane에 설정을 전파하는 역할을 해요. Ambient mode에서 ztunnel과 waypoint는 data plane 컴포넌트로서 istiod와 xDS API를 통해 통신하며 클러스터 상태를 전달받습니다. ztunnel은 서비스 간 mTLS 통신을 위해 인증서도 받고요.

Data plane은 크게 세 가지 경우로 나뉘어요.
Mesh에 속하지 않는 경우 — 기존 쿠버네티스 네트워크(via kube-proxy)와 동일합니다. 아무 변화 없어요.

Mesh에 속하는 경우 — Pod에서 나가는 트래픽은 ztunnel로 transparently redirect되고, destination이 mesh에 포함된 경우 암호화된 HBONE 채널을 통해 보내져요. 들어오는 트래픽도 해당 node의 ztunnel을 거쳐서 들어옵니다. AuthorizationPolicy에 위배되지 않는 한, 요청이 성공적으로 들어오고요. mTLS를 강제하는 만큼, source와 destination 모두 고유한 x509 인증서를 가지고 있고, ztunnel은 자신이 속한 node의 workload들의 certificate를 이용합니다.

Mesh에 속하며 Waypoint가 설정된 경우 — waypoint의 범위에 적용되는 트래픽은 모두 waypoint를 거쳐가요. L7 policy(AuthorizationPolicy, RequestAuthentication, WasmPlugin, Telemetry 등)를 적용하는 거죠. ztunnel과 달리 waypoint proxy는 source/destination pod와 같은 node에 존재하지 않을 수 있다는 점이 중요해요.

Traffic Redirection — iptables가 다 해먹는다
istio-cni가 삽입한 iptables 규칙으로 pod의 TCP 패킷이 15006/15008/15001 포트로 리다이렉트돼요. Sidecar mode에서 sidecar가 트래픽을 가로채는 것처럼, Ambient mode에서는 모든 트래픽이 ztunnel로 리다이렉트됩니다.
여기서 주의할 점. ztunnel이 우회되면 mesh에서 설정한 모든 Authorization policy도 무시돼요. 보안과 직결되는 문제.

ztunnel은 istio-cni pod의 도움으로 workload pod의 모든 트래픽을 수신할 수 있게 됩니다. istio-cni node agent가 pod 생성/삭제 같은 CNI events에 반응하며, pod의 네트워크 규칙을 변경해서 node-local ztunnel로 트래픽이 리다이렉트되도록 해요. 리다이렉트는 모두 Pod network 안에서 이루어지고, host(node) side에서는 안 일어납니다.

여기서 짚고 넘어갈 게 있어요. 공식 문서에 "in-pod ztunnel"이라는 표현이 나오는데, ztunnel은 workload pod과 별개의 DaemonSet 컨테이너예요. 실제로는 istio-cni가 iptables에 주입하는 규칙이 Pod의 container network namespace 안에 생성된 TCP socket(localhost의 port 15001/15006/15008)으로 REDIRECT하는 것이고, 이 socket이 노드의 ztunnel DaemonSet과 연결되어 있기 때문에 "in-pod ztunnel port"라는 표현이 가능한 거예요. traffic redirection의 실제 대상은 ztunnel 컨테이너가 아니라 ztunnel과 연결된 Pod 내부 socket입니다.
또 하나. ztunnel과 istio-cni는 항상 Running 상태여야 해요. istio-cni가 아직 준비되지 않은 상태에서 Pod이 스케줄되면, 해당 Pod이 mesh에 불완전하게(partially) 참여하게 될 수 있거든요. untaint-controller로 이 문제를 방지할 수 있다는데, 구체적인 내용은 후속 편에서 나온다고 합니다.
리소스 절약 vs 장애 반경, 어디에 배팅할 것인가
이 시리즈의 첫 편인 만큼, 의사결정 과정과 Ambient mode의 개념 수준 동작 원리에 집중한 글이에요. 후속 편에서는 ztunnel과 waypoint의 Envoy config를 직접 들여다보고, 프로덕션에서 만난 트러블슈팅 이야기가 나온다고 해요.
Sidecar mode가 더 성숙한 건 맞아요. 하지만 4,000개 Pod에 sidecar를 붙이는 비용, 그리고 앞으로 서비스가 더 커질 걸 생각하면 — 채널팀의 선택이 납득이 가요. 결국 이건 "지금의 안정성"과 "미래의 확장성" 사이에서 어디에 배팅할 것인가의 문제니까요.
Envoy config 편이 나오면, 그때가 진짜 본론이겠죠.