📌 Notice
Istio Hands-on Study (=Istio)
직접 실습을 통해 Isito를 배포 및 설정하는 내용을 정리한 블로그입니다.
CloudNet@
에서 스터디를 진행하고 있습니다.
Gasida
님께 다시한번 🙇 감사드립니다.
EKS 관련 이전 스터디 내용은 아래 링크를 통해 확인할 수 있습니다.
이번 장에서는 Istio 다중 클러스터 서비스 메시를 실제 환경에 구축하는 방법에 대해 다룹니다.
단일 클러스터 환경에서는 경험할 수 없었던 클러스터 간 워크로드 디스커버리, 네트워크 연결, 공통 신뢰 구성이라는 세 가지 핵심 과제를 해결하는 과정을 살펴보게 됩니다.
ACME사의 온라인 스토어 시나리오를 통해 west-cluster의 webapp이 east-cluster의 catalog 서비스와 어떻게 안전하게 통신할 수 있는지, East-West Gateway와 SNI 클러스터를 활용한 투명한 클러스터 간 라우팅이 어떻게 동작하는지 직접 확인해봅니다.
또한 지역 인식 라우팅과 자동 장애 극복 기능을 통해 고가용성 서비스를 구현하는 방법과, AuthorizationPolicy를 활용한 클러스터 간 접근 제어까지 실습을 통해 익혀보게 됩니다.
실습 과정에서는 Kiali, Jaeger, istioctl 등 다양한 도구를 활용한 모니터링과 디버깅 기법도 함께 다루어, 실무에서 다중 클러스터 메시를 운영할 때 필요한 실전 노하우를 습득할 수 있습니다.
This chapter covers
Scaling Istio in your organization
- 여러 클러스터에서 서비스 메시를 스케일링하는 방법
- 두 클러스터를 합치기 위한 전체 조건을 해결하는 방법
- 여러 클러스터의 워크로드 간에 공통 신뢰를 설정하는 방법
- 클러스터 간 워크로드를 찾는 방법
- east-west 트래픽을 위한 이스티오 인그레스 게이트웨이를 설정하는 방법
지금까지는 단일 클러스터 내에서 이스티오의 다양한 기능을 어떻게 활성화할 수 있는지에 대해 살펴보았습니다. 그러나 서비스 메시는 단일 클러스터에만 종속되지 않습니다. 여러 클러스터에 걸쳐 서비스 메시를 구성할 수 있으며, 각 클러스터에서도 동일한 기능을 제공할 수 있습니다.
사실, 서비스 메시의 진정한 가치는 워크로드의 수가 많아질수록 더욱 증가합니다.
그렇다면 언제 서비스 메시가 여러 클러스터에 걸쳐 있기를 원하게 될까요?
단일 클러스터와 비교했을 때 다중 클러스터 서비스 메시는 어떤 이점을 제공할 수 있을까요?
이러한 질문에 답하기 위해, 가상의 기업인 ACME사를 다시 살펴보겠습니다. 이 회사는 클라우드 플랫폼으로 전환하였으며, 마이크로서비스 아키텍처로 인해 발생한 네트워크 복잡성을 모두 경험한 상태입니다.
다중 클러스터 서비스 메시의 이점에 대해서는 공식 문서에서도 확인할 수 있습니다.
ACME사는 클라우드 마이그레이션 초기, 클러스터의 규모를 어떻게 조정할 것인지에 대한 고민에 직면하였습니다. 처음에는 단일 클러스터로 시작했지만, 곧 결정을 바꾸어 작은 클러스터 다수를 사용하는 전략을 선택하였습니다. 그 이유는 다음과 같은 이점 때문입니다.
각 팀의 클러스터를 분리함으로써 한 팀의 장애나 실수가 다른 팀에 영향을 주지 않도록 구성할 수 있습니다.
클러스터 단위로 장애 범위를 제한함으로써, 특정 클러스터에서의 장애가 전체 시스템에 미치는 영향을 최소화할 수 있습니다.
특정 클러스터에 민감한 데이터 접근 권한이 필요한 워크로드만 배치함으로써, 보안 및 컴플라이언스 요구사항을 충족할 수 있습니다.
여러 리전에 클러스터를 배치함으로써, 사용자의 지리적 위치에 따라 가장 가까운 클러스터로 트래픽을 라우팅할 수 있어 지연 시간을 줄이고 가용성을 높일 수 있습니다.
여러 클라우드 제공자 또는 온프레미스 환경과 클라우드를 혼합한 구조에서 유연하게 워크로드를 배포하고 운영할 수 있습니다.
ACME사는 이러한 장점을 종합적으로 고려하여, 서비스 메시를 여러 클러스터에 걸쳐 확장할 수 있는 기능, 그리고 클러스터 간 트래픽 관리, 관찰 가능성, 보안 기능을 제공하는 이스티오의 도입을 결정하였습니다.
회사는 다중 클러스터 환경을 구축하기 위해 두 가지 접근 방식을 검토하였습니다.
이 책에서는 다중 클러스터 서비스 메시 방식을 중점적으로 다루며, 메시 연합 방식에 대해서는 공식 문서를 참고하시기 바랍니다.
다중 클러스터 서비스 메시를 구성하면 애플리케이션에는 완전히 투명한 방식으로 클러스터 간 서비스를 연결할 수 있으며, 이 과정에서 트래픽 제어, 복원력, 관찰 가능성, 보안 등 서비스 메시가 제공하는 기능은 그대로 유지됩니다.
이스티오는 다중 클러스터 구성을 위해, 각 클러스터의 서비스를 조회하고, 이 정보를 바탕으로 서비스 프록시가 클러스터 간 트래픽을 라우팅하도록 구성합니다.
다중 클러스터 메시를 구성하기 위해 충족해야 할 조건은 다음과 같습니다.
Istio 컨트롤 플레인은 서비스 프록시를 구성하기 위해 동료 Kubernetes 클러스터의 워크로드를 조회할 수 있어야 합니다. 이를 위해 상대 클러스터의 API 서버에 접근할 수 있어야 합니다.
워크로드 간 네트워크 연결이 가능해야 합니다. 엔드포인트 정보를 알고 있더라도 실제 통신이 불가능하다면 의미가 없습니다.
Istio의 보안 기능을 활용하려면 클러스터 간 워크로드가 상호 인증할 수 있어야 하며, 이를 통해 인증 및 인가 정책을 적용할 수 있습니다.
위 세 가지 조건을 충족하면 클러스터는 서로의 워크로드를 인지하고, 통신하며, 보안 정책을 적용할 수 있게 됩니다. 이것이 바로 다중 클러스터 메시 구성의 필수 조건입니다.
👉🏻 다중 클러스터 연결성과 보안
앞서 설명한 것처럼, Istio는 클러스터 간 연결을 위해 상대 클러스터의 Kubernetes API 서버에 접근해야 합니다. 그러나 일부 조직에서는 모든 클러스터가 서로의 API에 접근 가능한 구조가 보안상 적절하지 않을 수 있습니다.
이러한 경우에는 메시 연합(Mesh federation) 방식이 더 나은 선택일 수 있습니다.
다중 클러스터 메시를 구성할 때 Istio는 클러스터의 역할에 따라 다음 두 가지 유형으로 나눕니다.
Istio 컨트롤 플레인이 설치된 클러스터입니다.
→ 아래 그림의 왼쪽 클러스터에 해당합니다.
컨트롤 플레인이 설치되어 있지 않은 클러스터입니다.
→ 아래 그림의 오른쪽 클러스터에 해당합니다.
워크로드의 가용성과 목적에 따라 다음 세 가지 배포 모델 중 하나를 선택할 수 있습니다.
다중 클러스터 환경에서 Istio의 컨트롤 플레인(istiod)은 서비스 프록시 구성을 위해, Kubernetes API 서버를 통해 서비스와 엔드포인트 정보를 조회해야 합니다. 그러나 이러한 접근은 강력한 권한을 필요로 하며, 잘못 사용될 경우 클러스터에 치명적인 영향을 줄 수 있습니다.
👉🏻 보안상의 고려
원격 Kubernetes API에 접근할 수 있도록 토큰과 RBAC으로 보호하는 방법을 뒤에서 다루지만, 이 방식에는 트레이드오프가 존재합니다. 각 클러스터가 다른 클러스터의 API에 접근해야 하기 때문에, 보안상 위험 요소로 작용할 수 있습니다. 이러한 이유로 메시 연합이 대안이 될 수 있습니다.
클러스터 간 디스커버리를 구현할 때, Kubernetes의 RBAC 기능과 서비스 어카운트가 중요한 역할을 합니다.
서비스 어카운트 (Service accounts)
서비스 어카운트는 사람(사용자)이 아닌 클라이언트(머신, 서비스 등)에게 ID를 제공합니다.
서비스 어카운트 토큰
서비스 어카운트마다 자동으로 생성되며, 해당 ID를 나타내는 JWT 토큰입니다. Kubernetes는 이 토큰을 파드에 주입하며, 파드는 이를 이용해 API 서버에 인증할 수 있습니다.
롤 (Role) 및 클러스터롤 (ClusterRole)
특정 ID(서비스 어카운트 또는 사용자)가 어떤 리소스에 어떤 작업을 할 수 있는지 권한을 부여합니다.
기술적으로 클러스터 간 디스커버리는 일반적인 디스커버리와 동일합니다. 다만 istiod에 원격 클러스터의 서비스 어카운트 토큰을 제공해야 하며, API 서버와의 보안 통신을 위한 인증서도 함께 구성해야 합니다.
istiod는 해당 토큰을 이용해 원격 클러스터의 API 서버에 인증하고, 클러스터에서 실행 중인 워크로드를 검색합니다.
처음에는 복잡해 보일 수 있지만 걱정할 필요는 없습니다.
이 장의 뒷부분에서 istioctl을 이용해 이 과정을 자동화하는 방법을 살펴볼 예정입니다.
다중 클러스터 메시를 구성할 때 충족해야 할 또 다른 중요한 조건은 클러스터 간 워크로드 연결성입니다. 즉, 한 클러스터의 워크로드가 다른 클러스터의 워크로드와 통신할 수 있어야 합니다.
클러스터들이 동일한 네트워크(예: Amazon VPC) 상에 존재하거나, 네트워크 피어링이 설정된 경우에는 워크로드들이 서로의 IP 주소를 직접 참조하여 통신할 수 있으므로 이 조건은 이미 충족된 상태입니다.
클러스터들이 서로 다른 네트워크에 위치한 경우, 워크로드 간 직접 연결이 불가능하므로 특수한 Istio 인그레스 게이트웨이를 사용해야 합니다. 이 게이트웨이는 각 네트워크의 에지(edge)에 위치하여 클러스터 간 트래픽을 프록시합니다.
이러한 게이트웨이를 east-west 게이트웨이라고 부릅니다.
다중 네트워크 환경에서 Istio는 다음과 같은 문제를 해결합니다 (공식 문서):
만약 Multi-primary 구성에서 클러스터들이 L3 라우팅이 가능한 네트워크 장비로 연결되어 있다면, east-west 게이트웨이 없이도 직접 통신이 가능합니다.
자세한 실습 예제는 다음 GitHub 리포지토리를 참고할 수 있습니다:
🔗 https://github.com/abasitt/kube6/tree/main/istio/istiocon
다중 클러스터 서비스 메시를 구성할 때 마지막으로 해결해야 할 핵심 요소는 클러스터 간의 공통된 신뢰(Trust)입니다.
공통된 신뢰를 갖추면 서로 다른 클러스터에 위치한 워크로드들도 상호 인증(Mutual Authentication)이 가능해집니다.
클러스터 간 공통 신뢰를 구성하는 방법은 크게 두 가지로 나뉩니다.
장점
단점 및 보안 리스크
이 방식에서는 외부 CA를 통해 인증서를 서명하고, Istio가 클러스터 간 신뢰 관계를 중계합니다.
방법 1. cert-manager 활용
istio-csr
컴포넌트가 Kubernetes CSR을 감시하고 외부 CA에 서명 요청을 전달합니다.Signature Algorithm: ecdsa-with-SHA256
Issuer: O=cert-manager, O=cluster.local, CN=istio-ca
...
URI:spiffe://cluster.local/ns/default/sa/httpbin
방법 2. 맞춤형 컨트롤러 개발
이 장에서는 구성의 단순성을 중시하여 플러그인 CA 인증서 방식을 사용해 클러스터 간 공통 신뢰를 설정합니다.
이로써 다중 클러스터 서비스 메시를 구성하는 데 필요한 모든 조건을 고수준에서 살펴보았습니다.
이번 실습에서는 실세계 엔터프라이즈 서비스를 모방한 인프라 환경을 구성합니다.
이 환경은 다음과 같은 특성을 가집니다:
west-cluster
:
미국 서부(us-west) 리전에 위치한 사설 네트워크 상의 Kubernetes 클러스터이며, webapp 서비스를 실행합니다.
east-cluster
:
미국 동부(us-east) 리전에 위치한 사설 네트워크 상의 Kubernetes 클러스터이며, catalog 서비스를 실행합니다.
이렇게 클러스터를 서로 다른 리전에 배치하면, 한 리전에 장애가 발생하더라도 다른 클러스터에서 서비스를 유지할 수 있어 재해 복구(Disaster Recovery) 관점에서 이점을 가집니다.
참고로, webapp
과 catalog
워크로드는 반드시 별도 클러스터에 배치되어야 할 기술적인 이유가 있는 것은 아닙니다.
이 구성은 단지 다중 클러스터 메시를 시연하기 위한 예시일 뿐입니다.
또한, 잦은 통신이 발생하는 워크로드(예: 채팅, 추천 등)는 지연 시간을 줄이기 위해 물리적으로 가까운 위치에 배치하는 것이 이상적입니다.
현재 구성하는 인프라는 다중 네트워크 환경이기 때문에, 클러스터 간의 연결을 위해 반드시 east-west 게이트웨이를 사용해야 합니다.
그러나 컨트롤 플레인을 복제할 것인지, 혹은 단일 컨트롤 플레인으로 유지할 것인지는 반드시 정해진 선택이 아니며, 비즈니스 요구 사항에 따라 결정됩니다.
ACME사의 경우, 온라인 상점의 인기가 매우 높기 때문에 서비스가 단 1분만 중단되어도 수백만 달러의 손해가 발생합니다.
이러한 이유로 고가용성(high availability)이 최우선 과제가 되었고, 각 클러스터에 Istio 컨트롤 플레인을 개별적으로 설치하는 기본-기본(Primary-Primary) 배포 모델을 선택하게 됩니다.
따라서 이 실습에서는 다음과 같은 구성을 진행합니다:
이제 본격적으로 다중 클러스터 메시를 구축해보겠습니다.
이 실습에서는 Azure와 같은 퍼블릭 클라우드가 아닌 로컬 환경 기반으로 Kubernetes 클러스터를 구성합니다.
west-cluster
와 east-cluster
를 Kind를 통해 생성하고, 이를 통해 다중 클러스터/네트워크 환경을 재현합니다.
👉🏻 주의
실습 중NodePort
를 사용하는 서비스가 겹칠 수 있으므로, 충돌이 발생하면 수동으로 포트를 수정해야 합니다.
west 클러스터 배포
# Kind를 이용해 west 클러스터 생성 kind create cluster --name west --image kindest/node:v1.23.17 --kubeconfig ./west-kubeconfig --config - <<EOF kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 30000 # istio-ingressgateway HTTP 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 networking: podSubnet: 10.10.0.0/16 serviceSubnet: 10.100.0.0/24 EOF # 설치 확인 docker ps cat west-kubeconfig kubectl get node --kubeconfig=./west-kubeconfig kubectl get pod -A --kubeconfig=./west-kubeconfig # 컨트롤 플레인 노드에 유틸리티 설치 docker exec -it west-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y' # (옵션) kube-ops-view 설치 helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30005 --set env.TZ="Asia/Seoul" --namespace kube-system --kubeconfig=./west-kubeconfig kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view --kubeconfig=./west-kubeconfig # 대시보드 접속 확인 open "http://localhost:30005/#scale=1.5"
east 클러스터 배포
# Kind를 이용해 east 클러스터 생성 kind create cluster --name east --image kindest/node:v1.23.17 --kubeconfig ./east-kubeconfig --config - <<EOF kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 31000 # istio-ingressgateway HTTP hostPort: 31000 - containerPort: 31001 # Prometheus hostPort: 31001 - containerPort: 31002 # Grafana hostPort: 31002 - containerPort: 31003 # Kiali hostPort: 31003 - containerPort: 31004 # Tracing hostPort: 31004 - containerPort: 31005 # kube-ops-view hostPort: 31005 networking: podSubnet: 10.20.0.0/16 serviceSubnet: 10.200.0.0/24 EOF # 설치 확인 docker ps cat east-kubeconfig kubectl get node --kubeconfig=./east-kubeconfig kubectl get pod -A --kubeconfig=./east-kubeconfig # 컨트롤 플레인 노드에 유틸리티 설치 docker exec -it east-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y' # (옵션) kube-ops-view 설치 helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=31005 --set env.TZ="Asia/Seoul" --namespace kube-system --kubeconfig=./east-kubeconfig kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view --kubeconfig=./east-kubeconfig # 대시보드 접속 확인 open "http://localhost:31005/#scale=1.5"
테스트용 PC 컨테이너 (mypc) 생성
# kind 브리지 네트워크 확인 docker network ls docker inspect kind # mypc 컨테이너 실행 (고정 IP 할당 실패 시 생략 가능) docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # 또는 IP 미지정 버전 docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # 클러스터 노드 IP 확인 docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}' # ping 테스트로 네트워크 확인 docker exec -it mypc ping -c 1 west-control-plane docker exec -it mypc ping -c 1 east-control-plane docker exec -it west-control-plane ping -c 1 mypc
MetalLB 설치 및 LoadBalancer 테스트
# west 클러스터에 MetalLB 설치 kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml --kubeconfig=./west-kubeconfig # east 클러스터에도 설치 kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml --kubeconfig=./east-kubeconfig # IPAddressPool 및 L2Advertisement 설정 - west cat << EOF | kubectl apply --kubeconfig=./west-kubeconfig -f - apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: default namespace: metallb-system spec: addresses: - 172.18.255.101-172.18.255.120 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: default namespace: metallb-system spec: ipAddressPools: - default EOF # east 클러스터도 동일하게 설정 (다른 IP 대역 사용) cat << EOF | kubectl apply --kubeconfig=./east-kubeconfig -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
nginx 테스트 배포 및 확인
# west 클러스터에 nginx 배포 cat << EOF | kubectl apply --kubeconfig=./west-kubeconfig -f - apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - port: 80 targetPort: 80 type: LoadBalancer EOF # east 클러스터에도 동일하게 배포 cat << EOF | kubectl apply --kubeconfig=./east-kubeconfig -f - apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - port: 80 targetPort: 80 type: LoadBalancer EOF # LoadBalancer IP 확인 kubectl get svc nginx-service --kubeconfig=./west-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}' kubectl get svc nginx-service --kubeconfig=./east-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}' # mypc 컨테이너에서 curl 테스트 docker exec -it mypc curl -s $WNIP docker exec -it mypc curl -s $ENIP
자주 사용하는 alias 설정
# kubeconfig 별칭 alias kwest='kubectl --kubeconfig=./west-kubeconfig' alias keast='kubectl --kubeconfig=./east-kubeconfig' # istioctl 접근 별칭 alias iwest='docker exec -it west-control-plane istioctl' alias ieast='docker exec -it east-control-plane istioctl'
9장에서 다룬 워크로드 ID 부트스트래핑 과정에서는 단순화를 위해, 이스티오 설치 시 자동으로 CA가 생성된다는 사실을 생략했습니다.
실제로 이 CA는 istio-system
네임스페이스에 istio-ca-secret
이라는 이름으로 저장되며, 모든 istiod
인스턴스에서 공유됩니다.
그러나 기본 CA 대신 사용자 정의 CA(플러그인 CA)를 사용하도록 구성할 수 있습니다.
이 경우, CA 인증서를 cacerts
라는 시크릿으로 제공하여 Istio가 자체 CA 생성 대신 해당 인증서를 참조하도록 설정합니다.
이 시크릿에는 다음과 같은 파일이 포함되어야 합니다:
ca-cert.pem
: 중간 CA 인증서ca-key.pem
: 중간 CA 개인 키root-cert.pem
: 루트 CA 인증서cert-chain.pem
: 중간 CA 인증서 + 루트 CA 인증서를 결합한 체인인증서 생성 스크립트
step CLI
를 이용해 루트 CA 및 각 클러스터의 중간 CA를 생성하는 스크립트입니다.#!/bin/bash set -ex cert_dir=`dirname "$BASH_SOURCE"`/../certs echo "Clean up contents of dir './chapter12/certs'" rm -rf ${cert_dir} echo "Generating new certificates" mkdir -p ${cert_dir}/west-cluster mkdir -p ${cert_dir}/east-cluster # step CLI 설치 여부 확인 if ! [ -x "$(command -v step)" ]; then echo 'Error: Install the smallstep cli (https://github.com/smallstep/cli)' exit 1 fi # 루트 CA 생성 step certificate create root.istio.in.action ${cert_dir}/root-cert.pem ${cert_dir}/root-ca.key \ --profile root-ca --no-password --insecure --san root.istio.in.action \ --not-after 87600h --kty RSA # west 클러스터용 중간 CA step certificate create west.intermediate.istio.in.action ${cert_dir}/west-cluster/ca-cert.pem ${cert_dir}/west-cluster/ca-key.pem \ --ca ${cert_dir}/root-cert.pem --ca-key ${cert_dir}/root-ca.key \ --profile intermediate-ca --not-after 87600h --no-password --insecure --san west.intermediate.istio.in.action --kty RSA # east 클러스터용 중간 CA step certificate create east.intermediate.istio.in.action ${cert_dir}/east-cluster/ca-cert.pem ${cert_dir}/east-cluster/ca-key.pem \ --ca ${cert_dir}/root-cert.pem --ca-key ${cert_dir}/root-ca.key \ --profile intermediate-ca --not-after 87600h --no-password --insecure --san east.intermediate.istio.in.action --kty RSA # 인증서 체인 생성 cat ${cert_dir}/west-cluster/ca-cert.pem ${cert_dir}/root-cert.pem > ${cert_dir}/west-cluster/cert-chain.pem cat ${cert_dir}/east-cluster/ca-cert.pem ${cert_dir}/root-cert.pem > ${cert_dir}/east-cluster/cert-chain.pem
생성된 인증서 구조 확인
tree ch12/certs # 결과 ch12/certs ├── east-cluster │ ├── ca-cert.pem # 중간 CA 인증서 │ ├── ca-key.pem # 중간 CA 개인 키 │ └── cert-chain.pem # 인증서 체인 ├── root-ca.key # 루트 개인 키 ├── root-cert.pem # 루트 인증서 └── west-cluster ├── ca-cert.pem ├── ca-key.pem └── cert-chain.pem
인증서의 유효성은
openssl
명령어로 검증할 수 있습니다.# 루트 CA openssl x509 -in ch12/certs/root-cert.pem -noout -text # 중간 CA openssl x509 -in ch12/certs/east-cluster/ca-cert.pem -noout -text openssl x509 -in ch12/certs/west-cluster/ca-cert.pem -noout -text # 체인 openssl x509 -in ch12/certs/east-cluster/cert-chain.pem -noout -text openssl x509 -in ch12/certs/west-cluster/cert-chain.pem -noout -text
각 클러스터에 CA 시크릿 설정
# west 클러스터용 시크릿 생성 kwest create namespace istio-system kwest create secret generic cacerts -n istio-system \ --from-file=ch12/certs/west-cluster/ca-cert.pem \ --from-file=ch12/certs/west-cluster/ca-key.pem \ --from-file=ch12/certs/root-cert.pem \ --from-file=ch12/certs/west-cluster/cert-chain.pem # east 클러스터용 시크릿 생성 keast create namespace istio-system keast create secret generic cacerts -n istio-system \ --from-file=ch12/certs/east-cluster/ca-cert.pem \ --from-file=ch12/certs/east-cluster/ca-key.pem \ --from-file=ch12/certs/root-cert.pem \ --from-file=ch12/certs/east-cluster/cert-chain.pem
시크릿이 정상적으로 적용되었는지 확인합니다:
# 네임스페이스 및 시크릿 확인 for i in west east; do echo ">> Cluster: $i"; kubectl get ns istio-system --kubeconfig=./$i-kubeconfig; echo; done for i in west east; do echo ">> Cluster: $i"; kubectl get secret cacerts -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
이제 플러그인 CA 인증서 구성까지 완료되었으므로, 이스티오 컨트롤 플레인을 설치할 준비가 끝났습니다.
컨트롤 플레인은 사용자 정의 중간 CA를 사용하여 각 워크로드에 대한 인증서를 서명하게 됩니다.
Istio의 컨트롤 플레인을 설치하기 전에, 먼저 각 클러스터의 네트워크 메타데이터를 설정해야 합니다.
이 정보를 통해 Istio는 클러스터 간의 네트워크 토폴로지를 인식할 수 있으며, 워크로드 설정 및 트래픽 라우팅에 활용됩니다.
Istio는 MeshNetwork
설정을 통해 토폴로지를 정의할 수 있지만, 대부분의 경우 간단한 네임스페이스 레이블 방식이 선호됩니다.
west-network
east-network
istio-system
# west 클러스터에 네트워크 레이블 설정
kwest label namespace istio-system topology.istio.io/network=west-network
# east 클러스터에 네트워크 레이블 설정
keast label namespace istio-system topology.istio.io/network=east-network
# 레이블 적용 확인
for i in west east; do
echo ">> k8s cluster : $i <<";
kubectl get ns istio-system --show-labels --kubeconfig=./$i-kubeconfig;
echo;
done
이제 각 클러스터는 자신의 네트워크 이름을 명확하게 갖게 되었으며, Istio는 이를 기반으로 워크로드의 라우팅 경로 및 트래픽 정책을 자동으로 최적화할 수 있습니다.
Istio를 설치할 때는 직접 명령어 기반으로 설치하는 대신, IstioOperator 리소스를 사용하는 것이 더욱 유연하고 반복 가능한 방식입니다.
이번 실습에서는 각 클러스터에 IstioOperator를 정의하여 east-west 게이트웨이 없이도 클러스터별로 컨트롤 플레인을 구성합니다.
west 클러스터용 IstioOperator 설정
# cat ./ch12/controlplanes/cluster-west.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: istio-controlplane namespace: istio-system spec: profile: demo components: egressGateways: - name: istio-egressgateway enabled: false values: global: meshID: usmesh multiCluster: clusterName: west-cluster network: west-network
east 클러스터용 IstioOperator 설정
# cat ./ch12/controlplanes/cluster-east.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: istio-controlplane namespace: istio-system spec: profile: demo components: egressGateways: - name: istio-egressgateway enabled: false values: global: meshID: usmesh multiCluster: clusterName: east-cluster network: east-network
west-control-plane에서 Istio 설치 및 부가 도구 배포
docker exec -it west-control-plane bash # istioctl 설치 export ISTIOV=1.17.8 echo 'export ISTIOV=1.17.8' >> /root/.bashrc curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh - cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl # IstioOperator 리소스 작성 및 적용 cat << EOF > west-istio.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: istio-controlplane namespace: istio-system spec: profile: demo components: egressGateways: - name: istio-egressgateway enabled: false values: global: meshID: usmesh multiCluster: clusterName: west-cluster network: west-network EOF # Istio 설치 istioctl install -f west-istio.yaml --set values.global.proxy.privileged=true -y # 부가 도구 설치 kubectl apply -f istio-$ISTIOV/samples/addons exit
NodePort 및 외부 접근용 서비스 설정 (west)
# ingressgateway NodePort 및 externalTrafficPolicy 설정 kwest patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}' kwest patch svc -n istio-system istio-ingressgateway -p '{"spec": {"externalTrafficPolicy": "Local"}}' # 관측 도구 NodePort 설정 kwest patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}' kwest patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}' kwest patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}' kwest patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
정상적으로 kiali등 애드온에 접근 가능한지 확인!
east-control-plane에서도 동일하게 Istio 설치
docker exec -it east-control-plane bash # istioctl 설치 export ISTIOV=1.17.8 echo 'export ISTIOV=1.17.8' >> /root/.bashrc curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh - cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl # IstioOperator 작성 cat << EOF > east-istio.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: istio-controlplane namespace: istio-system spec: profile: demo components: egressGateways: - name: istio-egressgateway enabled: false values: global: meshID: usmesh multiCluster: clusterName: east-cluster network: east-network EOF # Istio 설치 istioctl install -f east-istio.yaml --set values.global.proxy.privileged=true -y # 부가 도구 설치 kubectl apply -f istio-$ISTIOV/samples/addons exit
NodePort 및 외부 접근용 서비스 설정 (east)
keast patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 31001}]}}' keast patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 31002}]}}' keast patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 31003}]}}' keast patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 31004}]}}'
east cluster 역시 애드온을 확인합니다.
istioctl alias 설정 및 상태 확인
# alias 등록 alias iwest='docker exec -it west-control-plane istioctl' alias ieast='docker exec -it east-control-plane istioctl' # proxy status 확인 iwest proxy-status ieast proxy-status # 인증서 확인 iwest proxy-config secret deploy/istio-ingressgateway.istio-system ieast proxy-config secret deploy/istio-ingressgateway.istio-system # 기타 config 확인 명령 iwest proxy-config listener deploy/istio-ingressgateway.istio-system iwest proxy-config route deploy/istio-ingressgateway.istio-system ...
현재 상태에서는 각 클러스터마다 개별 Istio 메시를 형성하고 있으며, 각각 istiod
복제본과 ingressgateway
를 갖습니다.
다음 단계에서는 클러스터 간 워크로드 디스커버리 및 연결 설정을 추가해야 메시가 하나로 통합됩니다.
그러기 전에, 클러스터 내에서 테스트할 워크로드를 먼저 배포해 기능 검증을 준비합니다.
컨트롤 플레인 설치가 완료되었으니, 이제 각 클러스터에 워크로드를 배포해봅니다.
이를 통해 Istio의 기본적인 워크로드 관리와 프록시 연동 상태를 점검할 수 있습니다.
west-cluster에 webapp 및 stub catalog 서비스 배포
# 네임스페이스 생성 및 사이드카 자동 주입 활성화 kwest create ns istioinaction kwest label namespace istioinaction istio-injection=enabled # webapp 및 가상 서비스, 게이트웨이 배포 kwest -n istioinaction apply -f ch12/webapp-deployment-svc.yaml kwest -n istioinaction apply -f ch12/webapp-gw-vs.yaml # stub catalog 서비스 생성 (DNS 해석 목적) kwest -n istioinaction apply -f ch12/catalog-svc.yaml
stub catalog 서비스란?
이 서비스는 실제로 파드가 없는 dummy 서비스입니다.
webapp이catalog.istioinaction.svc.cluster.local
을 FQDN으로 해석해 ClusterIP로 요청을 보낼 수 있게 하며,
이 요청이 Envoy 프록시로 전달되어 실제 cross-cluster 트래픽 처리가 가능해집니다.# ch12/catalog-svc.yaml 내용 apiVersion: v1 kind: Service metadata: labels: app: catalog name: catalog spec: ports: - name: http port: 80 protocol: TCP targetPort: 3000 selector: app: catalog
# 리소스 상태 확인 kwest get deploy,pod,svc,ep -n istioinaction kwest get svc,ep catalog -n istioinaction kwest get gw,vs,dr -A # 프록시 동기화 상태 확인 iwest proxy-status # Envoy에 설정된 catalog 관련 정보 확인 iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
east-cluster에 실제 catalog 워크로드 배포
# 네임스페이스 생성 및 사이드카 자동 주입 keast create ns istioinaction keast label namespace istioinaction istio-injection=enabled # 실제 catalog 애플리케이션 배포 keast -n istioinaction apply -f ch12/catalog.yaml
# 리소스 확인 keast get deploy,pod,svc,ep -n istioinaction keast get gw,vs,dr -A # east는 VirtualService 없이 단순 서비스만 존재 # 프록시 상태 및 catalog 라우팅 정보 확인 ieast proxy-status ieast proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
현재 상태는 각 클러스터에 webapp과 catalog가 배포된 상태입니다.
그러나 클러스터 간 디스커버리 설정이 적용되지 않은 상태이므로,
webapp의 Envoy 프록시는 여전히 원격 클러스터(east)의 catalog 엔드포인트를 알지 못합니다.
다음 단계에서는 클러스터 간 디스커버리를 활성화하여,
webapp이 실제로 east-cluster의 catalog 서비스에 트래픽을 보낼 수 있도록 구성하겠습니다.
다중 클러스터 서비스 메시에서 Istio 컨트롤 플레인이 원격 클러스터의 워크로드 정보를 쿼리할 수 있도록 하려면,
서비스 어카운트 기반 인증과 접근 시크릿 설정이 필요합니다.
Istio는 기본적으로 istio-reader-service-account
를 생성합니다.
이 서비스 어카운트는 최소 권한 읽기 전용 권한을 가지며 다른 컨트롤 플레인에서 이 ID로 인증해 서비스, 엔드포인트, 파드 등의 정보를 가져갈 수 있도록 설정됩니다.
그러나 이 정보를 다른 클러스터에 전달하려면 create-remote-secret
명령어로 접근 토큰이 포함된 시크릿을 만들어야 합니다.
east-cluster 접근을 위한 remote-secret 생성 및 west-cluster에 적용
# east-cluster의 istio-reader-service-account 정보 확인 keast describe sa -n istio-system istio-reader-service-account keast get sa -n istio-system istio-reader-service-account -o yaml keast get secret -n istio-system $(keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}') -o json # 접근 권한 요약 확인 (rolesum, auth can-i) kubectl rolesum istio-reader-service-account -n istio-system --kubeconfig=./east-kubeconfig keast auth can-i --as=system:serviceaccount:istio-system:istio-reader-service-account --list
# east에서 remote-secret 생성 후 west에 적용 ieast x create-remote-secret --name="east-cluster" | kwest apply -f -
# west istiod 로그에서 원격 클러스터 등록 확인 kwest logs deploy/istiod -n istio-system | grep 'Adding cluster' # west의 istio-ingressgateway에 등록된 catalog 엔드포인트 확인 iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
west-cluster 접근을 위한 remote-secret 생성 및 east-cluster에 적용
# west에서 remote-secret 생성 후 east에 적용 iwest x create-remote-secret --name="west-cluster" | keast apply -f - # east istiod 로그에서 클러스터 등록 확인 keast logs deploy/istiod -n istio-system | grep 'Adding cluster' # east의 istio-ingressgateway에 등록된 webapp 엔드포인트 확인 ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep webapp
상태 확인
- west에서는 catalog 엔드포인트로 라우팅할 수 있는 상태입니다.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||catalog.istioinaction.svc.cluster.local' -o json # 10.20.0.15:3000 등 east 네트워크의 IP가 포함됨
- east에서도 webapp 관련 엔드포인트를 확인할 수 있습니다.
ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep webapp # 10.10.0.x 등 west 네트워크의 IP가 포함됨
주의: Stub 서비스는 여전히 endpoint 없음
# west의 stub catalog 서비스 kwest get svc,ep -n istioinaction # endpoints/catalog: <none> # east에는 stub webapp 서비스가 없으므로, west의 webapp 정보는 서비스 목록에 없음 keast get svc,ep -n istioinaction # endpoints/catalog: 10.20.0.8:3000
이제 컨트롤 플레인은 상대 클러스터의 워크로드 정보를 성공적으로 조회할 수 있는 상태입니다.
webapp stub service 생성하지 않았으므로 별도 west 의 webapp service 정보가 없습니다.
다음 단계는 클러스터 간 연결 설정입니다.
다중 클러스터 환경에서 클러스터 간 연결을 설정하는 것은 서비스 메시의 핵심 기능 중 하나입니다.
이 절에서는 Istio에서 제공하는 east-west 게이트웨이를 활용해 다른 클러스터의 워크로드에 연결하는 방법을 다룹니다.
먼저 트래픽의 방향성에 따라 구분되는 개념을 정리해보겠습니다.
North-South 트래픽은 외부에서 내부로 또는 내부에서 외부로 흐르는 트래픽입니다.
Istio에서는 인그레스 게이트웨이를 통해 이 트래픽을 처리합니다.
East-West 트래픽은 내부 네트워크 간, 특히 다른 클러스터의 네트워크 사이를 오가는 트래픽을 의미합니다.
예: webapp(west 클러스터)이 catalog(east 클러스터)에 요청을 보내는 트래픽
아래 다이어그램은 이 둘을 시각적으로 구분합니다.
네트워크 피어링 vs Istio 게이트웨이
대부분의 클라우드에서는 VPC(가상 네트워크) 간에 피어링(Peering)을 통해 네트워크를 직접 연결할 수 있습니다.
이 경우에는 서로의 IP 주소를 직접 참조하여 통신이 가능합니다.
하지만 피어링은 다음과 같은 한계를 가집니다:
이러한 제한을 극복하기 위해 Istio는 east-west 게이트웨이를 제공합니다.
이제까지 우리는:
그러나 아직까지는 webapp이 catalog로 직접 연결할 수 없습니다.
왜냐하면 서로 다른 네트워크에 있기 때문에, 클러스터 간 트래픽을 전달할 게이트웨이가 없기 때문입니다.
따라서 이제부터 할 일은 다음과 같습니다:
이를 통해 완전한 다중 클러스터 메시의 연결성을 실현하게 됩니다.
다음 스텝부터 실습을 통해 구체적으로 구성해보겠습니다.
east-west 게이트웨이는 클러스터 간 트래픽의 진입점 역할을 하며, 이 과정을 서비스 운영자에게 투명하게 처리하는 것을 목표로 합니다.
이를 위해 east-west 게이트웨이는 다음 기능을 수행해야 합니다:
운영자는 추가적인 Istio 리소스 없이도 클러스터 간 라우팅이 동작하도록 설정할 수 있어야 합니다.
즉, 클러스터 내부 라우팅과 클러스터 간 라우팅이 동일한 방식으로 작동합니다.
단, 클러스터 간 로드 밸런싱 방식에만 약간 차이가 있습니다 (이는 다음 절에서 다룸).
SNI 클러스터로 east-west 게이트웨이 설정하기
east-west 게이트웨이는 단순한 인그레스 게이트웨이가 아닙니다.
모든 서비스에 대해 SNI 클러스터를 추가로 구성한 특수한 형태의 게이트웨이입니다.
SNI 클러스터란 무엇인가?
SNI 클러스터는 일반적인 Envoy 클러스터와 유사하지만,
을 포함해 유사한 워크로드 그룹을 정의합니다.
그러나 가장 큰 차이점은 모든 클러스터 정보가 SNI(Server Name Indication) 필드 안에 인코딩된다는 것입니다.
SNI가 동작하는 방식
클라이언트(예: webapp
)가 원격 클러스터의 워크로드(예: catalog
)로 커넥션을 시작할 때 SNI 필드 안에 목적 클러스터 정보가 인코딩됩니다.
예를 들어 SNI에는 다음 정보들이 포함됩니다:
outbound
(방향)catalog.istioinaction.svc.cluster.local
(FQDN)이 정보를 기반으로 게이트웨이는 SNI 헤더를 읽고 해당 트래픽을 정확히 의도한 워크로드로 암호화된 상태로 프록시할 수 있습니다.
핵심 요약
east-west 게이트웨이와 SNI 클러스터를 활용하면 클러스터 간 연결을 서비스 운영자 입장에서 추가 구성 없이도 투명하게 설정할 수 있습니다.
트래픽은 안전하게 암호화되며, 워크로드 간의 상호 인증도 보장됩니다.
결과적으로, 다중 클러스터 환경에서도 단일 클러스터처럼 트래픽이 관리될 수 있습니다.
east-west 게이트웨이는 단순히 인그레스 게이트웨이가 아니라,
SNI 클러스터를 기반으로 클러스터 간 트래픽을 라우팅하는 특수한 게이트웨이입니다.
이를 위해 Istio는 옵트인 방식으로 ISTIO_META_ROUTER_MODE
를 sni-dnat
으로 설정하도록 요구합니다.
IstioOperator로 east-west gateway 설치하기
# 예제 파일: ch12/gateways/cluster-east-eastwest-gateway.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: istio-eastwestgateway namespace: istio-system spec: profile: empty components: ingressGateways: - name: istio-eastwestgateway label: istio: eastwestgateway app: istio-eastwestgateway topology.istio.io/network: east-network enabled: true k8s: env: - name: ISTIO_META_ROUTER_MODE value: sni-dnat - name: ISTIO_META_REQUESTED_NETWORK_VIEW value: east-network service: ports: - name: status-port port: 15021 targetPort: 15021 - name: mtls port: 15443 targetPort: 15443 - name: tcp-istiod port: 15012 targetPort: 15012 - name: tcp-webhook port: 15017 targetPort: 15017 values: global: meshID: usmesh multiCluster: clusterName: east-cluster network: east-network
💡
profile: empty
로 설정하면 불필요한 컴포넌트 없이 east-west 게이트웨이만 설치됩니다.
💡ISTIO_META_ROUTER_MODE: sni-dnat
를 설정하면 SNI 클러스터 자동 구성이 활성화됩니다.
설치 및 상태 확인 (east 클러스터)
# east 클러스터로 파일 복사 및 설치 docker cp ./ch12/gateways/cluster-east-eastwest-gateway.yaml east-control-plane:/cluster-east-eastwest-gateway.yaml ieast install -f /cluster-east-eastwest-gateway.yaml --set values.global.proxy.privileged=true -y
# 설치 결과 확인 keast get IstioOperator -n istio-system keast get pod -n istio-system -l app=istio-eastwestgateway
istio-eastwestgateway
가 정상 실행 중인지 확인proxy-status
및 proxy-config cluster
에서 SNI 클러스터 확인webapp → catalog 트래픽 흐름 점검
# west 클러스터에서 webapp이 호출하는 catalog는 실제 east 클러스터의 catalog 서비스 kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
출력:
Trying 10.100.0.248:80...
* connect to 10.100.0.248 port 80 failed: Connection refused
❗️ 이유: 현재 catalog 서비스의 endpoint는 stub이며 실제 catalog로 라우팅되지 않음.
east-west gateway에 대한 mTLS 자동 통과 설정이 아직 되어 있지 않기 때문입니다.
SNI auto passthrough 모드로 mTLS 포트 노출하기
SNI 클러스터 구성은 ISTIO_META_ROUTER_MODE=sni-dnat
으로 활성화됩니다.
그러나 이를 통해 트래픽이 실제 워크로드로 도달하게 하려면,
east-west gateway가 SNI auto passthrough 모드로 동작해야 합니다.
ROUTING CROSS-CLUSTER TRAFFIC USING SNI AUTO PASSTHROUGH
수동 SNI 통과와의 차이
과거에는 인그레스 게이트웨이가 SNI 헤더를 기반으로 트래픽을 허용하도록 구성할 수 있었지만,
해당 트래픽을 실제 워크로드로 라우팅하려면 서비스 운영자가 수동으로 VirtualService 리소스를 정의해야 했습니다.
자동 SNI 통과란?
이름 그대로, VirtualService 리소스를 따로 만들지 않아도
Istio가 자동으로 게이트웨이에 SNI 클러스터를 설정해
SNI 기반 트래픽을 자동 라우팅할 수 있게 됩니다.
Gateway 리소스 구성 예시
# ch12/gateways/expose-services.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: cross-network-gateway namespace: istio-system spec: selector: istio: eastwestgateway servers: - port: number: 15443 name: tls protocol: TLS tls: mode: AUTO_PASSTHROUGH hosts: - "*.local" # 모든 Kubernetes 서비스에 해당
💡 15443 포트는 멀티 클러스터 mTLS 트래픽에 특화된 포트입니다.
east 클러스터에 적용
# east 클러스터에 cross-network-gateway 적용 keast apply -n istio-system -f ch12/gateways/expose-services.yaml # 적용 확인 keast get gw -n istio-system
west → east catalog 서비스 호출 테스트
# west 클러스터에서 webapp → catalog 호출 시도 kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
이전에는 Connection refused
였던 호출이 east 클러스터의 catalog 서비스로 정상 라우팅됩니다.
SNI 자동 통과 설정 상태 확인
# east 클러스터의 eastwestgateway에 자동으로 설정된 listener 확인 ieast proxy-config listener deploy/istio-eastwestgateway.istio-system | grep istioinaction # 출력 예시 0.0.0.0 15443 SNI: outbound_.80_._.catalog.istioinaction.svc.cluster.local → Cluster: outbound_.80_._.catalog.istioinaction.svc.cluster.local
SNI 클러스터에 기반한 라우팅 구성이 자동으로 되어 있음
west 클러스터에도 east-west 게이트웨이 및 Gateway 리소스 적용
# west 클러스터에 IstioOperator로 게이트웨이 설치 iwest install -f /cluster-west-eastwest-gateway.yaml --set values.global.proxy.privileged=true -y # cross-network-gateway 리소스도 함께 적용 kwest apply -n istio-system -f ch12/gateways/expose-services.yaml
SNI 클러스터 확인
# SNI 클러스터가 자동으로 구성되어 있는지 확인 ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system | grep catalog # → outbound_.80_._.catalog.istioinaction.svc.cluster.local # listener에서도 SNI 헤더 기반 설정 확인 ieast proxy-config listener deploy/istio-eastwestgateway.istio-system --port 15443 -o json
VALIDATING CROSS-CLUSTER WORKLOAD DISCOVERY
east-cluster → west-cluster 연결 확인
이제 east-cluster의 catalog 워크로드가 west-cluster의 webapp에 의해 참조될 수 있는 상태입니다.
webapp의 Envoy는 catalog를 위한 엔드포인트가 east-west 게이트웨이 주소로 구성되어 있어야 하며, 이를 확인합니다.
# east 클러스터의 east-west 게이트웨이 외부 IP 확인
keast -n istio-system get svc istio-eastwestgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
# 결과: 172.18.255.203
# webapp Envoy에 설정된 catalog 엔드포인트 확인 (west-cluster 기준)
iwest pc endpoints deploy/webapp.istioinaction | grep catalog
# 결과: 172.18.255.203:15443 → east-west 게이트웨이 주소와 일치
webapp → catalog 호출 실동작 확인
# west의 istio-ingressgateway 주소 확인 EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo $EXT_IP # 결과: 172.18.255.101 # 요청 시도 docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog | jq
Kiali로 흐름 확인
- west-cluster Kiali: 트래픽 흐름은 webapp → catalog로 표시됨
- east-cluster Kiali: mTLS 기반 트래픽과 함께 TCP, HTTP 트래픽 흐름 모두 표시됨
트레이싱 정보 확인 (Jaeger)
- webapp → catalog 호출 흐름이 Jaeger에 기록됨
Envoy 프록시 로그 확인
# west - istio-ingressgateway kwest logs -n istio-system -l app=istio-ingressgateway -f # west - webapp kwest logs -n istioinaction -l app=webapp -c istio-proxy -f kwest logs -n istioinaction -l app=webapp -c webapp -f # east - istio-eastwestgateway (디버깅용 로그 레벨 설정) keast exec -it -n istio-system deploy/istio-eastwestgateway -- curl -X POST http://localhost:15000/logging?level=debug keast logs -n istio-system -l app=istio-eastwestgateway -f # east - catalog keast logs -n istioinaction -l app=catalog -c istio-proxy -f keast logs -n istioinaction -l app=catalog -c catalog -f
네트워크 레벨 패킷 캡처 (tcpdump)
# webapp 프록시에서 catalog로 나가는 트래픽 캡처 (west) kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i eth0 tcp -nn # 결과: 172.18.255.202:15443로 트래픽 발생 # catalog 프록시에서 포트 3000으로 들어오는 패킷 캡처 (east) keast exec -it -n istioinaction deploy/catalog -c istio-proxy -- sudo tcpdump -i any tcp port 3000 -w /var/lib/istio/data/dump.pcap # pcap 파일 로컬로 다운로드 후 Wireshark 확인 keast cp -n istioinaction -c istio-proxy catalog-xxxx:/var/lib/istio/data/dump.pcap ./dump.pcap
💡 워크로드들은 서로의 존재를 디스커버리하고, east-west 게이트웨이를 통과하여 mTLS 기반의 안전한 통신을 수행합니다.
Load-balancing across clusters
지역 인식 클러스터 간 로드 밸런싱을 실습하기 위한 준비가 완료되었습니다.
앞서 6장에서 예고했던 내용을 바탕으로, 이번 단계에서는 클러스터 간 트래픽이 어떻게 분산되는지를 직접 확인해보게 됩니다.
이를 위해 각 클러스터(east, west)에 간단한 백엔드 서비스를 배포하고 각 서비스는 요청을 처리한 클러스터의 이름을 반환하도록 구성되어 있습니다.
이 방식은 트래픽이 어느 클러스터에서 처리되었는지를 쉽게 확인할 수 있게 해주며 이후 실습에서는 이를 통해 지역 인식 라우팅(locality-aware routing) 과 클러스터 간 장애 극복(failover) 기능을 검증하게 됩니다.
west-cluster에 서비스 배포
# 디렉토리 구성 확인 tree ch12/locality-aware/west # 배포 리소스 확인 및 적용 cat ch12/locality-aware/west/simple-backend-deployment.yaml cat ch12/locality-aware/west/simple-backend-svc.yaml kwest apply -f ch12/locality-aware/west/simple-backend-deployment.yaml kwest apply -f ch12/locality-aware/west/simple-backend-svc.yaml # 인그레스 설정 (게이트웨이 + VirtualService) cat ch12/locality-aware/west/simple-backend-gw.yaml cat ch12/locality-aware/west/simple-backend-vs.yaml kwest apply -f ch12/locality-aware/west/simple-backend-gw.yaml kwest apply -f ch12/locality-aware/west/simple-backend-vs.yaml
서비스가 정상적으로 배포되면
"Hello from WEST"
메시지를 응답합니다.EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
east-cluster에 서비스 배포
tree ch12/locality-aware/east cat ch12/locality-aware/east/simple-backend-deployment.yaml cat ch12/locality-aware/east/simple-backend-svc.yaml keast apply -f ch12/locality-aware/east/simple-backend-deployment.yaml keast apply -f ch12/locality-aware/east/simple-backend-svc.yaml
이 서비스는
"Hello from EAST"
메시지를 응답하도록 설정되어 있습니다.
트래픽 분산 확인
west 클러스터 인그레스를 통해 반복 호출을 시도하고, 응답 메시지에서 어느 클러스터에서 응답했는지를 확인합니다.
for i in {1..10}; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" echo done | sort | uniq -c
예시 출력:
4 "Hello from EAST" 6 "Hello from WEST"
이 결과는 기본 라운드로빈 로드 밸런싱에 따라 트래픽이 두 클러스터로 고르게 분산되고 있음을 보여줍니다.
엔드포인트 확인 및 트래픽 경로 분석
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep simple-backend
결과:
10.10.0.17:8080 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local 172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
west의 ingressgateway는 두 클러스터의 워크로드를 모두 알고 있으며, 이 두 주소로 분산 요청을 수행합니다.
추가 정보 및 구성 상태 확인
iwest proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 iwest proxy-config route deploy/istio-ingressgateway.istio-system --name http.8080 iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn simple-backend.istioinaction.svc.cluster.local
출력 정보로 로드 밸런싱 대상과 경로, 리스너 상태를 상세히 확인 가능
현재 상태는 다음과 같습니다:
지역성 정보 레이블 설정 및 반영
클러스터 간 지역 인식 라우팅을 실습하기 위해 먼저 각 클러스터의 노드에 지역 정보 레이블을 설정합니다. 이를 통해 이스티오가 워크로드의 지역성을 파악할 수 있습니다.
# west 노드에 지역/존 레이블 부여
kwest label node west-control-plane 'topology.kubernetes.io/region=westus'
kwest label node west-control-plane 'topology.kubernetes.io/zone=0'
# east 노드에도 동일하게 적용
keast label node east-control-plane 'topology.kubernetes.io/region=eastus'
keast label node east-control-plane 'topology.kubernetes.io/zone=0'
적용 결과는 get node -o yaml
명령어로 확인할 수 있습니다.
topology.kubernetes.io/region: westus
topology.kubernetes.io/zone: "0"
이스티오는 이 레이블 정보를 기반으로 지역 정보(locality) 를 설정하며, 이 정보는 EDS를 통해 각 프록시에 전파됩니다. 이를 반영하기 위해 관련 파드를 재시작합니다.
# EDS 정보 반영을 위한 파드 롤링 재시작
kwest rollout restart -n istio-system deploy/istio-ingressgateway
kwest rollout restart -n istio-system deploy/istio-eastwestgateway
kwest rollout restart -n istioinaction deploy/simple-backend-west
keast rollout restart -n istio-system deploy/istio-ingressgateway
keast rollout restart -n istio-system deploy/istio-eastwestgateway
keast rollout restart -n istioinaction deploy/simple-backend-east
이제 프록시에 전달된 endpoint 설정을 다시 확인해보면, locality 필드가 포함되어 있습니다.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
"locality": {
"region": "westus",
"zone": "0"
},
"locality": {
"region": "eastus",
"zone": "0"
}
지역성 기반 라우팅 강화를 위한 DestinationRule 적용
이스티오가 지역성을 고려해 트래픽을 라우팅하려면 수동적 헬스체크(outlier detection) 설정이 필수입니다. 이를 위해 다음과 같은 DestinationRule을 적용합니다.
# ch12/locality-aware/west/simple-backend-dr.yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: simple-backend-dr
namespace: istioinaction
spec:
host: simple-backend.istioinaction.svc.cluster.local
trafficPolicy:
connectionPool:
http:
http2MaxRequests: 10
maxRequestsPerConnection: 10
outlierDetection:
consecutive5xxErrors: 1
interval: 20s
baseEjectionTime: 30s
kwest apply -f ch12/locality-aware/west/simple-backend-dr.yaml
이제 엔드포인트 설정에서 outlier check 상태가 OK
로 나타나야 합니다.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local'
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.10.0.18:8080 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
지역 인식 라우팅 결과 확인
이제 west 클러스터의 인그레스 게이트웨이를 통해 요청을 보냈을 때, 자신과 동일한 지역의 워크로드로만 라우팅되는지를 확인합니다.
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
# 반복 요청
for i in {1..20}; do
docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
echo
done | sort | uniq -c
예상 결과는 다음과 같습니다:
20 "Hello from WEST"
이는 west 클러스터에서 들어온 요청이 동일 클러스터(west)의 워크로드로만 분산되었음을 의미합니다.
프록시 내부 라우팅 우선순위 확인
엔보이 프록시는 클러스터 간 지역 정보를 기준으로 priority 기반 우선순위 라우팅을 수행합니다. 이를 직접 확인해보겠습니다.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
{
"locality": {
"region": "westus",
"zone": "0"
}
},
{
"priority": 1,
"locality": {
"region": "eastus",
"zone": "0"
}
}
지역 인식(Locality-aware) 라우팅이 성공적으로 동작하고 있습니다.
이스티오는 각 노드의 지역 정보를 자동으로 추출하고, 이를 기반으로 워크로드의 위치를 판단하여 우선적으로 로컬 클러스터의 워크로드로 트래픽을 분산합니다.
프록시(Envoy)는 우선순위 기반 라우팅을 통해 장애 복구 상황에서도 탄력적으로 대응할 수 있는 구조를 제공합니다.
→ 이제 Istio 기반 멀티 클러스터 환경에서 고가용성 및 성능 최적화를 위한 지역 인식 라우팅까지 설정을 완료하였습니다.
장애 상황 시뮬레이션 및 자동 장애 극복 동작 확인
클러스터 간 장애 극복 기능을 검증하기 위해, 간단한 방법으로 ERROR_RATE
환경 변수를 1
로 설정하여 west 클러스터의 simple-backend 워크로드가 모든 요청을 실패하도록 유도합니다. 이후 이스티오의 이상값 감지(Outlier Detection)가 해당 인스턴스를 비정상으로 판단하고, 트래픽을 east 클러스터로 우회하게 됩니다.
# west의 워크로드에 오류율 설정
kwest -n istioinaction set env deploy simple-backend-west ERROR_RATE='1'
kwest exec -it -n istioinaction deploy/simple-backend-west -- env | grep ERROR
# 출력: ERROR_RATE=1
다른 터미널에서 요청을 반복하며 failover가 일어나는 시점을 관찰합니다.
while true; do
docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
date "+%Y-%m-%d %H:%M:%S"
sleep 1
echo
done
"Hello from WEST"
2025-05-18 09:31:21
"Hello from EAST" ← failover 발생
2025-05-18 09:31:23
프록시의 endpoint 상태도 이를 확인할 수 있습니다.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local'
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.10.0.21:8080 HEALTHY FAILED outbound|80||simple-backend.istioinaction.svc.cluster.local
172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
이 결과는 west 클러스터의 워크로드가 실패한 것으로 감지되어, 이스티오가 자동으로 east 클러스터의 인스턴스로 트래픽을 우회했음을 보여줍니다.
SNI 기반 트래픽 흐름 및 로드밸런싱 한계 이해
클러스터 간 트래픽은 상대 클러스터의 east-west 게이트웨이를 통과하며, 이는 SNI passthrough 모드로 처리됩니다. 이때 발생하는 주요 특징은 다음과 같습니다:
즉, 클러스터 간 장애 극복은 정상 동작하지만, 원격 클러스터 내 모든 인스턴스에 트래픽이 고르게 분산되지는 않습니다. 클라이언트 입장에서는 장애가 회피되었지만, 실제로는 특정 인스턴스 하나에만 집중될 수 있습니다.
원격 클러스터 내 인스턴스 수 증가 및 확인
트래픽 집중 현상을 관찰하기 위해 east 클러스터의 인스턴스 수를 2개로 확장해봅니다.
keast scale deploy -n istioinaction simple-backend-east --replicas 2
keast get pod -n istioinaction -l app=simple-backend -owide
NAME IP NODE
simple-backend-east-7b7ccfcbcf-k7fm9 10.20.0.21 east-control-plane
simple-backend-east-7b7ccfcbcf-pv85h 10.20.0.20 east-control-plane
이후 게이트웨이 로그에서 실제 트래픽이 두 인스턴스로 분산되지 않고, 커넥션 단위로 특정 IP로 고정된 것을 확인할 수 있습니다.
keast logs -n istio-system -l app=istio-eastwestgateway -f
"10.20.0.20:8080" outbound_.80_._.simple-backend.istioinaction.svc.cluster.local ...
"10.20.0.21:8080" outbound_.80_._.simple-backend.istioinaction.svc.cluster.local ...
이는 커넥션이 한번 열리면, 동일 커넥션에서 동일 백엔드로 전달되기 때문입니다.
west 클러스터에서 워크로드 실패가 발생하면, 이스티오는 자동으로 east 클러스터의 인스턴스로 트래픽을 우회합니다.
이 과정은 프록시 기반의 Outlier Detection, 지역 우선순위(priority), 그리고 SNI passthrough 설정을 통해 구현됩니다.
다만, east-west 게이트웨이는 TLS를 종료하지 않기 때문에 요청 단위 로드밸런싱은 불가능하며, 커넥션 단위로만 트래픽이 분산됩니다.
이를 고려해 워크로드 설계와 수평 확장 전략을 병행해야, 장애 극복 시에도 안정적인 트래픽 분산이 가능합니다.
Verifying cross-cluster access control using Authorization Policies
시나리오: 인그레스 게이트웨이 출처 트래픽만 허용
이번 단계에서는 클러스터 간 트래픽에 대해 접근 제어를 설정하는 방법을 확인합니다.
이를 위해 다음과 같은 시나리오를 가정합니다:
“인그레스 게이트웨이를 통해 들어온 트래픽만 서비스로의 접근을 허용하고, 그 외의 출처는 모두 차단하고 싶다.”
이를 구현하려면 워크로드가 상호 인증을 통해 신뢰할 수 있는 ID 메타데이터를 갖고 있어야 하며, 그 메타데이터를 기반으로 접근 제어를 수행해야 합니다.
인가 정책 적용
AuthorizationPolicy
리소스를 정의해 simple-backend
워크로드에 대한 접근을 istio-ingressgateway 서비스 어카운트로부터의 요청만 허용하도록 설정합니다.
# west의 simple-backend를 삭제하여 east에서만 요청을 처리하게 만듭니다 (선택적)
kwest delete deploy simple-backend-west -n istioinaction
# 정책 파일 내용 확인
cat ch12/security/allow-only-ingress-policy.yaml
apiVersion: "security.istio.io/v1beta1"
kind: "AuthorizationPolicy"
metadata:
name: "allow-only-ingress"
namespace: istioinaction
spec:
selector:
matchLabels:
app: simple-backend
rules:
- from:
- source:
principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]
# east-cluster에 적용
keast apply -f ch12/security/allow-only-ingress-policy.yaml
keast get authorizationpolicy -A
정책 테스트: west 클러스터에서 요청 생성
west 클러스터에서 임시 netshoot
파드를 실행하여 테스트를 수행합니다.
# netshoot 파드 실행
kwest run netshoot -n istioinaction --rm -it --image=nicolaka/netshoot -- zsh
# webapp를 통해 접근 (정상)
curl -s webapp.istioinaction/api/catalog
# 서비스로 직접 요청 → 차단됨 (RBAC)
curl -s simple-backend.istioinaction.svc.cluster.local
# 출력: RBAC: access denied
# 인그레스를 통해 요청 → 허용됨
curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system
# 반복 호출로 Kiali 등에서 확인 가능
watch curl -s simple-backend.istioinaction.svc.cluster.local
watch 'curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system'
정책이 예상대로 인그레스를 통해 들어온 트래픽만 허용하고, 직접 접근은 거부되는 것을 확인할 수 있습니다.
이번 실습을 통해 다음과 같은 이스티오의 기능을 실제로 확인했습니다:
- 클러스터 간 로드 밸런싱
- 지역 인식 라우팅 (locality-aware routing)
- 장애 극복(failover)
- 상호 인증된 트래픽
- 워크로드 단위 인가 정책 기반 접근 제어
이러한 기능은 클러스터가 어디에 있든 상관없이, 별도의 추가 설정 없이도 단일 메시 내에서 통합된 정책과 보안 정책이 적용된다는 것을 보여줍니다.
이를 통해 이스티오가 대규모 분산 인프라에서 일관성 있는 보안 및 라우팅 제어를 제공할 수 있다는 점을 입증했습니다.
이번 실습을 통해 Istio 다중 클러스터 서비스 메시의 전체적인 구성과 운영 방법을 직접 경험해볼 수 있었습니다.
Kind 환경에서 west-cluster와 east-cluster를 구축하고, 클러스터 간 워크로드 디스커버리부터 East-West Gateway 설정, SNI 기반 자동 라우팅까지 단계별로 진행하면서 다중 클러스터 메시의 핵심 동작 원리를 이해할 수 있었습니다.
실습 과정에서 가장 인상 깊었던 부분은 클러스터 간 통신이 얼마나 투명하게 처리되는지였습니다. webapp에서 catalog 서비스를 호출할 때, 해당 서비스가 다른 클러스터에 있다는 사실을 전혀 의식하지 않아도 되었고, 이는 SNI Auto Passthrough와 east-west gateway의 조합이 만들어낸 결과였습니다.
지역 인식 라우팅과 자동 장애 극복 기능도 매우 실용적이었습니다. 로컬 클러스터의 워크로드를 우선적으로 사용하다가, 장애 발생 시 자동으로 원격 클러스터로 트래픽을 우회하는 과정을 직접 확인하면서 고가용성 아키텍처의 실제 구현 방법을 체감할 수 있었습니다. 특히 AuthorizationPolicy를 통한 클러스터 간 접근 제어는 보안 정책이 클러스터 경계를 넘어서도 일관되게 적용된다는 것을 보여주었습니다.
무엇보다 Kiali, Jaeger, istioctl 등의 관측 도구들이 다중 클러스터 환경에서도 통합된 뷰를 제공한다는 점 역시 인상적이었습니다. 복잡한 클러스터 간 트래픽 흐름을 시각화하고 디버깅할 수 있는 이런 도구들이 있어야 실제 운영 환경에서도 안정적으로 메시를 관리할 수 있을 것 같습니다.
이번 경험을 통해 다중 클러스터 서비스 메시가 단순한 기술적 확장이 아니라, 비즈니스 연속성과 운영 효율성을 위한 필수적인 인프라라는 점을 깨달았습니다. 실제 프로덕션 환경에서 이런 구성을 적용할 때는 네트워크 설계와 보안 정책에 더욱 신중하게 접근해야겠지만, 기본적인 구축 방법과 운영 노하우에 대해 충분히 익힐 수 있었습니다.