카테고리 없음

Istio Hands-on Study [1기] [9주차] Ambient Mesh

juyeon22 2025. 6. 9. 00:11
CloudNet@ 가시다님이 진행하는 Istio In Action 책 기반 Hands-on Study  

Extension AmbientMesh

목차

  • 1.Istio Blog 살펴보기

  • 2.Jimmy Song님 Blog 살펴보기

  • 3.실습

1. Istio Blog 살펴보기

여태까지 저희는 실습을 Istio를 SideCar Mode로 사용해왔습니다. 그런데 2022년도에 AmbientMesh라는 개념이 Istio에 도입이 되면서 SideCar를 사용하는 방식이 아닌 다른 방식이 추가 됐습니다.

Istio 블로그를 통해 AmbientMesh가 무엇인지, 그리고 SideCar와 어떤 차이가 있는지 알아보겠습니다.

Introducing Ambient Mesh

Ambient Mesh 란 Service Mesh 환경에서 SideCar를 사용하지 않는 Data Plane 모드를 뜻합니다. SideCar를 사용하지 않고 통합된 Data Plane을 사용하여 Istio의 기능을 사용 합니다.

SideCar Mode를 사용하게되면 Istio 업그레이드를 하게 되는경우 모든 SideCar가 적용된 pod들을 재시작을 해야하는데, Ambient Mesh를 사용하면 Proxy가 분리돼있어, 업데이트 시 애플리케이션 작동에 영향을 주지 않습니다.

Figure 1.1 SideCarMode와 AmbientMesh Mode 비교

Ambient Mesh를 사용하게되면 Proxy의 모든 기능을하는 SideCar를 사용하는 대신 L4,L7 Proxy로 나눠서 사용할 수 있습니다.

Figure 1.2 ztunnel 설명

L4, L7 Proxy로 나눠서 사용할 수 있다고 설명을했는데, L4 Proxy는 ztunnel로 구현을 하고 L7 Proxy는 Waypoint Proxy로 구현을 합니다.

Figure 1.3 waypoint proxy pod 설명

Figure 1.4 ztunnel과 waypoint 설명

앞에서 언급한 HBONE이란 HTTP Based Overlay Network Encapulstaion의 약자로, HTTP Connect 메소드를 통해, HTTP 터널링을 사용합니다. 통신 시 HTTP/2를 사용하며 TLS를 통해 인증 및 암호화를 적용할 수 잇으며 TCP 15008포트를 사용합니다.

Figure 1.5 HBONE 설명

ztunnel을 사용하게되면, 기존에 istiod가 sidecar에 설정을 Injection하는 것처럼 Istiod가 ztunnel pod에 설정을 Injection을 하게됩니다.

Figure 1.6 ztunnel과 istiod와의 관계 설명

SideCar의 한계

SideCar Pattern을 사용하면 애플리케이션에 크게 변화를 주지않고 Istio의 이점을 사용할 수 있었지만, 애플리케이션과 Istio Data Plane을 완벽하게 분리하지 못하는 문제가 발생합니다.

이외에도 다른 제약들이 발생하는데 제약 내용은 다음과 같습니다.

  • 간섭(Invasiveness)

    • SideCar Pattern은 k8s pod의 spec을 수정하고 Appliction 에 주입을 해야하는데, SideCar와 관련된 작업을 하게되면, 애플리케이션에 영향을 주게됩니다.

    • 리소스 활용 저하

    • SideCar Pattern을 사용하게되면 SideCar가 배포된 pod의 Worst Case를 고려해서 리소스를 프로비저닝 하게 되는데, 이때 k8s클러스터의 리소스 활용도가 줄어들 수 있습니다.

    • 트래픽 차단

      • SideCar Pattern으로 배포된 Proxy가 수행하는 트래픽 캡처 및 HTTP 처리에는 리소스가 많이 소요되며, 애플리케이션을 중단할 수 있는 문제가 발생할 수 있습니다.

레이어 분리(L4, L7)

SideCar Pattern으로 Istio를 사용한 환경에서는 모든 Data Plane의 기능들을 SideCar에 구현을 했지만, 이렇게 구현을 했기때문에 운영 비용이 많이 발생 하여 Ambient Mode에서는 레이어를 분리하게 됐습니다.

Figure 1.7 분리된 레이어의 구현 설명

Ambient Mesh 동작 소개

Ambient Mesh는 각 노드에 DaemonSet으로 ztunnel pod Agent로 작동하며, 워크로드의 모든 트래픽을 Redirection 합니다.

ztunnel은 L7 처리를 하지 않기때문에 트래픽 처리시 SideCar Pattern 보다 훨씬 효율적으로 처리 할 수 있습니다.

L7처리가 필요한 경우에는 WayPoint를 사용하여 구현을 할 수 있고, 트래픽 용량에 따라, 자동으로 확장할 수 있어 SideCar보다 더 리소스를 절감 할 수 있습니다.

Node에서 L7처리가 없는 이유?

Node에서 L7처리를 하지 않는 이유는 L4 처리로 제한하여 취약점 노출을 줄이고, L4 기능 사용 시 L7 영역에서 처리하는 리소스 의 사용량이 적기 때문에 처리를 하지 않습니다.

추가된 홉에 대한 처리

WayPoint가 추가되어 Network의 홉이 추가될 수 있지만, Istio의 네트워크 지연시간은 Network자체에서 발생하는 갓이 아닌 주로 L7 처리 시 발생을 하기 때문에 L7 처리가 필요한 경우에만 WayPoint를 사용하기 때문에 전반적으로 Network 홉이 추가된다고 해도 지연 시간을 체감하지 않을 것 입니다.

