Istio Hands-on Study [1기] [9주차] Ambient Mesh
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