1. 개요
현대적인 클라우드 아키텍처에서는 여러 개의 쿠버네티스 클러스터를 운영하는 것이 점점 더 일반적인 선택이 되고 있다. 단일 클러스터로 모든 워크로드를 처리하려는 접근은 초기에는 단순하고 빠르게 시작할 수 있지만, 시간이 지날수록 확장성, 장애 복원력, 규정 준수, 팀 간 책임 분리 등 다양한 한계에 부딪히게 된다.
Istio는 이러한 상황에 대응할 수 있도록 여러 클러스터를 하나의 서비스 메시로 통합할 수 있는 기능을 제공한다. 이를 통해 클러스터가 분리되어 있더라도 트래픽 제어, 보안 정책, 관찰 가능성(mTLS, 로그, 메트릭) 등의 기능을 일관되게 적용할 수 있으며, 운영자는 단일 메시처럼 관리할 수 있다.
1.1 서비스 메시 확장 방식
Istio는 여러 클러스터 환경에서 서비스 메시를 구성하는 데 두 가지 주요 아키텍처 모델을 제공한다. 바로 다중 클러스터 서비스 메시(Multi-cluster Service Mesh)와 메시 연합(Mesh Federation)이다. 목적은 같지만 접근 방식은 전혀 다르며, 실습에서는 다중 클러스터 메시를 중심으로 다룬다.
다중 클러스터 서비스 메시 (Multi-cluster Service Mesh)
하나의 Istio 메시가 여러 클러스터에 걸쳐 확장된 구조다. 모든 클러스터는 동일한 메시 정책, 인증 체계, 트래픽 제어 설정을 공유하고, 워크로드는 클러스터 간 자동으로 연결된다. istioctl create-remote-secret 명령어를 통해 컨트롤 플레인 간 정보를 공유하고, 이를 기반으로 서비스 디스커버리, mTLS 인증, 지역 인식 라우팅, 장애 복구(failover) 기능까지 그대로 사용할 수 있다. 단일 조직 또는 팀 내에서 복수의 클러스터를 통합 운영하려는 환경에 적합하며, 운영 자동화와 정책 일관성에 강점을 가진다.
메시 연합 (Mesh Federation, Multi-Mesh)
완전히 독립적인 Istio 메시 두 개 이상을 서로 연결하는 방식이다. 각 메시(클러스터)는 고유한 컨트롤 플레인과 인증 체계를 가지며, 일부 서비스만 수동으로 노출하고 통신을 허용한다. 이 구조는 자동화 수준은 낮지만 보안 격리, 조직 간 운영 경계, 규제 요건 대응이 중요한 환경에서 유용하다. 예를 들어 서로 다른 팀이나 조직, 혹은 보안 정책이 다른 워크로드 간 연결이 필요할 때 메시 연합을 고려할 수 있다.
https://istio.io/latest/docs/ops/deployment/deployment-models/#multiple-clusters
이번 실습에서는 다중 클러스터 메시 모델을 중심으로 설명하며, 메시 연합은 공식 문서를 참고하자.
1.2 다중 클러스터 서비스 메시 배포 모델
Istio에서 다중 클러스터 메시를 구성할 때, 조직의 목적과 환경에 따라 선택할 수 있는 세 가지 배포 모델이 있다. 이들은 가용성, 구성 복잡도, 리소스 요구 사항 측면에서 서로 차이가 있다.
- 기본-원격 모델 (Primary-Remote, Shared Control Plane)
한 클러스터에만 Istio 컨트롤 플레인을 설치하고, 나머지 클러스터는 원격으로 연결된다. 구성이 단순하고 운영이 쉽지만, 컨트롤 플레인이 다운되면 전체 메시가 영향을 받는다는 리스크가 있다. - 기본-기본 모델 (Primary-Primary, Replicated Control Plane)
각 클러스터에 컨트롤 플레인을 독립적으로 복제해 설치한다. 고가용성과 장애 격리를 제공하지만 리소스 소모와 운영 복잡도는 증가한다. 이번 실습에서 사용되는 모델이다. - 외부 컨트롤 플레인 모델 (External Control Plane)
컨트롤 플레인은 클러스터 외부에 설치되고, 클러스터 내부는 워크로드만 포함한다. 클라우드 서비스 제공자(CSP)가 Istio를 관리형 서비스로 운영할 때 사용되며, 클러스터 내부를 경량화할 수 있다
1.3 다중 클러스터 메시를 위한 핵심 요건
다중 클러스터 메시가 제대로 동작하려면 다음 세 가지 조건을 충족해야 한다:
- 클러스터 간 워크로드 디스커버리
각 컨트롤 플레인이 다른 클러스터의 Kubernetes API 서버에 접근할 수 있어야 하며, 이를 통해 서비스와 엔드포인트 정보를 조회할 수 있다. - 클러스터 간 워크로드 연결성
DNS 해석뿐 아니라, 실제 네트워크 트래픽이 클러스터 간 전송 가능해야 한다. 이를 위해 east-west 게이트웨이 같은 인프라 구성 요소가 필요하다. - 클러스터 간 공통 신뢰 구성
서로 다른 클러스터의 워크로드가 mTLS를 사용해 서로 인증할 수 있도록 동일한 루트 CA 또는 플러그인 중간 인증서를 공유해야 한다.
이 세 가지 요건이 충족돼야 Istio의 기능이 단일 클러스터와 동일한 수준으로 다중 클러스터 환경에서도 동작하게 된다.
1. 다중 클러스터 배포에서 워크로드는 어떻게 찾는가?(Discovery)
Istio의 컨트롤 플레인인 istiod는 쿠버네티스 API 서버와 통신하여 서비스 메시 구성에 필요한 다양한 정보를 수집한다. 이 정보에는 서비스와 그 뒤에 연결된 엔드포인트, 메타데이터, 프록시 설정에 활용될 트래픽 라우팅 정보 등이 포함된다. 이를 통해 각 워크로드가 클러스터 내외부에서 어떤 방식으로 통신할지 결정된다.
하지만 이 접근 방식은 강력한 권한을 요구한다. 쿠버네티스 API 서버는 단순 조회뿐 아니라 리소스를 생성, 수정, 삭제할 수 있는 권한까지 부여받을 수 있기 때문에, 오용 시 클러스터 전체에 심각한 영향을 줄 수 있다. 특히 다중 클러스터 환경에서는 하나의 클러스터에서 다른 클러스터의 API 서버에 접근해야 하므로, 조직에 따라 보안적으로 민감한 구조일 수 있다.
Istio는 쿠버네티스의 RBAC(Role-Based Access Control)을 기반으로 API 접근을 제어한다. 이를 통해 각 컨트롤 플레인이 다른 클러스터의 API 서버에 안전하게 접근할 수 있도록 구성할 수 있다
클러스터 간 디스커버리 자동화
기술적으로 클러스터 간 워크로드 디스커버리는 동일한 원리로 동작하지만, 한 가지 중요한 전제가 있다. 원격 클러스터의 서비스 어카운트 토큰과 인증서 정보가 로컬 클러스터의 istiod에 전달되어야 한다는 점이다. 이 토큰은 istiod가 원격 API 서버에 접근하여 해당 클러스터의 워크로드 정보를 쿼리할 수 있게 해 준다.
이 과정을 수동으로 처리하는 것은 복잡해 보일 수 있지만, Istio는 이를 위해 istioctl create-remote-secret 명령어를 제공한다. 이를 통해 원격 클러스터 접근을 위한 인증 토큰과 인증서를 담은 시크릿을 자동으로 생성하고, 손쉽게 적용할 수 있다.
2. 클러스터 간 워크로드 연결(connectivity)
다중 클러스터 서비스 메시를 구성할 때 중요한 조건 중 하나는, 워크로드가 클러스터 경계를 넘어 서로 통신할 수 있어야 한다는 점이다. Istio가 워크로드 간 라우팅 설정을 구성하더라도, 네트워크 수준에서 실제 트래픽이 도달하지 못하면 서비스 메시가 제대로 동작하지 않는다.
단일 네트워크(Flat Network) 환경
클러스터 간 네트워크가 단일 네트워크(Flat Network) 상에 있거나, VPC Peering, VPN, 또는 하이브리드 클라우드 상호 연결 등을 통해 동일한 네트워크처럼 통신이 가능하다면 문제는 간단하다. 이 경우 각 워크로드는 상대 클러스터의 Pod IP 또는 Service IP에 직접 연결할 수 있으므로, Istio가 추가적인 네트워크 구성 없이도 메시를 정상 동작시킬 수 있다.
분리된 네트워크 환경
하지만 클러스터들이 서로 다른 네트워크 상에 존재하는 경우에는 문제가 달라진다. 이 경우 워크로드 간 직접 통신이 불가능하므로, 트래픽을 중계해 주는 경유 지점이 필요하다. Istio는 이를 위해 특수한 인그레스 게이트웨이를 사용한다. 이 게이트웨이는 각 클러스터의 네트워크 경계(에지)에 위치하여, 다른 클러스터에서 들어오는 트래픽을 대신 수신하고 내부 워크로드로 전달하는 역할을 수행한다.
east-west 게이트웨이
Istio에서는 이러한 클러스터 간 메시 트래픽을 처리하는 전용 인그레스 게이트웨이를 east-west 게이트웨이라고 부른다. 단순한 인그레스 역할을 넘어, 멀티 네트워크 간 경로를 안전하게 터널링 하고, 중복된 IP 대역 문제를 해결하며, mTLS 기반의 통신을 유지하는 핵심 구성 요소다.
요약하자면, 클러스터 간 연결성은 단순히 IP를 인식하는 것을 넘어서 네트워크 구조에 따라 적절한 경로 설정이 필요하며, 서로 다른 네트워크 환경에서는 반드시 east-west 게이트웨이가 필요하다.
3. 클러스터 간 공통 신뢰(mTLS)
다중 클러스터 메시에서 마지막으로 필요한 요소는 클러스터 간 공통된 신뢰(Trust)다. 이 신뢰를 기반으로 서로 다른 클러스터의 워크로드들이 mTLS를 통한 상호 인증을 수행할 수 있다.
이를 구성하는 방법은 크게 두 가지다:
1.플러그인 CA 인증서 (Plug-in CA Certificates)
클러스터 간 공통 루트 CA를 공유하는 구조다.
각 클러스터에 동일한 루트 CA가 서명한 중간 CA 인증서를 cacerts 시크릿 형태로 생성해 istio-system 네임스페이스에 배포한다. 그러면 istiod는 자체 CA를 생성하지 않고, 주어진 중간 CA를 사용해 워크로드 인증서를 서명한다.
- 장점: 간단하고 구성이 빠름
- 단점: 중간 CA 노출 시 보안 리스크 존재
- 이를 완화하기 위해 메모리 내에서만 키를 유지하거나 etcd에 저장하지 않는 구조 고려 가능
실습에서는 이 플러그인 CA 인증서 방식을 사용하여 클러스터 간 신뢰를 구성했다.
2. 다중 클러스터 서비스 메시 실습 환경 구성
실습을 위한 워크로드 및 클러스터 환경을 구성한다.
2.1 실습 환경 개요
인프라 구성 목적
실제 엔터프라이즈 환경을 모방한 인프라를 구성해, 다중 클러스터 + 다중 네트워크 + 다중 컨트롤 플레인 기반 Istio 메시를 실습한다.
두 개의 Kubernetes 클러스터를 배포하고, 클러스터 간 트래픽은 east-west 게이트웨이를 통해 연결한다. 이 구성은 Primary-Primary (복제된 컨트롤 플레인) 배포 모델을 기반으로 한다.
클러스터 구성
west-cluster
: webapp 워크로드 실행east-cluster
: catalog 워크로드 실행
catalog 서비스는 webapp 서비스를 통해 호출되는 형태이지만, 실습에서는 이 둘을 서로 다른 클러스터에 배치해 클러스터 간 통신이 원활하게 이루어지는지를 검증한다. 실제 운영 환경에서는 빈번하게 통신하는 워크로드일수록 지연 시간을 최소화하기 위해 물리적으로 가까운 위치에 배치하는 것이 바람직하다.
정리하자면, east-west 게이트웨이를 사용해 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시를 구축할 것이고, 기본-기본(Primary-Primary) 배포 모델을 사용할 것이다.
2.2 클러스터 배포 (with kind)
이번 실습에서는 kind를 사용해 로컬에서 경량 쿠버네티스 클러스터 2개를 생성한다. 각각 별도의 Kubeconfig를 사용해 독립적인 컨트롤 플레인으로 구성된다.
west-cluster 배포
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-ingrssgateway 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
kubectl get node --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'
east-cluster 배포
#
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-ingrssgateway 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'
2.3 실습 편의를 위한 추가 워크로드 구성
테스트 클라이언트용 컨테이너(mypc) 배포
kind가 생성하는 Docker 네트워크 내부에서 테스트 클라이언트 역할을 할 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 지정 실패 시 fallback
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity
컨테이너 간 IP 확인 및 통신 테스트:
# 네트워크 내 컨테이너들의 IP 확인
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
/east-control-plane 192.168.107.4
/west-control-plane 192.168.107.2
/mypc 192.168.107.3
# 이름 또는 IP로 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
docker exec -it east-control-plane ping -c 1 mypc
MetalLB 설치 및 설정
Kind 환경에서 LoadBalancer 타입 서비스를 노출하기 위해 MetalLB를 사용한다.
# 설치
# west 클러스터
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
설치 확인:
kubectl get pod -n metallb-system --kubeconfig=./west-kubeconfig
NAME READY STATUS RESTARTS AGE
controller-686c7db689-jv2sg 1/1 Running 0 49s
speaker-mvf25 1/1 Running 0 49s
kubectl get pod -n metallb-system --kubeconfig=./east-kubeconfig
NAME READY STATUS RESTARTS AGE
controller-686c7db689-fg9p5 1/1 Running 0 50s
speaker-fpltt 1/1 Running 0 50s
IPAddressPool 및 L2Advertisement 설정
kind Docker 네트워크의 IP 대역을 확인한 후, 해당 대역 내에서 MetalLB가 할당할 IP 풀을 설정한다.
# kind 네트워크 서브넷 확인
docker network inspect kind | jq -r '.[0].IPAM.Config[].Subnet'
IP 풀 구성 예시 (west):
cat << EOF | kubectl apply --kubeconfig=./west-kubeconfig -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 192.168.107.101-192.168.107.120 # 자신의 PC kind 도커네트워크에 맞게 작성
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipAddressPools:
- default
EOF
cat << EOF | kubectl apply --kubeconfig=./east-kubeconfig -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default
namespace: metallb-system
spec:
addresses:
- 192.168.107.201-192.168.107.220 # 자신의 PC kind 도커네트워크에 맞게 작성
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default
namespace: metallb-system
spec:
ipAddressPools:
- default
EOF
# 확인
kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./west-kubeconfig
kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./east-kubeconfig
명령어 사용 편의성을 위한 Alias 설정
# kubectl
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'
# docker
alias dwest='docker exec -it west-control-plane'
alias deast='docker exec -it east-control-plane'
이 구성을 기반으로 테스트 클라이언트와 두 개의 클러스터가 동일한 네트워크 상에서 통신 가능하며, MetalLB를 통해 각 클러스터에서 외부로 노출 가능한 IP를 할당할 수 있다.
2.4 플러그인 CA 인증서 설정하기
기본적으로 Istio는 설치 시 자동으로 내장된 CA(Certificate Authority)를 생성해 사용한다. 하지만 외부에서 발급한 자체 CA 인증서를 사용하는 방식도 지원한다. 이 경우 Istio는 자체적으로 CA를 생성하지 않고, 사용자가 제공한 중간 CA 인증서를 기반으로 워크로드에 대한 인증서를 서명한다.
이를 위해선 아래와 같이 cacerts라는 이름의 시크릿을 istio-system 네임스페이스에 미리 생성해두어야 한다
(기본 CA는 istio-system 네임스페이스에 istio-ca-secret이라는 시크릿으로 생성된다.)
- 9장에서 워크로드 ID를 부트스트래핑(워크로드가 자신의 정체를 입증하는 서명된 인증서를 받는 방법)을 다룰 때는 설치할 때 인증서에 서명할 CA를 생성한다는 사실을 생략했다.
- 이렇게 만들어진 CA는 이스티오를 설치한 네임스페이스에 istio-ca-secret이라는 시크릿으로 저장되고, istiod 복제본들에게 공유된다.
- 이 기본 동작은 우리의 자체 CA를 사용하도록 변경할 수 있는데, 그러면 이스티오 CA가 CA를 새로 만드는 대신 이 CA를 사용하게 된다.
- 그렇게 하려면 CA 인증서를 이스티오를 설치한 네임스페이스인 istio-system에 cacerts라는 시크릿으로 저장해야 하며, 다음과 같은 데이터를 포함해야 한다.
CA 인증서 구성
시크릿에는 다음과 같은 파일들이 포함되어야 한다:
파일명 | 설명 |
---|---|
ca-cert.pem | 중간 CA의 인증서 |
ca-key.pem | 중간 CA의 개인 키 |
root-cert.pem | 루트 CA의 인증서 (모든 클러스터가 신뢰함) |
cert-chain.pem | 중간 CA + 루트 CA를 연결한 인증서 체인 |
플러그인 CA 인증서 적용하기
클러스터별로 cacerts 시크릿을 생성한다. 먼저 istio-system 네임스페이스를 만든 뒤, 각각의 인증서를 시크릿으로 등록한다.
west-cluster
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-cluster
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 ">> k8s cluster : $i <<";
kubectl get ns istio-system --kubeconfig=./$i-kubeconfig;
echo;
done
for i in west east; do
echo ">> k8s cluster : $i <<";
kubectl get secret cacerts -n istio-system --kubeconfig=./$i-kubeconfig;
echo;
done
for i in west east; do
echo ">> k8s cluster : $i <<";
kubectl view-secret cacerts -n istio-system --all --kubeconfig=./$i-kubeconfig;
echo;
done
이제 각 클러스터에 CA 인증서 구성이 완료되었으며, 이후에는 Istio 컨트롤 플레인을 설치하면 해당 플러그인 CA를 기반으로 워크로드 인증서가 자동 서명된다.
2.5 Istiod 설치 및 워크로드 배포
Step 1: 클러스터 간 연결을 위해 네트워크에 레이블 붙이기
Istio 컨트롤 플레인을 설치하기 전, 클러스터별 네트워크 메타데이터를 설정해 두자. 이 메타데이터는 Istio가 네트워크 토폴로지를 이해할 수 있도록 도와주며, 이를 기반으로 워크로드가 더 가까운 지역의 엔드포인트를 우선 라우팅 할 수 있게 해 준다.
또한, Istio는 이 정보로 클러스터 간 통신 시 east-west 게이트웨이를 사용하도록 자동 설정할 수 있다.
가장 간단한 방법은 Istio를 설치할 네임스페이스(istio-system)에 다음과 같은 레이블을 추가하는 것이다
kwest label namespace istio-system topology.istio.io/network=west-network
keast label namespace istio-system topology.istio.io/network=east-network
Step 2: IstioOperator 리소스를 사용해 컨트롤 플레인 설치하기
IstioOperator 리소스를 통해 west와 east 클러스터에 각각 독립적인 Istio 컨트롤 플레인을 설치한다. 이 실습에서는 egress gateway를 비활성화하고, meshID, clusterName, network 값을 지정하여 Primary-Primary 구조의 멀티 클러스터 메시를 구성한다.
west-cluster
# west-control-plane 진입 후 설치 진행
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
# 컨트롤 플레인 배포
istioctl install -f west-istio.yaml --set values.global.proxy.privileged=true -y
# 보조 도구 설치
kubectl apply -f istio-$ISTIOV/samples/addons
east-cluster
docker exec -it east-control-plane bash
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
istioctl install -f east-istio.yaml --set values.global.proxy.privileged=true -y
kubectl apply -f istio-$ISTIOV/samples/addons
Step3: 포트 노출 및 모니터링 툴 접근 설정
west 클러스터의 주요 서비스(NodePort):
# istio-ingressgateway 서비스 : NodePort 변경 및 nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
kwest patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}], "externalTrafficPolicy": "Local"}}'
kwest patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "nodePort": 30001}]}}'
kwest patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "nodePort": 30002}]}}'
kwest patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "nodePort": 30003}]}}'
kwest patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
east 클러스터도 동일하게 설정하되 포트 번호만 다르게 구성:
keast patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "nodePort": 31001}]}}'
keast patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "nodePort": 31002}]}}'
keast patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "nodePort": 31003}]}}'
keast patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 31004}]}}'
Step 4: 컨트롤 플레인 설치 확인
# west
kwest get istiooperators -n istio-system -o yaml
...
meshID: usmesh
meshNetworks: {}
mountMtlsCerts: false
multiCluster:
clusterName: west-cluster
enabled: false
network: west-network
...
# east
keast get istiooperators -n istio-system -o yaml
...
meshID: usmesh
meshNetworks: {}
mountMtlsCerts: false
multiCluster:
clusterName: east-cluster
enabled: false
network: east-network
...
Step5: 워크로드 배포
이제 각 클러스터에 테스트용 워크로드를 배포한다.
west-cluster에 webapp 배포
kwest create ns istioinaction
kwest label namespace istioinaction istio-injection=enabled
kwest -n istioinaction apply -f ch12/webapp-deployment-svc.yaml
kwest -n istioinaction apply -f ch12/webapp-gw-vs.yaml
kwest -n istioinaction apply -f ch12/catalog-svc.yaml # catalog 서비스 배포
catalog 서비스는 east-cluster에서만 실제로 동작하는데, 왜 west-cluster에도 같은 이름의 서비스를 등록해야 할까?
그 이유는 webapp이 통신하고자 하는 catalog 서비스의 FQDN(정규 도메인 이름)을 올바르게 해석해야 하기 때문이다. Kubernetes 환경에서 서비스 이름은 DNS를 통해 IP 주소로 변환되는데, 만약 west-cluster에 catalog라는 이름의 서비스가 존재하지 않는다면, DNS 해석 자체가 실패하게 된다.
DNS 해석이 실패하면 요청은 Envoy 프록시까지 도달하지도 못하고 애플리케이션 레벨에서 바로 오류가 발생하게 된다. 트래픽이 사이드카 프록시로 라우팅 되기 이전 단계에서 끊겨버리는 셈이다.
그래서 east-cluster에서 catalog 서비스가 실제로 동작하더라도, west-cluster에도 이름만 같은 스텁(stub) 서비스를 만들어 놓는 것이 중요하다. 이렇게 하면 DNS는 해당 이름을 로컬 클러스터의 서비스로 인식해 클러스터 IP를 반환하고, 그다음부터는 Envoy가 실제 라우팅 경로를 참조해 클러스터 간 트래픽 전송을 정상적으로 처리할 수 있다.
east-cluster에 catalog 서비스를 배포
keast create ns istioinaction
keast label namespace istioinaction istio-injection=enabled
keast -n istioinaction apply -f ch12/catalog.yaml
하지만 클러스터 간 워크로드 디스커버리가 아직 활성화되지 않았기 때문에, 사이드카 프록시는 반대편 클러스터의 워크로드를 인식하지 못한다.
3. 다중 클러스터 서비스 메시 기능별 실습
다중 클러스터 서비스 메시 기능별 실습에서는 Istio가 제공하는 클러스터 간 디스커버리, 트래픽 연결, 인증/인가, 장애 복구, 지역 인식 라우팅 등 핵심 기능들을 단계별로 실습하며 실제 동작 방식을 확인한다.
3.1 클러스터 간 워크로드 디스커버리 활성화하기(remote-secret)
Istio에서 클러스터 간 통신을 가능하게 만들려면, 한 클러스터의 istiod가 다른 클러스터의 워크로드 정보를 조회할 수 있어야 한다. 이 조회는 Kubernetes API를 통해 이루어지며, API 접근을 위해서는 인증 정보와 권한이 필요하다.
이를 위해 Istio는 설치 시 각 클러스터에 istio-reader-service-account
라는 서비스 어카운트를 생성한다. 이 계정은 최소 권한으로 설정되어 있으며, 다른 클러스터의 Istio 컨트롤 플레인이 API 서버로부터 서비스/엔드포인트 정보를 읽어올 수 있도록 한다.
하지만 여기서 중요한 점은, 서비스 어카운트 토큰과 API 서버 인증서를 상대 클러스터에 전달해야 한다는 것이다. 그래야 상대 클러스터에서 이 정보를 기반으로 인증과 쿼리를 수행할 수 있다.
create-remote-secret 명령어로 시크릿 생성
Istio는 이런 목적을 위해 istioctl create-remote-secret 명령어를 제공한다. 이 명령어는 현재 클러스터의 istio-reader-service-account
정보를 기반으로, 다른 클러스터에서 사용할 수 있는 시크릿을 생성한다. 반드시 IstioOperator에 설정한 클러스터 이름(west-cluster, east-cluster)을 정확히 사용해야 한다.
east 클러스터의 시크릿을 west에 적용
# east-cluster에서 remote-secret 생성 후 west에 적용
ieast x create-remote-secret --name="east-cluster" | kwest apply -f -
# 로그 확인
kwest logs deploy/istiod -n istio-system | grep 'Adding cluster'
Adding cluster cluster=east-cluster secret=istio-system/istio-remote-secret-east-cluster
이제 west-cluster의 istiod는 east-cluster의 API 서버에 접근해 워크로드 정보를 조회할 수 있게 된다.
west-cluster에서 east-cluster의 Envoy 설정 정보를 조회해 보자.
# west 확인 : east 의 모든 CDS/EDS 정보를 west 에서도 확인 가능!
for i in listener route cluster endpoint; do echo ">> k8s cluster : west - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
# endpoint 설정에서 east-cluster의 catalog 서비스 정보를 확인할 수 있다.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.12:3000
하지만 stub 서비스가 존재하지 않는 경우, FQDN은 해석되더라도 엔드포인트가 빈 상태로 남게 된다.
# catalog stub service 정보 확인 : endpoints 는 아직도 none.
kwest get svc,ep -n istioinaction
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/catalog ClusterIP 10.100.2.43 <none> 80/TCP 14h
service/webapp ClusterIP 10.100.2.172 <none> 80/TCP 14h
NAME ENDPOINTS AGE
endpoints/catalog <none> 14h
endpoints/webapp 10.10.0.8:8080 14h
반면, east-cluster에는 catalog 서비스가 실제로 존재하므로 endpoint가 채워진다.
keast get svc,ep -n istioinaction
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/catalog ClusterIP 10.200.3.0 <none> 80/TCP 14h
NAME ENDPOINTS AGE
endpoints/catalog 10.20.0.8:3000 14h
반대 방향 설정 (east → west)
Primary-Primary 구조에서는 서로 쿼리가 가능해야 하므로, east에서도 west를 조회할 수 있게 시크릿을 생성해줘야 한다.
# west-cluster에서 remote-secret 생성 후 east에 적용
iwest x create-remote-secret --name="west-cluster" | keast apply -f -
# 로그로 정상 반영 확인:
keast logs deploy/istiod -n istio-system | grep 'Adding cluster'
2025-05-24T16:45:39.578265Z info Adding cluster cluster=west-cluster secret=istio-system/istio-remote-secret-west-cluster
이제 두 클러스터가 서로의 워크로드 정보를 조회할 수 있는 상태가 되었지만, 이후 통신 연결을 위한 작업이 필요하다.
3.2 클러스터 간 연결 설정하기(east-west gateway)
North-South vs. East-West 트래픽
Istio의 인그레스 게이트웨이는 외부에서 들어오는 트래픽의 진입점 역할을 한다. 퍼블릭 네트워크에서 조직 내부로 들어오는 이러한 트래픽은 흔히 north-south 트래픽이라고 부른다. 반면, 이번 장에서 다루는 east-west 트래픽은 서로 다른 내부 네트워크, 즉 클러스터 간에 이동하는 트래픽을 의미한다.
Istio의 East-West 게이트웨이란?
Istio의 east-west 게이트웨이는 클러스터 간 서비스 연결을 클러스터 내 연결처럼 만들어 주는 게이트웨이이다. 이 구조를 통해 트래픽 흐름의 일관성과 보안을 유지할 수 있다.
하지만, 이를 위해서는 다음과 같은 조건을 만족해야 한다.
- 클러스터 간 정밀한 트래픽 관리가 가능해야 한다
- 트래픽은 암호화된 상태로 전달되어야 하며, 이 과정에서 워크로드 간 상호 인증(mTLS)을 유지해야 한다
- 운영자가 별도의 리소스를 추가로 구성하지 않아도 된다
이렇게 구성되면, 클러스터 내 라우팅이든, 클러스터 간 라우팅이든 서비스 관점에서는 동일한 방식으로 서비스를 호출하여 일관성을 유지할 수 있다.
SNI 클러스터와 Auto Passthrough
이러한 게이트웨이의 동작 방식은 SNI 클러스터(SNI Cluster)와 SNI 자동 통과(Auto Passthrough)라는 두 가지 Istio 기능에 기반한다.
SNI 클러스터란?
- SNI 클러스터는 일반적인 Envoy 클러스터 설정과 유사하지만, 중요한 차이점은 트래픽 정보를 SNI(Server Name Indication)에 인코딩한다는 점이다.
- 이 방식은 라우팅을 위한 정보들 — 예: 서비스 이름, 포트, 버전, 클러스터 ID 등을 SNI에 담아 보낸다.
- 이 정보를 통해 게이트웨이는 클라이언트가 어떤 클러스터의 어떤 워크로드로 트래픽을 보내려는지를 정확히 파악할 수 있다.
실제 동작 방식 예시
- 클라이언트(webapp)가 다른 클러스터의 워크로드(catalog)로 연결을 시도하면,
- 해당 요청에는 SNI에 클러스터 정보가 인코딩 되어 전송된다.
- 게이트웨이는 이 SNI 정보를 기반으로 트래픽을 적절한 워크로드로 라우팅 한다.
- 이 모든 과정은 mTLS 기반 상호 인증을 유지한 채 안전하게 진행된다.
Auto Passthrough란?
Auto Passthrough는 Istio 게이트웨이에서 SNI 기반 트래픽을 자동으로 내부 서비스로 전달할 수 있도록 도와주는 기능이다.
보통 인그레스 게이트웨이를 사용할 때는 Gateway, VirtualService 같은 리소스를 명시적으로 만들어줘야 라우팅이 가능하다. 하지만 east-west 게이트웨이에서는 클러스터 간 트래픽을 유연하게 처리해야 하므로, 이런 리소스를 매번 따로 정의하는 건 번거롭고 비효율적이다.
Auto Passthrough는 이런 문제를 해결한다. SNI 헤더에 포함된 정보만으로도 게이트웨이가 내부 워크로드로 트래픽을 자동 전달할 수 있도록 해준다. 즉, 게이트웨이는 “아, 이 트래픽은 catalog 서비스로 가는 거구나”라는 걸 SNI 정보만 보고 알아서 판단하고 처리해 준다.
이 기능 덕분에 운영자는 VirtualService나 DestinationRule 같은 별도 설정 없이도 클러스터 간 트래픽을 자동으로 라우팅 할 수 있게 된다
SNI 클러스터 기반 east-west 게이트웨이 설치하기
Step1: IstioOperator로 east-west 게이트웨이 설치
클러스터 간 트래픽을 처리하기 위해서는 각 클러스터에 east-west 게이트웨이를 설치해야 한다. 이 게이트웨이는 SNI 클러스터 기반으로 동작하도록 설정되어야 하며, sni-dnat 모드를 통해 자동으로 SNI 기반 트래픽 라우팅 구성을 만들어준다.
먼저 east-cluster에 east-west 게이트웨이를 설치하자.
cat << EOF > cluster-east-eastwest-gateway.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio-eastwestgateway # IstioOperator 이름은 앞 선 이스티오 설정 이름과 겹치지 않아야 한다
namespace: istio-system
spec:
profile: empty # 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 # sni-dnat 모드는 트래픽을 프록시하는 데 필요한 SNI 클러스터를 추가한다
value: "sni-dnat"
# The network to which traffic is routed
- name: ISTIO_META_REQUESTED_NETWORK_VIEW # 게이트웨이가 트래픽을 라우팅하는 네트워크
value: east-network
service:
ports:
... (생략) ...
values:
global:
meshID: usmesh # 메시, 클러스터, 네트워크 식별 정보
multiCluster:
clusterName: east-cluster
network: east-network
EOF
ieast install -f /cluster-east-eastwest-gateway.yaml --set values.global.proxy.privileged=true -y
ISTIO_META_ROUTER_MODE=sni-dnat
: SNI 클러스터 자동 구성을 활성화한다. 지정하지 않으면 기본값인 standard로 설정되어 SNI 기반 라우팅이 작동하지 않는다.ISTIO_META_REQUESTED_NETWORK_VIEW=east-network
이 게이트웨이가 어느 네트워크로 라우팅할 수 있는지를 지정하는 값이다
설치 확인
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get pod -n istio-system -l istio.io/rev=default --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-5db74c978c-l8w7c 1/1 Running 0 60m
istiod-5585445f4c-85cv4 1/1 Running 0 60m
>> k8s cluster : east <<
NAME READY STATUS RESTARTS AGE
istio-eastwestgateway-866794c798-q4bxz 1/1 Running 0 11s
istio-ingressgateway-7f6f8f8d99-4dpzd 1/1 Running 0 54m
istiod-85976468f-dhvzs 1/1 Running 0 54m
Step2: SNI Auto Passthrough 설정
이제 게이트웨이에서 클러스터 간 트래픽을 자동으로 라우팅할 수 있도록 SNI Auto Passthrough 모드를 설정하자. 이는 별도의 VirtualService나 DestinationRule 없이도 SNI 헤더만으로 트래픽 목적지를 판단할 수 있게 해 준다.
# cat 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 # 이스티오에서 15443 포트는 멀티 클러스터 상호 TLS 트래픽 용도로 지정된 특수 포트다
name: tls
protocol: TLS
tls:
mode: AUTO_PASSTHROUGH # SNI 헤더를 사용해 목적지를 해석하고 SNI 클러스터를 사용한다.
hosts:
- "*.local" # 정규식 *.local 과 일치하는 SNI에 대해서만 트래픽을 허용한다.
# 배포
kwest apply -f ch12/gateways/expose-services.yaml
kwest get svc,ep -n istioinaction
NAMESPACE NAME AGE
istio-system cross-network-gateway 2s
하지만 아직 통신은 불가능하다. 이는 반대편 클러스터(west)에도 동일한 east-west 게이트웨이 구성과 노출 설정이 필요하기 때문이다.
kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
* Trying 10.100.0.42:80...
* connect to 10.100.0.42 port 80 failed: Connection refused
Step3: west 클러스터에도 east-west 게이트웨이 설치
반대편 클러스터에도 작업 수행. west-cluster에 east-west 게이트웨이를 만들고, 그 서비스를 east-cluster 워크로드에 노출.
# IstioOperator 로 west 클러스터에 east-west 게이트웨이를 설치
cat ch12/gateways/cluster-west-eastwest-gateway.yaml
docker cp ./ch12/gateways/cluster-west-eastwest-gateway.yaml west-control-plane:/cluster-west-eastwest-gateway.yaml
iwest install -f /cluster-west-eastwest-gateway.yaml --set values.global.proxy.privileged=true -y
# west 클러스터에 적용하자. east-cluster의 워크로드를 west-cluster 에 노출
kwest apply -n istio-system -f ch12/gateways/expose-services.yaml
# sni 클러스터 확인
ieast pc clusters deploy/istio-eastwestgateway.istio-system | grep catalog | awk '{printf "CLUSTER: %s\n", $1}'
CLUSTER: catalog.istioinaction.svc.cluster.local
CLUSTER: outbound_.80_._.catalog.istioinaction.svc.cluster.local
Step 4: 클러스터 간 워크로드 디스커버리 검증하기
이제 east-cluster의 워크로드가 west-cluster에서 접근 가능한 상태인지 확인해 보자.
# east-cluster의 east-west 게이트웨이 IP 확인
keast -n istio-system get svc istio-eastwestgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
192.168.107.202
이제 이 값을 west-cluster의 워크로드가 클러스터 간 트래픽을 라우팅 할 때 사용하는 주소와 비교해 보자.
iwest pc endpoints deploy/webapp.istioinaction | grep catalog
# 만약 catalog 엔드포인트가 east-west 게이트웨이 IP로 설정되어 있다면, **클러스터 간 라우팅 구성이 성공한 것**이다.
192.168.107.202:15443 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.loca
이제 실제로 요청을 날려보자.
# west 에 istio-ingressgateway 인입을 위한 접속 정보 확인
kwest get svc -n istio-system istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istio-ingressgateway LoadBalancer 10.100.0.82 172.18.255.101 15021:30627/TCP,80:30000/TCP,443:31615/TCP,31400:32694/TCP,15443:32016/TCP 119m
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $EXT_IP
192.168.107.202
# 요청 성공!
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog | jq
[
{
"id": 1,
"color": "amber",
...
# 신규 터미널 : 반복 접속
alias kwest='kubectl --kubeconfig=./west-kubeconfig'
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
while true; do docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
Step 5: 네트워크 흐름 확인
Kiali에서 mTLS 트래픽 흐름 및 클러스터 간 흐름이 시각적으로 확인된다.
로그 확인
# west: webapp
kwest logs -n istio-system -l app=istio-ingressgateway -f
[2025-05-24T17:12:58.315Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 4 4 "192.168.107.3" "curl/8.7.1" "a9099ada-e63c-9fcc-a2f2-9a09fedbdc6c" "webapp.istioinaction.io" "10.10.0.12:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.7:46134 10.10.0.7:8080 192.168.107.3:41330 - -
east-cluster의 catalog 측에서는 포트 3000에 대한 요청이 TCP 캡처로 기록된다.
# istio-proxy 에 tcp port 3000 에서 패킷 덤프에 출력 결과를 파일로 저장
keast exec -it -n istioinaction deploy/catalog -c istio-proxy -- sudo tcpdump -i any tcp port 3000 -w /var/lib/istio/data/dump.pcap
keast exec -it -n istioinaction deploy/catalog -c istio-proxy -- ls -l /var/lib/istio/data/
# 출력 결과 파일을 로컬로 다운로드
keast get pod -n istioinaction -l app=catalog -oname
pod/catalog-6cf4b97d-rq6nl
keast cp -n istioinaction -c istio-proxy catalog-6cf4b97d-rq6nl:var/lib/istio/data/dump.pcap ./dump.pcap
# 로컬로 다운 받은 파일을 wireshark 로 불러오기 : XFF 로 요청 Client IP 확인
wireshark dump.pcap
Wireshark에서 분석 시 X-Forwarded-For 헤더를 통해 요청자가 kind 네트워크 게이트웨이임을 알 수 있다.
3.3 클러스터 간 로드 벨런싱 실습
이제 두 개의 클러스터가 연결된 상태에서 클러스터 간 로드 밸런싱 동작 방식을 살펴볼 차례다. 이를 위해 각 클러스터에 자신의 이름을 응답으로 반환하는 샘플 서비스를 배포하고, 요청이 어떤 클러스터에서 처리되는지 확인해 볼 수 있다
west-cluster에 샘플 서비스 배포
먼저 west-cluster에 simple-backend라는 이름의 서비스와 디플로이먼트를 배포한다. 이 서비스는 요청을 처리한 클러스터가 “Hello from WEST”라는 메시지를 반환하도록 구성되어 있다.
kwest apply -f ch12/locality-aware/west/simple-backend-deployment.yaml
kwest apply -f ch12/locality-aware/west/simple-backend-svc.yaml
kwest get deploy -n istioinaction simple-backend-west
kwest get svc,ep -n istioinaction simple-backend
NAME READY UP-TO-DATE AVAILABLE AGE
simple-backend-west 1/1 1 1 30s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/simple-backend ClusterIP 10.100.0.203 <none> 80/TCP 30s
NAME ENDPOINTS AGE
endpoints/simple-backend 10.10.0.14:8080 30s
트래픽 수신을 위해 Ingress Gateway와 VirtualService도 함께 설정해 준다.
kwest apply -f ch12/locality-aware/west/simple-backend-gw.yaml
kwest apply -f ch12/locality-aware/west/simple-backend-vs.yaml
kwest get gw,vs,dr -n istioinaction
NAME AGE
gateway.networking.istio.io/coolstore-gateway 43m
gateway.networking.istio.io/simple-backend-gateway 3s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/simple-backend-vs-for-gateway ["simple-backend-gateway"] ["simple-backend.istioinaction.io"] 3s
virtualservice.networking.istio.io/webapp-virtualservice ["coolstore-gateway"] ["webapp.istioinaction.io"] 43m
서비스가 정상적으로 동작하는지 확인:
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"
# 출력 예시
"Hello from WEST"
실습을 위해 반복접속
alias kwest='kubectl --kubeconfig=./west-kubeconfig'
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
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
east-cluster에도 동일한 서비스 배포
이제 동일한 구성을 east-cluster에도 적용해 보자. 이 서비스는 “Hello from
EAST” 메시지를 반환한다.
keast apply -f ch12/locality-aware/east/simple-backend-deployment.yaml
keast apply -f ch12/locality-aware/east/simple-backend-svc.yaml
keast get deploy -n istioinaction simple-backend-east
keast get svc,ep -n istioinaction simple-backend
NAME READY UP-TO-DATE AVAILABLE AGE
simple-backend-east 1/1 1 1 17s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/simple-backend ClusterIP 10.200.0.37 <none> 80/TCP 17s
NAME ENDPOINTS AGE
endpoints/simple-backend 10.20.0.14:8080 17s
이제 두 클러스터에 같은 이름의 서비스가 실행되며, 클러스터 간 연결 및 디스커버리가 활성화되어 있기 때문에 Istio는 이 두 서비스를 하나의 엔드포인트 집합으로 간주한다.
요청 분산 결과 확인
이제 실제로 요청을 여러 번 반복하여 보내고, 응답이 어느 클러스터에서 왔는지 확인해 보자.
기본적으로, 이스티오는 라운드 로빈 알고리듬으로 워크로드 간에 로드 밸런싱한다. 그러므로 트래픽은 고르게 분산된다.
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
5 "Hello from EAST"
5 "Hello from WEST"
Istio는 기본적으로 라운드 로빈 알고리즘을 사용해 트래픽을 고르게 분산시킨다. 따라서 클러스터 간에도 트래픽이 균등하게 분포하는 것을 확인할 수 있다.
클러스터 간 지역 인식 라우팅 검증하기
Istio의 다중 클러스터 메시 환경에서 지역 인식 라우팅을 활성화하면, 인그레스 게이트웨이가 자신과 가까운 클러스터의 워크로드를 우선 선택하여 트래픽을 전달할 수 있다. 이 방식은 네트워크 지연을 줄이고, 성능을 개선하는 데 매우 효과적이다.
이를 검증하기 위해 먼저 각 클러스터 노드에 지역성 레이블(region, zone)을 부여하자
노드에 지역 레이블 부여
# west-control-plane
kwest label node west-control-plane 'topology.kubernetes.io/region=westus'
kwest label node west-control-plane 'topology.kubernetes.io/zone=0'
kwest get node -o yaml
...
topology.kubernetes.io/region: westus
topology.kubernetes.io/zone: "0"
...
# east-control-plane
keast label node east-control-plane 'topology.kubernetes.io/region=eastus'
keast label node east-control-plane 'topology.kubernetes.io/zone=0'
keast get node -o yaml
...
topology.kubernetes.io/region: eastus
topology.kubernetes.io/zone: "0"
...
Istio는 istiod가 이 레이블 정보를 읽어 들여 EDS(Endpoint Discovery Service)에 포함시킨다. 이를 적용하려면 관련 파드를 재기동하거나, proxy-config에서 엔드포인트 정보가 갱신됐는지 확인할 수 있다.
# 재기동 전,엔드포인트 설정 확인 시, 지역 라우팅 정보가 없음.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
...
"weight": 1,
"locality": {}
...
재기동 후, 아래 명령어로 simple-backend 서비스의 엔드포인트 설정을 확인할 수 있다.
# 재기동
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
# 재기동 후
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
...
"weight": 1,
"locality": {
"region": "eastus", # east-cluster 에 있는 워크로드의 위치 정보
"zone": "0"
}
...
"weight": 1,
"locality": {
"region": "westus", # west-cluster 에 있는 워크로드의 위치 정보
"zone": "0"
}
...
지역성 기반 라우팅을 활성화하기 위한 설정.
Istio가 지역성을 기반으로 라우팅 결정을 내리려면 DestinationRule에 OutlierDetection
설정이 포함돼 있어야 한다. 이는 엔드포인트 상태를 수동으로 확인하기 위함이다.
cat 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
kwest get gw,vs,dr -n istioinaction
지역 우선 트래픽 분산 결과 확인
설정이 전파된 뒤 요청을 여러 번 보내 보면, 트래픽이 현재 클러스터(west)의 워크로드로만 전달되는 것을 확인할 수 있다.
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
20 "Hello from WEST"
모든 요청이 로컬 클러스터(westus)의 워크로드로만 전달된 것이다. 이는 Istio가 지역 우선 정책을 적용해 라우팅 한 결과이며, 네트워크 경계를 넘지 않고 처리되어 지연도 줄어든다.
우선순위 기반 로드 밸런싱 구조 확인
Istio는 지역성 정보에 따라 엔드포인트에 우선순위(priority)를 자동으로 설정한다.
#
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
...
"weight": 1,
"locality": {
"region": "westus", # priority 가 없으면(생략 시), 0으로 우선 순위가 가장 높음
"zone": "0"
}
...
"weight": 1,
"priority": 1, # priority 0 다음으로, 두 번쨰 우선순위
"locality": {
"region": "eastus",
"zone": "0"
}
...
- priority 0: 현재 클러스터(westus)의 워크로드로, 가장 우선순위가 높음
- priority 1: 원격 클러스터(eastus)의 워크로드로, 후보군으로만 존재
즉, 로컬 워크로드가 사용 가능하면 로컬로만 라우팅 되고, 문제가 생겼을 경우에만 원격 워크로드가 fallback 대상이 된다.
정리하자면:
- 클러스터 노드에 지역성 레이블을 부여하면 Istio는 이를 기반으로 지역 인식 라우팅을 설정한다.
- DestinationRule에 outlierDetection을 추가해야 해당 정보가 반영된다.
- 이후 트래픽은 기본적으로 로컬 클러스터의 워크로드로 먼저 라우팅 되며, 네트워크 비용이 줄고 응답 속도가 개선된다.
이렇게 지역 기반 트래픽 라우팅을 적용하면, 단순한 라운드 로빈보다 더 똑똑하고 효율적인 멀티 클러스터 트래픽 제어가 가능해진다.
클러스터 간 장애 극복 확인하기
다중 클러스터 환경에서는 단일 클러스터의 워크로드가 실패하더라도, 다른 클러스터의 워크로드로 트래픽을 자동으로 전환할 수 있어야 한다. 이를 장애 극복(failover)이라고 하며, Istio에서는 DestinationRule의 outlierDetection 설정과 SNI 클러스터 기반 라우팅을 통해 이를 구현할 수 있다.
장애 상황 시뮬레이션
먼저 west-cluster의 simple-backend 워크로드가 실패하는 상황을 만들어보자. 애플리케이션에서 실패 응답을 반환하도록 환경 변수 ERROR_RATE=1을 설정한다.
# 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
잠시 후, Envoy 프락시는 해당 워크로드에서 5xx 오류가 반복되는 것을 감지하고, 설정된 outlierDetection에 따라 이를 비정상 엔드포인트로 간주하게 된다.
# 이전 실습을 통해 반복요청에 대한 상태 확인
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local'
# 엔드포인트 확인시 fail
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.10.0.18:8080 HEALTHY FAILED outbound|80||simple-backend.istioinaction.svc.cluster.local
192.168.107.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
이제 west의 트래픽은 자동으로 우선순위 2순위인 east-cluster의 워크로드로 라우팅 된다
# 반복 요청 시도
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-25 02:39:57
# failover!
"Hello from EAST"
2025-05-25 02:39:58
...
Kiali UI에서도 클러스터 간 트래픽이 전환되었음을 시각적으로 확인할 수 있다.
인가 정책을 사용해 클러스터 간 접근 제어 확인하기
장애 상황에서 클러스터 간 트래픽 전환은 유용하지만, 모든 트래픽을 허용하는 것은 위험하다. 따라서, 인가 정책(AuthorizationPolicy)을 통해 클러스터 간 트래픽의 출처를 제한해야 한다. 예를 들어, 아래와 같은 정책을 적용하면 ingress gateway를 통해 들어온 요청만 허용하고, 내부에서 직접 접근한 요청은 모두 차단된다.
# 적용 전에 west-cluster 서비스를 제거해서 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"]
keast apply -f ch12/security/allow-only-ingress-policy.yaml
keast get authorizationpolicy -
테스트를 위해 직접 simple-backend에 접근해 보면 다음과 같이 거절된다:
# 직접 요청하면 실패!
curl -s simple-backend.istioinaction.svc.cluster.local
RBAC: access denied
반면, ingress gateway를 거쳐 요청하면 정상적으로 응답을 받을 수 있다:
# istio-ingressgateway 로 요청하면 성공!
curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system
{
"name": "simple-backend-east",
"uri": "/",
"type": "HTTP",
"ip_addresses": [
"10.20.0.18"
],
"start_time": "2025-05-24T17:53:23.870126",
"end_time": "2025-05-24T17:53:24.021191",
"duration": "151.067081ms",
"body": "Hello from EAST",
"code": 200
}
Istio는 여러 클러스터 간에도 일관된 트래픽 제어와 보안, 관찰성을 제공할 수 있으며, east-west 게이트웨이와 remote-secret 등을 활용하면 안정적인 클러스터 간 통신이 가능하다. 이를 통해 클러스터 환경에서도 유연하게 서비스 메시를 확장할 수 있다.
'Kubernetes > Istio' 카테고리의 다른 글
Istio 시리즈 # 12 - Istio와 가상머신 통합하기(VM Support) (2) | 2025.06.01 |
---|---|
Istio 시리즈 # 11 - EnvoyFilter로 요청 처리 로직 확장하기 (0) | 2025.05.25 |
Istio 시리즈 # 9 – Istio 성능 튜닝 가이드(Istio Tuning) (0) | 2025.05.18 |
Istio 시리즈 # 8 - Istio 트러블 슈팅 가이드(Istio Troubleshooting) (1) | 2025.05.17 |
Istio 시리즈 # 7 – Istio Security로 살펴보는 마이크로서비스 통신 보안 (1) | 2025.05.11 |