Ambient Mode에서의 보안

SideCar Pattern으로 Service Mesh를 구성하게 되는 경우에는, 워크로드에 같이 배포가 되기 때문에 취약점이 추가적으로 노출될 수가 있는데, Ambient Mode에서는 ztuunel과 WayPoint를 사용하기 떄문에 워크로드와 Layer가 나눠져있어 보안정책을 보다 더 엄격하게 적용할 수 있습니다.

Introducing Rust-Based Ztunnel for Istio Ambient Service Mesh

Ambient Mesh를 위한 Proxy

ztunnel은 Ambient Mesh룰 위한 노드에서 사용되는 Proxy이며, Ambient Mesh 아키텍쳐에서 워크로드간 안전하게 연결하고 인증하는 업무를 수행합니다. 또한 HTTP 트래픽에 대해서 추가적인 작업을 수행하지 않고도 mTLS, L4 권한 부여와 같은 기능을 수행할 수 있습니다.

Envoy를 ztunnel로 사용하지 않는 이유

Envoy를 사용해서 ztuunel을 구현하지 않는 이유는 Envoy에서 제공하는 L7기능이나, Extension같은 요소들이 ztunnel에서는 필요없기 때문에 Envoy를 사용하지 않았습니다.

ztunnel 구성

ztunnel은 Envoy와 다르게 최소한의 리소스를 사용해서 작동을 하는 목적으로 만들어졌기때문에, 자체적인 Protocol을 사용을 했습니다.
xDS 전송 API를 사용해서 설정 값들이 전달이 되지만, 기존의 SideCar를 사용했을때에 비해 내용의 양이 훨씬 줄어들어 가볍게 구성돼있습니다.
ztunnel은 HTTPS 터널을 사용해서 사용자 요청은 전달합니다. Envoy도 터널링 기능을 지원하지만 요청 수락 및 요청 전송 간 필터를 통해 구현이 되는데, 이때 Envoy를 사용하여 ztuunel을 구현을 하게되면, 연결당 여러번의 필터를 반복해야 했기때문에 ztunnel에서는 Envoy와 다른 방식으로 구현을 했습니다.

Istio Ambient Waypoint Proxy Made Simple

WayPoint 구성

WayPoint는 Ambient Mesh 환경에서 L7 처리를 위해 사용되는 Envoy기반의 요소입니다. WayPoint는 Application 외부에서 실행 되기 떄문에 NameSpace기반으로도 구성할 수 잇으며, 서비스 계정별로도 구성을 할 수 있습니다.
L7 처리가 필요 없이 L4(ztunnel) 처리만 필요한 경우에는, WayPoint를 사용하지 않고 ztuunel만 사용하여 Service Mesh를 구성할 수 있습니다.

Ambient Mesh 구성과 SideCar Pattern 비교 분석

SideCar Pattern으로 구성된 Istio Service Mesh에서는 트래픽을 처리하는 정책은 Client Proxy에 의해 구현이 되지만, 보안 정책은 Server Proxy에 의해 구현이 되기떄문에 여러가지 문제가 발생합니다.

가장 큰 예시로는 Client의 SideCar들은 Server Proxy에 대한 정보를 알아야하고, 정보가 변경되는 경우 모든 SideCar에게 알려야하기 떄문에 많은 리소스 사용이 발생하게 됩니다.

이러한 문제가 있기 떄문에 Ambient Mesh에서 WayPoint는 통신 시 목적지 기반의 정책을 사용하기 때문에 트래픽 처리 시 필요한 xDS 설정의 갯수가 간단한 예시를 통해 확인하도 1/4로 감소한 것을 알 수 있습니다.

Figure 1.8 SideCar Pattern 사용 시 구성적용 갯수 예시

Ambient Mode Security Deep Dive

데이터 플레인에서 애플리케이션 분리

Ambient Mode에서는 기존에 설명을 한 것 처럼 구성요소들이 Applicaiton Pod에 SideCar Pattern으로 배포되는 것이 아닌 Applicatino 외부에 배포되어 보안의 경계가 L4, L7, Application으로 구분이 됐습니다. 이렇기 때문에 기존 SideCar Pattern으로 Service Mesh를 구성하게 됐을 때, Application에서 침해 사고가 발생하게되면 SideCar 관련 ID/Key값에 대한 비밀 정보가 노출될 수 있는데 Ambient Mesh에서는 Application, L4, L7이 나눠져 있기 떄문에 Applicatino 침해가 발생해도, 비밀 정보가 노출이 되지 않습니다. Envoy의 취약점이 대부분이 L7 처리에서 많이 발생을 했는데, SideCar에서는 L7 처리 기능을 사용하지 않아도 강제 적으로 L7 Proxy 기능이 적용 돼있었는데, Ambient Mesh에서는 L7 처리에 대해 관리가 어려운 경우에는 사용을 하지 않아 보안에 대한 취약점을 처음부터 제거할 수 있습니다.

Pushing L4 down into the CNI

