1. Ambient로 옮기기 전에 확인할 것
Ambient mode가 Istio 1.24에서 GA로 찍힌 게 작년 말이다. 그동안 1.25, 1.26을 거치면서 production 사용 사례도 꽤 쌓였고, 2026년 초부터는 "이제 슬슬 옮겨도 되겠다"는 분위기가 우리 팀 안에서도 잡혔다. 사이드카가 안 좋아서가 아니라, Pod 하나당 envoy 컨테이너 하나씩 붙는 모델이 노드 수 200대 넘어가면서 점점 답답해졌다. 메모리 사용량의 상당 부분이 사이드카 envoy였고, 무엇보다 사이드카 버전 올릴 때마다 전체 Pod restart가 필요한 게 가장 큰 문제였다.
이 글은 "Ambient 좋다, 옮겨라"는 류의 이야기는 아니다. 어떻게 끊김 없이 옮길지에 대한 실전 절차를 정리한다. 단번에 바꾸지 않는다. 클러스터 안에 사이드카와 ambient가 공존하는 기간을 길게 가져갔고, 그 과정에서 만난 함정들도 같이 적는다.
옮기기 전에 확인할 것
먼저 현재 사이드카 mesh에서 쓰고 있는 기능을 목록으로 만들었다. Ambient는 기본적으로 L4 기능(mTLS, L4 인가)은 ztunnel이 처리하고, L7 기능(라우팅, 헤더 기반 권한, 트래픽 스플릿 등)은 waypoint proxy가 따로 처리하는 구조다. 즉, 모든 namespace에 waypoint를 다 띄우면 비용 절감 효과가 죽는다.
체크리스트는 단순했다.
- VirtualService에서 헤더 기반 라우팅, 가중치 트래픽 스플릿, prefix 매칭 같은 L7 라우팅을 쓰는 namespace는?
- AuthorizationPolicy에서 HTTP method, path 기반 규칙 쓰는 namespace는?
- 외부 서비스 호출 시 EnvoyFilter로 헤더 가공하는 곳은?
- Telemetry API로 메트릭 dimension 커스터마이징 하는 곳은?
이 네 가지에 해당하지 않는 namespace가 의외로 많았다. 단순히 mTLS와 L4 deny 정책만 필요한 백엔드들. 우리 클러스터 기준으로 약 60% 정도의 namespace가 L4-only였다. 이들은 waypoint 없이도 그대로 작동한다. 나머지 40%만 waypoint를 붙이면 됐다.
1단계: 컨트롤 플레인 업그레이드
Ambient를 쓰려면 컨트롤 플레인이 Ambient profile로 설치돼 있어야 한다. 우리는 기존 사이드카 컨트롤 플레인이 1.22였는데, 두 번에 나눠 올렸다. 1.22 → 1.24(여기서 ambient GA) → 1.26. 한 번에 두 단계 이상 건너뛰지 말라는 게 공식 권고다.
핵심은 istioctl install 할 때 ambient profile을 지정하되, 기존 sidecar injection은 그대로 살려두는 것이다.
istioctl install --set profile=ambient \
--set components.cni.enabled=true \
--set values.cni.ambient.enabled=true \
--set values.cni.cniBinDir=/opt/cni/bin \
--set values.cni.cniConfDir=/etc/cni/net.d
이 시점에서 클러스터에는 istiod, istio-cni-node DaemonSet, ztunnel DaemonSet이 같이 떠 있게 된다. 기존 사이드카가 주입된 Pod들은 그대로 사이드카로 동작한다. ztunnel은 ambient label이 붙은 namespace의 Pod 트래픽만 가로챈다. 두 모드가 같은 클러스터에서 공존하는 게 핵심이다.
설치 직후 반드시 확인할 것:
kubectl get pods -n istio-system
# istiod-xxx, istio-cni-node-xxx (per node), ztunnel-xxx (per node)
kubectl logs -n istio-system ds/ztunnel | head -50
# "connected to xds" 라인이 보여야 정상
2단계: L4-only namespace부터 옮기기
여기서 한 가지 함정이 있다. ambient로 옮기려면 해당 namespace에서 사이드카 주입 label을 떼고 ambient label을 붙여야 한다. 그런데 이미 떠 있는 Pod들의 사이드카는 안 사라진다. 새 Pod부터 사이드카 없이 뜬다. 즉, 점진적 전환은 rolling restart 한 번이 필요하다.
가장 안전한 순서는 이렇다. 먼저 가장 트래픽 작고 영향도 낮은 namespace 하나를 골랐다. 우리는 내부 admin 도구가 있는 namespace로 시작했다.
# 1. 기존 사이드카 주입 label 제거
kubectl label namespace internal-admin istio-injection-
# 2. ambient label 추가
kubectl label namespace internal-admin istio.io/dataplane-mode=ambient
# 3. 해당 namespace Deployment rolling restart
kubectl rollout restart deployment -n internal-admin
이 상태에서 새로 뜬 Pod에는 사이드카 컨테이너가 없다. 트래픽은 ztunnel을 거쳐 다른 Pod로 흘러간다. mTLS는 자동으로 적용된다 (HBONE 프로토콜).
확인하는 가장 빠른 방법은 ztunnel 로그를 보는 것이다.
kubectl logs -n istio-system ds/ztunnel | grep <pod-ip>
# inbound/outbound 트래픽이 잡혀야 정상
여기서 한 가지 자주 놓치는 것: 같은 namespace 안에 사이드카 있는 Pod와 ambient Pod가 섞이면 곤란하다. 짧게는 괜찮은데 길게 두면 의도하지 않은 정책 경로가 생긴다. namespace 단위로 전환하고 그 안의 모든 Deployment를 한 번에 restart 하는 게 깔끔하다.
3단계: L7 기능이 필요한 namespace에 waypoint 추가
이게 ambient의 진짜 다른 점이다. 사이드카 시절에는 L7 라우팅이 그냥 됐다. 사이드카 envoy가 다 처리했으니까. ambient에서는 ztunnel은 L4까지만 본다. L7이 필요하면 waypoint proxy라는 별도의 Pod를 띄워야 한다.
waypoint는 namespace 단위로 띄울 수도 있고, 특정 service나 ServiceAccount만 대상으로 띄울 수도 있다. 우리 팀은 namespace 단위로 시작했다. 단순하고 디버깅하기 쉽다.
# namespace 단위 waypoint
istioctl waypoint apply -n payment --enroll-namespace
이 명령은 내부적으로 Gateway API의 Gateway 리소스를 만든다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: waypoint
namespace: payment
labels:
istio.io/waypoint-for: service
spec:
gatewayClassName: istio-waypoint
listeners:
- name: mesh
port: 15008
protocol: HBONE
기존 사이드카 시절에 만들어둔 VirtualService, DestinationRule은 대부분 그대로 동작한다. 단, 한 가지 주의할 점이 있는데, AuthorizationPolicy의 selector 동작이 사이드카 시절과 미묘하게 다르다. waypoint를 거치는 트래픽은 waypoint Pod의 ServiceAccount로 인가가 평가된다. 즉, 사이드카 시절에 워크로드 ServiceAccount 기준으로 만들어둔 정책 일부는 다시 봐야 한다.
우리는 정책 검증을 위해 istioctl analyze를 활용했다.
istioctl analyze -n payment --use-kube
# AuthorizationPolicy, VirtualService 호환성 경고 확인
4단계: 사이드카와 ambient 사이의 통신
마이그레이션 중간 단계에서 가장 신경 쓰이는 부분이다. 사이드카 namespace의 Pod와 ambient namespace의 Pod가 서로 호출하는 경우다. 결론부터 말하면, 이건 그냥 된다. mTLS도 자동으로 적용되고 정책도 적용된다. 단, 디버깅이 평소보다 어렵다.
문제가 생겼을 때 흐름을 추적하는 게 까다로워서, 우리는 마이그레이션 기간 동안 Hubble은 아니지만 Istio의 access log를 양쪽에서 모두 켜뒀다.
apiVersion: telemetry.istio.io/v1
kind: Telemetry
metadata:
name: access-log-all
namespace: istio-system
spec:
accessLogging:
- providers:
- name: envoy
ambient namespace에서는 ztunnel과 waypoint의 로그를 따로 봐야 한다. ztunnel은 L4 connection 단위 로그, waypoint는 L7 HTTP request 단위 로그가 나온다. 사이드카 시절보다 로그를 두 군데서 봐야 해서 처음엔 좀 헷갈렸다.
마이그레이션 중 만난 문제 두 가지
문제 1: PeerAuthentication mode가 STRICT인 namespace에서 사이드카 → ambient 전환 후 일시적 통신 불가.
원인은 ztunnel과 사이드카가 동일한 워크로드를 동시에 처리하려고 한 짧은 순간이 있었기 때문이다. CNI 플러그인이 iptables 룰을 갱신하는 데 시간이 걸리고, 그 사이에 일부 connection이 lost 된다. 해결책은 단순하다. rolling restart의 maxSurge를 25%에서 0%로 잠시 낮추고, maxUnavailable을 25%로 두면 한 번에 너무 많은 Pod가 동시에 갈아엎히지 않는다.
문제 2: waypoint Pod의 CPU 스파이크.
특정 namespace에서 트래픽이 몰릴 때 waypoint Pod 하나가 CPU 한계까지 치는 일이 있었다. waypoint는 namespace 안의 모든 L7 트래픽을 받기 때문에 사실상 사이드카 시절의 envoy 트래픽이 한 곳으로 집중된 형태다. 처음에는 replicas 1로 띄웠는데, 트래픽 큰 namespace는 2~3 replicas + HPA로 바꿔야 했다.
kubectl scale deployment waypoint -n payment --replicas=3
kubectl autoscale deployment waypoint -n payment --min=2 --max=8 --cpu-percent=70
waypoint를 어디까지 띄울지가 결국 ambient의 핵심 운영 포인트다. 너무 잘게 쪼개면 관리 부담이 늘고, 너무 크게 묶으면 단일 장애점이 된다. 우리는 같은 도메인 서비스를 묶어서 namespace당 1개 waypoint, 트래픽 1만 RPS 이상인 곳은 3 replicas로 맞췄다.
비용 측면에서
마이그레이션 두 달이 지난 시점의 숫자다. 200대 노드 클러스터 기준:
- 사이드카 시절 mesh proxy 전체 메모리 사용량: 약 320GB
- ambient 전환 후 ztunnel(노드당) + waypoint들 합계: 약 95GB
약 70% 줄었다. 90% 절감이라는 공식 자료보다는 보수적인데, 우리는 waypoint를 꽤 많이 띄운 편이다. L7 기능을 쓰는 namespace 비중이 높으면 절감 폭이 줄어든다.
업그레이드 비용도 줄었다. 사이드카는 Istio 마이너 버전 올릴 때마다 모든 Pod restart가 필요했는데, ztunnel은 DaemonSet이라 노드 단위 rolling으로 끝난다. waypoint도 Deployment라 일반적인 rolling update 절차로 처리된다.
마이그레이션을 멈춘 namespace도 있다
전부 옮긴 건 아니다. 특정 레거시 namespace 두 곳은 사이드카로 남겼다. 둘 다 EnvoyFilter를 거의 envoy 내부 동작 패치 수준으로 쓰고 있었고, 이걸 waypoint로 옮기는 게 시간 대비 효용이 떨어진다고 판단했다. 사이드카와 ambient가 같은 클러스터에서 공존 가능한 게 ambient의 가장 큰 장점 중 하나다. 모든 걸 한 번에 바꾸지 않아도 된다.
여기까지가 우리 팀의 마이그레이션 절차다. 핵심은 단번에 옮기지 않는다는 점, 그리고 L4-only namespace부터 옮긴 뒤 waypoint가 필요한 곳을 천천히 추가한다는 점이다. Ambient는 GA가 됐다고 해서 모든 사이드카 기능을 1:1로 대체하지는 않는다. 워크로드별로 어디까지 L7이 필요한지 한 번 정리하고 시작하면 옮기는 과정이 훨씬 매끄럽다.
다음 글에서는 waypoint 옆에 ext-authz를 붙여 인가 위임하는 패턴을 다뤄볼 생각이다. 혹시 비슷한 마이그레이션 중인 팀 있으면 어떤 함정 만나셨는지 댓글로 공유 부탁드린다.
'IT > 기타' 카테고리의 다른 글
| Istio sidecar에서 ambient로 옮기다 일주일을 날렸다 (0) | 2026.05.30 |
|---|---|
| Linkerd 2.18을 다시 봤지만, 결국 옮기지 않은 이유 (0) | 2026.05.25 |
| Kafka 3.9를 다리 삼아 KRaft로 넘어가는 법 (0) | 2026.05.14 |
| Istio Ambient vs Cilium Service Mesh, 우리는 뭘 쓰고 있나 (0) | 2026.05.06 |
| Istio Ambient vs Sidecar, 6개월 검토 후 결론 (0) | 2026.04.27 |