Ambient Mesh의 L4요소(Ztunnel)는 DaemonSet 형식으로 각 Node별로 배포가 됩니다. Ztunnel은 Ambient Mesh에서 중요한 요소이기 떄문에 CNI, kubernetes, linux Kernel 요소와 동일한 수준으로 처리 되어야 합니다.
Ambient Mesh 내부 워크로드에서 발생하는 트래픽들은 ztunnel로 리다이렉션이 되고, ztunnel은 워크로드를 식별하고, mTLS 연길 시 워크로드를 나타내는 인증서를 선택 합니다.
ztunnel 인결시 사용하는 인증서는 각 pod별로 고유하며, pod가 노드에서 실행 중인 경우에만 발급이 됩니다.
이렇기 때문에 ztuunel에서 침해사고가 발생해도, 클러스터 전체에 대해서 침해사고가 발생하는 것이 아닌 침해사고가 발생한 범위는 ztunnel이 배포된 노드에 현재 예약된 포드의 자격 증명만 유출 됩니다.

Simplicity of operations is better for security

SideCar Pattern으로 Istio를 사용하다보면 워크로드 업그레이드 시 Applcation 다운을 고려해야하지만 Ambient Mesh를 사용하는 경우에는 Application과 Proxy가 명확히 구분 되어 있기 때문에 ztuunel, WayPoint 업그레이드를 일반적인 k8s 노드 패치 및 업그레이드와 같이 진행할 수 있습니다.

Maturing Istio Ambient: Compatibility Across Various Kubernetes Providers and CNIs

앰비언트 알파에서의 트래픽 리디렉션

초창기 Ambient Mesh Model에서는 워크로드 pod와 ztunnel pod간 트래픽을 투명하게 리다이렉션 하는 것에 중점을 두었는데 해당 문제가 다른 CNI과 충돌이 발생 했습니다. 해당 문제를 해결하기 위해 Istio가 겪은 과정들을 알아보겠습니다.

SideCar Mode와 다르게 Ambient Mesh에서는 NET_ADMIN, NET_RAW기능의 요구사항을 제거 한 클러스터에서 사용할 수 있는 CNI확장 모듈 개념인 istio-cni를 필수적으로 사용 했어야 합니다. Ambient Mesh에 Pod가 추가될 때마다 istio-cni는 Node 수준의 Network NameSpace를 통해 실행되는 ztunnel과 pod 간 나가고 들어오는 트래픽들에 대해서 리다이렉션을 구성 했습니다. 기본 CNI를 사용하여 Ambeient Mesh를 테스트를 수행 했을때 istio-cni 리다이렉션 하는 부분과 기본 CNI에서 사용되는 Network Policy가 적용되는 부분이 동일하기 떄문에, 충돌이 발생했습니다.

처음에는 eBPF를 사용해서 문제를 조치하려고 했지만 eBPF를 사용해서 표준화를 할 수 있는 방법이 없어 동일한 문제를 발생할 것이라고 예상했고, 비 ebPF CNI를 사용하게 될 경우 추가적인 문제가 발생할 것이라고 생각 했습니다.

그래서 Application Pod의 Network Namespace에서 리다이렉션을 구성하는 방식으로 조치를 하게 됩니다.

네트워크 네임스페이스에서 실행되는 Linux 프로세스가 다른 네트워크 네임스페이스 내에서 리스닝 소켓을 생성하고 소유할 수 있다는 것을 발견하여 해당 문제를 해결 하게 됩니다.

Pod의 Network Namespace를 공동으로 사용하는 ztunnel에 제공하여 ztunnel이 Pod Network Namespace 내부에서 리다이렉션 소켓을 시작하면서 Pod외부에서 실행을 하게 아키텍처를 변경합니다.

Figure 1.9 개량된 리다이렉션 아키텍처

Ambient Mesh에 도입된 새로운 아키텍처

지금 까지는 대략적인 문제 발생 및 문제 해결 방법에 대해서 설명을 했는데 정확히 어떻게 구현 했는지에 대해서 알아보겠습니다.

istio-cni agent는 Namespace에 istio.io/dataplane-mode=ambient, 라벨이 붙은 pod를 감지하여 Ambient Mesh에 해당 pod를 포함시킵니다.

istio-cni agent는 pod의 Network NameSpace에 들어가 pod Network Namespace 내부에 Network 리다이렉션 규칙을 설정하여 pod에 들어오고 나가는 패킷을 가로채서 잘 알려진 포트(15008, 15006, 15001)에서 노드 로컬 ztunnel 프록시 인스턴스로 투명하게 리다이렉션을 수행합니다. 그리고 Unix Domain Socket을 사용하여 ztunnel pod의 Network Namespace 내부에 Local Proxy 리스닝 포트를 설정해야한다고 알리고 Pod의 Network Namespace를 나타내는 저수준 리눅스 파일 Descriptor를 제공합니다.

Figure 1.10 Ambient Mesh에 pod가 추가 시 Application pod의 흐름

Figure 1.11 암호화 및 평문 트래픽 흐름

최종적으로 개선된 Ambient Mesh는 모든 트래픽 캡쳐와 리다이렉션이 pod내부의 Network NameSpace에서 발생하여 CNI의 충돌을 해결 하고 Ambient Mesh의 호환성을 크게 향상 시켰습니다.

2. Jimmy Song님 Blog 살펴보기

Istio CNI Unveiled: Streamlining Service Mesh Connectivity

Istio 네트워크 요구 사항 및 솔루션 개요

Istio에서 Service Mesh는 SideCar를 사용해서 Application의 트래픽을 가로채서 관리합니다.
이때 init-container를 사용하여 Application Pod에 Proxy를 주입하고 iptables 규칙을 이용하여 트래픽을 관리합니다.
iptables같은 네트워크 설정을 하게 되는경우 Container에서 고급 권한이 필요 했는데, 이때 Service Account에 NET_ADMIN 컨테이너 권한을 필수적으로 부여해야 했으며, 그로 인해 엄격한 보안정책을 적용할 수 없었습니다.
이런 문제를 해결하기 위햐 Istio CNI 플러그인을 사용하여 init-container의 의존도를 줄이고 k8s network 계층에서 직접 네트워크를 조작하는 방식으로 구현을 하려고 햇습니다.
앞에서 설명한 내용을 토대로 CNI와의 호환성 문제가 있었지만, 기존 네트워크 정책에 영향을 주지 않고 서비스 간의 트래픽을 잘 관리할 수 있도록 문제를 해결하여 사이드 카 없이 Service Mesh를 사용할 수 있게 됐습니다.

NET_ADMIN 권한에 대한 보안 고려 사항

NET_ADMIN 권한이란 컨테이너 환경에서 프로세스가 iptables 규칙을 수정하거나 network 인터페이스 구성 변경과 같은 작업들을 수행할 수 있는 권한을 뜻하며 해당 권한을 사용하게 되는 경우 과도한 권한 부여 문제가 발생하여 보안 취약점이 발생합니다.
Best Practice로는 필요한 경우에만 권한을 부여하고 기능 구현은 NET_ADMIN 권한을 사용하는 것이 아닌 k8s network policy로 사용합니다.

Istio CNI 플러그인의 작동 원리

Istio CNI 플러그인은 각 노드의 파일 시스템에 에이전트로 설치되는 바이너리 파일이고, CNI Agent 의 작동원리는 다음과 같습니다.

Figure 2.1 CNI Agent 작동원리

Istio CNI Node Agent

  • CNI 플러그인을 설치하며, 노드의 CNI 구성을 업데이트
  • Pod 이벤트를 Ambient 감시 서버와 동기화를 하고, Pod 내부에서 iptables를 구성합니다.

Istio Ambient Mode와 Kubernetes CNI 간 충돌 해결

Ambient Mesh는 모든 CNI에서 사용가능 하도록 설계되었으며, 기존 CNI 구성에 영향을 주지 않고 ztunnel을 사용하여 포드 내 트래픽 리디렉션을 투명하게 처리합니다.
istio-cni는 ztunnel을 통해 Istio Service Mesh를 통과하는 트래픽을 관리 하는데 중점을 두지만, 표준 CNI는 Pod에 표준화된 네트워크 액세스를 제공하는데 중점을 둡니다.
각각의 CNI들이 기능을 수행하다가 호환성 문제가 발생할 수 있는데, pod와 동일한 user-space에서 ztunnel을 실행하여 트래픽 리디렉션을 관리함으로써 CNI에 의해 수정된 커널 공간과의 충돌을 방지할 수 있습니다.

Figure 2.2 CNI 충돌해결을 위한 Process

Ambient CNI Agent는 Pod생성 시 발생하는 UDS 이벤트를 수신하여 ztunnel과 상호작용을 수행 합니다.

Ambient Watch Server는 배포되는 pod의 iptables를 수정하여, 트래픽을 ztunnel로 리다이렉션 합니다.

ztunnel은 Kubernetes 클러스터 내에서 연결을 설정하고 트래픽 리다이렉션 처리 합니다.

Istio Ambient Mode를 통한 최적화된 트래픽 관리

Ambient Mode는 각 Node별로 존재하는 ztunnel에서 pod의 네트워크 네임스페이스 내부에 수신 소켓을 설정합니다. 이 설정을 통해 mTLS 트래픽과 일반 트래픽의 리다이렉션을 CNI에서 발생할 수 있는 잠재적인 충돌을 방지하면서 구현할 수 있습니다.

Figure 2.3 Ambient Mode를 통한 최적화된 트래픽 관리

  • istio.io/dataplane-mode=ambient 태그를 감지하여, pod 생성을 감지하고 Istio CNI Agent가 트래픽 리다이렉션 구성
  • 네트워크 리다이렉션 규칙은 pod의 네트워크 네임스페이스 내부에 존재하여 모든 트래픽들을 ztunnel로 리다이렉션
  • 트래픽 리다이렉션 활성을 위해 pod의 네트워크 네임스페이스 내부에 수신 소켓 구성 후 mTLS 트래픽과 일반 트래픽을 pod가 ztuunel을 통해 처리

Detailed Explanation of Transparent Traffic Interception in Istio Ambient Mode

Detailed Traffic Interception Process

Figure 2.4 Ambient Mode의 전체 흐름

  • Pod 초기화 및 네트워크 구성
    • Pod 생성 시 CRI를 통해 기본 CNI 플러그인을 호출하여, Pod의 네트워크 구성
  • Istio CNI Agent가 트래픽 리디렉션을 구성
    • 생성된 Pod가 해당 istio.io/dataplane-mode=ambient Label이 있는지 확인
    • Pod의 Network Namespace에 들어가 iptables 규칙 설정
    • Network Namespace의 파일 설명자(FD)가 ztunnel에 전달
  • Ztunnel이 Pod 네트워크 네임스페이스에서 소켓 수신
    • ztunnel은 Network NameSpace의 FD를 수신하고 리디렉션된 트래픽을 처리하기 위해 해당 네임스페이스 내의 소켓을 수신
  • 투명한 트래픽 차단 및 처리
    • 애플리케이션에서 발생한 트래픽은 Pod의 iptables 규칙에 의해 가로채여 투명하게 ztunnel로 리디렉션

In-Pod IPtables Rule Injection in Istio Ambient Mode Explained

iptables Rules Inside the Pod

istio cni가 설정한 iptables 규칙은 중 중요하게 확인해야할 정보는 다음과 같습니다.

  • 15008(HBONE 소켓)
    • HBONE 프로토콜을 사용하여 HTTP 기반 트래픽을 투명하게 처리합니다.
  • 15006(일반 텍스트 소켓)
    • Pod 간 통신을 위해 메시 내의 암호화되지 않은 트래픽을 관리합니다.
  • 15001(아웃바운드 소켓)
    • 아웃바운드 트래픽을 제어하고 외부 서비스 액세스에 대한 정책을 시행합니다.

Mark

  • 0x539
    • Istio Porxy에서 발생하는 트래픽을 식별하며, 프록시에서 처리된 패킷을 구별하여 재처리되거나 잘못된 경로로 지정되지 않도록 합니다.
  • 0x111
    • Istio 메시 내의 연결 수준 표시에 사용되며, 이는 프록시에 의해 연결이 처리된 것을 확인하게 해줍니다.

Figure 2.5 Ambient Mesh Traffic 시각화

Packet Lifecycle and Traffic Optimization in Istio Ambient Mode

Overview of Packet Lifecycle: From Kernel Space to User Space

Ambient Mode에서 패킷 처리는 Pod의 커널 공간 Network Stack에서 시작하고, iptables 규칙에 의해 트래픽을 인터셉트 한 후 user space의 ztunnel에 의해 트래픽이 처리됩니다.

트래픽을 처리 할때, 첫번째 패킷을 분석하여 태그를 지정을 하고 다음 패킷의 경로를 확보를 한 뒤에 중복 오버헤드를 줄입니다.

Figure 2.6 Ambient Mesh에서의 Packet 수명 주기

First Packet Path: From Interception to Destination Resolution

Pod에서 패킷을 처리 할때 제일 첫번째로 Pod의 Network Namespace와 Network Stack에서 처리가 됩니다.

그 이후 iptables 규칙을 사용하여 OutBound Traffic을 필터링을 하게 되는데, 이때 주소가 로컬이 아니고 태그가 없는 경우 ztunnel의 Proxy 포트로 리다이렉션이 됩니다.

이때 Source IP가 보존이 되어있어, User Space에서 패킷의 목적지 주소를 추출 할 수 있습니다.

Subsequent Packet Path: Fast Forwarding with Conntrack and Tunnel Reuse

첫 번째 패킷을 처리 하게되면 리눅스 커널에서 Conntrack은 연결 상태를 기록하고 태그를 지정하게됩니다.

동일한 연결에 속하는 후속 패킷들에 대하여 iptables 규칙을 반복적으로 수행하지 않고 캐싱처리를 하여 패킷을 ztunnel로 직접 전송하여 최적화를 수행할 수 있습니다.

ztunnel에 Inbound 소켓으로 들어오는 패킷들은 태그로 식별이 되며 RBAC 검증 및 mTLS을 수행하지 않고 연결을 제공 하여 속도 향샹이 됩니다.

Key Technical Points and Optimization Strategies 핵심 기술 포인트 및 최적화 전략

Transparent Proxying

  • 소스 IP의 정보를 보관하여 ztunnel은 진정한 투명 프록시 기능을 수행합니다.

Efficient Kernel-User Space Switching

  • 첫 번째 패킷에 대한 정책 검증 및 구문 분석을 수행하여, 불필요한 Context 전환을 최소화 하고 최적화를 기대할 수 있습니다.

Multiplexed Tunnels

  • HBONE은 암호화,LB, 멀티플렉싱을 지원하여 후속 패킷 전송의 효율성을 향상 시킵니다.

Understanding L7 Traffic Management in Istio Ambient Mode: From Ztunnel to Waypoint Proxy

Roles and Responsibilities in Ambient Mode 앰비언트 모드에서의 역할 및 책임

ztunnel은 L4 트래픽까지만 처리를 하고 ztunnel이 L7 처리가 필요한 트래픽을 감지하면 HBONE 프로토콜을 사용하여 트래픽을 wayPoint로 전달합니다.
이때 트래픽을 처리하는 경로는 다음과 같습니다.

Figure 2.7 동일 노드에 있는 소스 pod와 목적지 pod의 L7 트래픽 경로

Figure 2.8 다른 노드에 있는 소스 pod와 목적지 pod 의 L7 트래픽 경로

3. 실습

Ambient Mode

실습 환경 구성


# Master 1대 Worker 1대 배포
kind create cluster --name myk8s --image kindest/node:v1.32.2 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000 # Sample Application
    hostPort: 30000
  - containerPort: 30001 # Prometheus
    hostPort: 30001
  - containerPort: 30002 # Grafana
    hostPort: 30002
  - containerPort: 30003 # Kiali
    hostPort: 30003
  - containerPort: 30004 # Tracing
    hostPort: 30004
  - containerPort: 30005 # kube-ops-view
    hostPort: 30005
- role: worker
- role: worker
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.1.0/24
EOF

# 노드에 기본 툴 설치
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'apt update && apt install tree psmisc lsof ipset wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'; echo; done

# 테스트용 컨테이너 기동
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity

# MetalLB 배포
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml

# IPAddressPool, L2Advertisement 설정
cat << EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default
  namespace: metallb-system
spec:
  addresses:
  - 172.18.255.201-172.18.255.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default
EOF

# Istio설치

# myk8s-control-plane 진입 후 설치 진행
docker exec -it myk8s-control-plane bash

export ISTIOV=1.26.0
echo 'export ISTIOV=1.26.0' >> /root/.bashrc

curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl


istioctl install --set profile=ambient --set meshConfig.accessLogFile=/dev/stdout --skip-confirmation

# Install the Kubernetes Gateway API CRDs
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
  kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml

# 보조 도구 설치
kubectl apply -f istio-$ISTIOV/samples/addons


# 빠져나오기
exit

# iptables 규칙 확인
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'iptables-save'; echo; done


# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'

# istio-cni-node 와 ztunnel 데몬셋 파드 확인
kubectl get ds -n istio-system

# 노드에서 istio-cni 정보 확인
for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /opt/cni/bin'; echo; done

for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /etc/cni/net.d'; echo; done

for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /var/run/istio-cni'; echo; done

for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /var/run/netns'; echo; done

for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'lsns -t net'; echo; done


# ztunnel 파드 확인 : 파드 이름 변수 지정
ZPOD1NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[0].metadata.name}")
ZPOD2NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[1].metadata.name}")
ZPOD3NAME=$(kubectl get pod -n istio-system -l app=ztunnel -o jsonpath="{.items[2].metadata.name}")

# ztunnel pod 접속을 위한 plugin 설치

kubectl krew install pexec

kubectl pexec $ZPOD1NAME -it -T -n istio-system -- bash

# ztunnel pod 접속 후 포트 및 소켓 정보 확인
# /var/run/ztunnel/ztunnel.sock 해당 소켓이 zunnel에서 Application으로 Injection되는 소켓
ss -tnlp
ss -xnp


# 메트릭 정보 확인
curl -s http://localhost:15020/metrics

# ztuunel config dump확인 시 sidecar에서 확인되는 설정 보다 현저히 적은 것 확인
curl -s http://localhost:15000/config_dump

# 터미널에서 나오기
exit

# 샘플 Application 설치
docker exec -it myk8s-control-plane kubectl apply -f istio-1.26.0/samples/bookinfo/platform/kube/bookinfo.yaml

# Request 테스트용 Pod 및 Service Account 설치
kubectl create sa netshoot

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: netshoot
spec:
  serviceAccountName: netshoot
  nodeName: myk8s-control-plane
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# Sample Application 접속을 위한 Gateway 리소스와 HTTP Route 리소스 배포
docker exec -it myk8s-control-plane kubectl apply -f istio-1.26.0/samples/bookinfo/gateway-api/bookinfo-gateway.yaml

# mypc 컨테이너에서 반복 요청
GWLB=$(kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl $GWLB/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

# for node in control-plane worker worker2; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'ls -l /opt/cni/bin'; echo; done
node : myk8s-control-plane
total 70820
-rwxr-xr-x 1 root root  4059492 Mar  5 12:52 host-local
-rwxr-xr-x 1 root root 54538424 Jun  8 11:17 istio-cni
-rwxr-xr-x 1 root root  4114956 Mar  5 12:52 loopback
-rwxr-xr-x 1 root root  4713142 Mar  5 12:52 portmap
-rwxr-xr-x 1 root root  5080390 Mar  5 12:52 ptp

node : myk8s-worker
total 70820
-rwxr-xr-x 1 root root  4059492 Mar  5 12:52 host-local
-rwxr-xr-x 1 root root 54538424 Jun  8 11:17 istio-cni
-rwxr-xr-x 1 root root  4114956 Mar  5 12:52 loopback
-rwxr-xr-x 1 root root  4713142 Mar  5 12:52 portmap
-rwxr-xr-x 1 root root  5080390 Mar  5 12:52 ptp

node : myk8s-worker2
total 70820
-rwxr-xr-x 1 root root  4059492 Mar  5 12:52 host-local
-rwxr-xr-x 1 root root 54538424 Jun  8 11:17 istio-cni
-rwxr-xr-x 1 root root  4114956 Mar  5 12:52 loopback
-rwxr-xr-x 1 root root  4713142 Mar  5 12:52 portmap
-rwxr-xr-x 1 root root  5080390 Mar  5 12:52 ptp

# ss -tnlp
State                     Recv-Q                    Send-Q                                       Local Address:Port                                         Peer Address:Port                    Process
LISTEN                    0                         1024                                             127.0.0.1:15000                                             0.0.0.0:*                        users:(("ztunnel",pid=1,fd=14))
LISTEN                    0                         1024                                                 [::1]:15000                                                [::]:*                        users:(("ztunnel",pid=1,fd=15))
LISTEN                    0                         1024                                                     *:15021                                                   *:*                        users:(("ztunnel",pid=1,fd=13))
LISTEN                    0                         1024                                                     *:15020                                                   *:*                        users:(("ztunnel",pid=1,fd=17))

# ss -xnp
Netid          State          Recv-Q          Send-Q                                     Local Address:Port                      Peer Address:Port           Process
u_seq          ESTAB          0               0                                                      * 79721                                * 81482           users:(("ztunnel",pid=1,fd=19))
u_str          ESTAB          0               0                                                      * 79698                                * 79697           users:(("ztunnel",pid=1,fd=7))
u_str          ESTAB          0               0                                                      * 79697                                * 79698           users:(("ztunnel",pid=1,fd=12),("ztunnel",pid=1,fd=8),("ztunnel",pid=1,fd=6))
u_seq          ESTAB          0               0                          /var/run/ztunnel/ztunnel.sock 81482                                * 79721

Figure 3.1 kiali를 통한 Sample Application 상태 확인

# Application Pod를 Ambient Mesh에 추가
# 제거하는경우 istio.io/dataplane-mode=none 해당 Label 추가
kubectl label namespace default istio.io/dataplane-mode=ambient

# ztunnel을 경유하는 pod 확인
docker exec -it myk8s-control-plane istioctl ztunnel-config workload | grep HBONE

# proxy-statys 확인
docker exec -it myk8s-control-plane istioctl proxy-status

# ztunnel config 확인(Evnoy 설정과 차이 확인 필수)

docker exec -it myk8s-control-plane istioctl ztunnel-config workload --address 10.10.1.11 -o json


PPOD=$(kubectl get pod -l app=productpage -o jsonpath='{.items[0].metadata.name}')

# ztunnel이 injection 됐을 때의 iptables확인
kubectl pexec $PPOD -it -T -- bash
iptables-save
iptables -t mangle -S
iptables -t nat -S

# ztunnel 파드 로그 모니터링 : IN/OUT 트래픽 정보
kubectl -n istio-system logs -l app=ztunnel -f | egrep "inbound|outbound"

# ztunnel 파드 확인 : 파드 이름 변수 지정
kubectl pexec $ZPOD1NAME -it -T -n istio-system -- bash
ls -l  /var/run/ztunnel


# 메트릭 정보 확인 Metric 정보는 Application pod가 아닌 ztunnel Pod에서만 확인 가능
curl -s http://localhost:15020/metrics | grep '^[^#]'
...

# Viewing Istiod state for ztunnel xDS resources
curl -s http://localhost:15000/config_dump

# netshoot 파드만 ambient mode 에서 제외
kubectl label pod netshoot istio.io/dataplane-mode=none


# ztunnel 설정 확인
docker exec -it myk8s-control-plane istioctl ztunnel-config service

# Ambient Mesh내부에서 ztunnel에 배포된 pod의 인증서 정보 확인
docker exec -it myk8s-control-plane istioctl ztunnel-config certificate --node myk8s-worker

Figure 3.2 kiali를 통해 ztunnel이 적용된(mTLS) Sample Application 상태 확인

Figure 3.3 Grafana에서 ztunnel 상태 확인

# docker exec -it myk8s-control-plane istioctl ztunnel-config workload | grep HBONE
NAMESPACE          POD NAME                                    ADDRESS    NODE                WAYPOINT PROTOCOL
default            details-v1-766844796b-qj2sx                 10.10.1.11 myk8s-worker        None     HBONE
default            netshoot                                    10.10.0.7  myk8s-control-plane None     HBONE
default            productpage-v1-54bb874995-zzcnj             10.10.1.15 myk8s-worker        None     HBONE
default            ratings-v1-5dc79b6bcd-jdm4c                 10.10.1.12 myk8s-worker        None     HBONE
default            reviews-v1-598b896c9d-psjv9                 10.10.1.13 myk8s-worker        None     HBONE
default            reviews-v2-556d6457d-2wz4r                  10.10.3.6  myk8s-worker2       None     HBONE
default            reviews-v3-564544b4d6-d6xvq                 10.10.1.14 myk8s-worker        None     HBONE

# docker exec -it myk8s-control-plane istioctl proxy-status
NAME                                                CLUSTER        CDS              LDS              EDS                RDS              ECDS        ISTIOD                     VERSION
bookinfo-gateway-istio-6cbd9bcd49-v7ltn.default     Kubernetes     SYNCED (27m)     SYNCED (27m)     SYNCED (7m22s)     SYNCED (27m)     IGNORED     istiod-86b6b7ff7-zckfz     1.26.0

#docker exec -it myk8s-control-plane istioctl ztunnel-config workload --address 10.10.1.11 -o json
[
    {
        "uid": "Kubernetes//Pod/default/details-v1-766844796b-qj2sx",
        "workloadIps": [
            "10.10.1.11"
        ],
        "protocol": "HBONE",
        "name": "details-v1-766844796b-qj2sx",
        "namespace": "default",
        "serviceAccount": "bookinfo-details",
        "workloadName": "details-v1",
        "workloadType": "pod",
        "canonicalName": "details",
        "canonicalRevision": "v1",
        "clusterId": "Kubernetes",
        "trustDomain": "cluster.local",
        "locality": {},
        "node": "myk8s-worker",
        "status": "Healthy",
        "hostname": "",
        "capacity": 1,
        "applicationTunnel": {
            "protocol": ""
        }
    }
]


# docker exec -it myk8s-control-plane istioctl ztunnel-config service
NAMESPACE      SERVICE NAME            SERVICE VIP  WAYPOINT ENDPOINTS
default        bookinfo-gateway-istio  10.200.1.59  None     1/1
default        details                 10.200.1.26  None     1/1
default        kubernetes              10.200.1.1   None     1/1
default        productpage             10.200.1.44  None     1/1
default        ratings                 10.200.1.235 None     1/1

#  docker exec -it myk8s-control-plane istioctl ztunnel-config certificate --node myk8s-worker
CERTIFICATE NAME                                              TYPE     STATUS        VALID CERT     SERIAL NUMBER                        NOT AFTER                NOT BEFORE
spiffe://cluster.local/ns/default/sa/bookinfo-details         Leaf     Available     true           96e699a5c6df3607f1758f4b559e8fb8     2025-06-09T12:07:28Z     2025-06-08T12:05:28Z
spiffe://cluster.local/ns/default/sa/bookinfo-details         Root     Available     true           44c554805767082e9fc83a1337ce0864     2035-06-06T11:17:29Z     2025-06-08T11:17:29Z
spiffe://cluster.local/ns/default/sa/bookinfo-productpage     Leaf     Available     true           6d5d43342459c9967c406fada1b7054c     2025-06-09T12:07:28Z     2025-06-08T12:05:28Z
spiffe://cluster.local/ns/default/sa/bookinfo-productpage     Root     Available     true           44c554805767082e9fc83a1337ce0864     2035-06-06T11:17:29Z     2025-06-08T11:17:29Z
spiffe://cluster.local/ns/default/sa/bookinfo-ratings         Leaf     Available     true           ea79c5161bb63626e4d7c968ad2e866c     2025-06-09T12:07:28Z     2025-06-08T12:05:28Z
spiffe://cluster.local/ns/default/sa/bookinfo-ratings         Root     Available     true           44c554805767082e9fc83a1337ce0864     2035-06-06T11:17:29Z     2025-06-08T11:17:29Z
spiffe://cluster.local/ns/default/sa/bookinfo-reviews         Leaf     Available     true           e831e2a272e1d711875a827cd0687d40     2025-06-09T12:07:28Z     2025-06-08T12:05:28Z
spiffe://cluster.local/ns/default/sa/bookinfo-reviews         Root     Available     true           44c554805767082e9fc83a1337ce0864     2035-06-06T11:17:29Z     2025-06-08T11:17:29Z
# ProductPage 확인

# Istio CNI가 주입하는 iptables 설정 확인

# DNS 트래픽에 대해 연결 추적을 설정, 이후 마크 기반으로 리디렉션 여부를 제어
iptables -t raw -S
# 특정 마크가 붙은 트래픽을 식별해서 추후 NAT에서 건너뛰게 함
iptables -t mangle -S


# 트래픽 리디렉션 실행 정보 확인
iptables -t nat -S

## DNS 트래픽 → 15053 포트로 리디렉션 (Envoy가 DNS를 프록시)
## TCP 트래픽 (출발지/도착지에 따라) → 15001 또는 15006 포트로 리디렉션
## 특정 마크 (0x539, 0x111)나 주소는 리디렉션 제외
## conntrack 및 mark 시스템으로 트래픽 상태 관리 및 리디렉션 조건 제어

# mTLS 트래픽 캡처
tcpdump -i eth0 -A -s 0 -nn 'tcp port 15008'

# PROTOCOL HBONE 확인(mTLS)
docker exec -it myk8s-control-plane istioctl ztunnel-config workload
# Result
# tcpdump -i eth0 -A -s 0 -nn 'tcp port 15008'
....:.&...(.....%..T.....
y....1..
14:50:30.979793 IP 10.10.1.15.56086 > 10.10.1.14.15008: Flags [.], ack 1387, win 9756, options [nop,nop,TS val 2722377705 ecr 2151026206], length 0
E..4..@.@.i.


# docker exec -it myk8s-control-plane istioctl ztunnel-config workload
NAMESPACE          POD NAME                                    ADDRESS    NODE                WAYPOINT PROTOCOL
default            productpage-v1-54bb874995-zzcnj             10.10.1.15 myk8s-worker        None     HBONE
default            ratings-v1-5dc79b6bcd-jdm4c                 10.10.1.12 myk8s-worker        None     HBONE
default            reviews-v1-598b896c9d-psjv9                 10.10.1.13 myk8s-worker        None     HBONE
default            reviews-v2-556d6457d-2wz4r                  10.10.3.6  myk8s-worker2       None     HBONE
default            reviews-v3-564544b4d6-d6xvq                 10.10.1.14 myk8s-worker        None     HBONE

Figure 3.4 Prometheus에서 istio_tcp_connections_opened_total Metric 확인

# Ambient Mesh환경에서 적용한 L4 Authorization Policy
# netshoot 파드만 ambient mode 다시 추가
kubectl label pod netshoot istio.io/dataplane-mode=ambient --overwrite
docker exec -it myk8s-control-plane istioctl ztunnel-config workload

# L4 Authorization Policy 신규 생성
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: productpage-viewer
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - cluster.local/ns/default/sa/netshoot
EOF

# L4 Authorization 차단 Policy 동작 확인
GWLB=$(kubectl get svc bookinfo-gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl $GWLB/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

# L4 Authorization 허용 Policy 동작 확인
kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title
while true; do kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done

# L4 Authorization Policy 업데이트
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: productpage-viewer
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  action: ALLOW
  rules:
  - from:
    - source:
        principals:
        - cluster.local/ns/default/sa/netshoot
        - cluster.local/ns/default/sa/bookinfo-gateway-istio
EOF

# WayPoint 생성
docker exec -it myk8s-control-plane istioctl waypoint apply -n default

# WayPoint 확인
docker exec -it myk8s-control-plane istioctl waypoint status
# docker exec -it myk8s-control-plane istioctl ztunnel-config workload
default            netshoot                                    10.10.0.7  myk8s-control-plane None     HBONE

# while true; do docker exec -it mypc curl $GWLB/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done
2025-06-09 00:00:35

# while true; do kubectl exec -it netshoot -- curl -sS productpage:9080/productpage | grep -i title ; date "+%Y-%m-%d %H:%M:%S"; sleep 1; done
<title>Simple Bookstore App</title>
2025-06-09 00:01:57

# docker exec -it myk8s-control-plane istioctl waypoint status
NAMESPACE     NAME         STATUS     TYPE           REASON         MESSAGE
default       waypoint     True       Programmed     Programmed     Resource programmed, assigned to service(s) waypoint.default.svc.cluster.local:15008