Istio Hands-on Study - 7주차 이스티오 스케일링, 데이터 플레인 확장

김성중·2025년 5월 20일

Istio Hands-on Study

목록 보기
8/10

가시다(gasida) 님이 진행하는 Istio Hands-on Study 1기 과정을 참여하여 정리한 글입니다.
7주차는 이스티오 스케일링, 데이터 플레인 확장 주제로 학습을 하였습니다.

1. 이스티오 스케일링

1.1 다중 클러스터 서비스 메시의 잇점

가상회사 ACME는 클라우드 마이그레이션 초기에 단일 클러스터를 선택했지만, 곧 다수의 소규모 클러스터 전략으로 전환했다. 그 이유는 다음과 같다:

  • 격리성 강화: 한 팀의 장애가 다른 팀에 영향을 주지 않도록 함
  • 장애 경계 설정: 클러스터 단위로 장애를 격리해 전체 시스템 영향 최소화
  • 규정 준수: 민감 데이터를 다루는 서비스에 대한 접근을 클러스터 수준에서 제한
  • 가용성 및 성능 향상: 여러 리전에 클러스터를 배치해 지연 시간을 줄이고 가용성을 향상
  • 멀티 및 하이브리드 클라우드 지원: 다양한 클라우드 환경에서 워크로드 실행 가능

또한 ACME는 서비스 메시를 채택한 주요 이유로 다음을 들었다:

  • 다중 클러스터 간 서비스 메시 확장 가능성
  • 클러스터 간 트래픽 관리, 관찰 가능성, 보안 지원

이러한 요구를 충족하기 위해 ACME는 다중 클러스터 운영을 위한 두 가지 접근 방식을 고려했다.

  • 다중 클러스터 메시
    • 여러 클러스터에 걸쳐 있고 워크로드가 클러스터 간에 트래픽을 라우팅하도록 설정하는 메시
    • 이 모든 것은 VirtualService, DestinationRule, Sidecar 등이 적용된 Istio 구성을 따른다.
  • 메시 연합(다중 메시)
    • 개별 서비스 메시 2개의 워크로드 간 통신을 노출하고 활성화한다.
    • 이 선택지는 덜 자동화돼 있어서 서비스 간 트래픽을 설정하려면 두 메시 모두에게 수동 설정이 필요한다.
    • 그러나 서로 다른 팀에서 두 메시를 운영하거나 보안 격리 요구 사항이 엄격한 경우 좋은 선택지다.

1.2 다중 클러스터 서비스 메시 개요

1.2.1 소개

Istio는 앱에 투명하게 클러스터 간 통신을 연결하면서도,
트래픽 제어, 복원력, 관찰 가능성, 보안 등 서비스 메시의 핵심 기능을 유지한다.

다중 클러스터 메시 구성 요소

  • 클러스터 간 워크로드 디스커버리
    • Istio 컨트롤 플레인이 상대 클러스터의 API 서버에 접근해 워크로드 정보를 가져와야 한다.
  • 클러스터 간 워크로드 연결성
    • 클러스터 간 네트워크 상의 연결이 가능해야 한다. 워크로드를 알아도 통신할 수 없으면 무용지물
  • 클러스터 간 공통 신뢰
    • 워크로드 간 상호 인증이 가능해야 Istio의 보안 기능이 제대로 작동한다.

📌 이 세 조건이 충족되면,클러스터는 서로의 워크로드를 인식하고 연결할 수 있으며, Istio의 인증/인가 정책도 적용할 수 있다. 이것이 다중 클러스터 서비스 메시를 구성하기 위한 핵심 요건이다.

1.2.2 이스티오 다중 클러스터 배포 모델

  • 기본 클러스터 (Primary): Istio 컨트롤 플레인이 설치된 클러스터
  • 원격 클러스터 (Remote): 컨트롤 플레인이 설치되지 않은 클러스터, Primary에 연결됨

✅ 배포 모델 3가지

  • 기본-원격 모델 (Primary-Remote)

    • 하나의 컨트롤 플레인 공유
    • 상대적으로 간단하고 리소스 절약
    • 단점: Primary 장애 시 전체 메시 영향
  • 기본-기본 모델 (Primary-Primary)

    • 각 클러스터에 컨트롤 플레인 설치 (복제됨)
    • 높은 가용성, 장애가 해당 클러스터로 국한됨
    • 단점: 더 많은 리소스 요구
  • 외부 컨트롤 플레인 모델 (External Control Plane)

    • 컨트롤 플레인이 클러스터 외부에 위치
    • 관리형 Istio 서비스 제공 시 사용
    • 클러스터는 모두 컨트롤 플레인과 분리됨

1.2.3 다중 클러스터 배포에서 워크로드는 어떻게 찾는가

🔐 Istio 컨트롤 플레인의 클러스터 간 디스커버리와 보안

  • istiod는 서비스 프록시 설정을 위해 Kubernetes API 서버와 통신하여 서비스와 엔드포인트 정보를 수집해야 한다.
  • API 서버 접근은 강력한 권한을 요구하며, 보안 리스크가 존재한다.
    예: 민감 정보 조회, 클러스터 설정 변경 또는 삭제 가능성

🛠️ 보안 메커니즘: Kubernetes RBAC

  • RBAC(Role-Based Access Control)은 API 접근을 보호한다.
  • 주요 구성 요소:
    • ServiceAccount: 사람 아닌 클라이언트(예: 파드)에 ID 제공
    • ServiceAccount Token: JWT 형식, 파드에 주입되어 API 인증에 사용
    • Role / ClusterRole: ID에 권한을 부여하는 정책 정의

🎯 istiod가 API에 접근하기 위해 사용하는 ID와 권한 리소스를 보여준다.

🔎 클러스터 간 디스커버리

  • 기술적으로 단일 클러스터 디스커버리와 동일하지만,
    • istiod가 원격 클러스터의 ServiceAccount 토큰과 인증서를 사용해야 함
    • 이 토큰은 istiod가 원격 API 서버와 보안 통신을 시작하는 데 사용됨

🎞️ istiod가 원격 클러스터를 인증하고 워크로드를 찾는 흐름을 보여준다.

🗜️자동화

  • 이 과정은 복잡해 보일 수 있지만, istioctl 도구가 대부분을 자동화해 사용자는 크게 걱정할 필요가 없다.

1.2.4 클러스터 간 워크로드 연결

✅ 워크로드 간 연결성 (Cross-cluster workload connectivity)

  • 필수 조건: 클러스터 간 워크로드가 서로 네트워크로 연결되어야 함

① 클러스터가 같은 네트워크에 있을 경우
예: 같은 VPC, VPC 피어링 등 플랫 네트워크
→ 워크로드가 IP 주소로 직접 통신 가능
→ 추가 구성 없이 연결 조건 충족

② 클러스터가 다른 네트워크에 있을 경우
직접 IP 통신이 불가능
→ 이스티오 인그레스 게이트웨이 필요(네트워크 경계에 배치해 클러스터 간 트래픽 프록시)

🛡️ East-West Gateway

  • 다중 네트워크 메시 환경에서 클러스터를 연결하는 Istio 인그레스 게이트웨이
  • 클러스터 간 트래픽을 게이트웨이로 중계

🔗 공식 문서 Multiple networks 제공 기능 : 중복 IP 환경 시 해결 등

📌 Istio 공식 문서에서 언급한 Multi-Network 구성 필요성

  • IP 주소 범위 중복 (Overlapping IP)
  • 관리자 경계 (Administrative boundaries)
  • 장애 허용 (Fault tolerance)
  • 네트워크 주소 확장성
  • 컴플라이언스 요구 (네트워크 분리 요구)

💡 참고: Multi-primary 모델에서는 east-west gateway 없이도 네트워크 장비의 L3 라우팅 설정으로 직접 통신 가능

실습 예시:
📦 GitHub 코드 : https://github.com/abasitt/kube6/tree/main/istio/istiocon
▶️ 관련 영상 : https://www.youtube.com/watch?v=4aeeLGrS514

1.2.5 클러스터 간 공통 신뢰 - Docs

✅ 공통 신뢰(Common Trust)의 필요성

  • 클러스터 간 상호 인증을 위해 필요
  • 두 클러스터의 워크로드가 서로의 인증서를 신뢰해야 함

🔐 공통 신뢰 구성 방법
1️⃣ 플러그인 CA 인증서 방식 (Plug-in CA Certificates)

  • 같은 루트 CA가 서명한 중간 CA 인증서를 각 클러스터에 제공
    Istio 설치 시, 중간 CA를 시크릿으로 수동 설정

📌 장점: 간단한 구성, Istio 설치 시 지정만 하면 됨
⚠️ 단점: 중간 CA 유출 시 보안 위험, 노출되면 악의적인 인증서 발급 가능, 보안 강화: 중간 CA를 etcd에 저장하지 않고 메모리로만 유지

2️⃣ 외부 CA 통합 방식 (External CA Integration)

  • Istiod가 쿠버네티스 CSR(인증서 서명 요청) 을 승인하고 외부 CA에 전달
  • 등록기관(Registration Authority) 역할 수행

📌 구현 방법: cert-manager + istio-csr 사용, cert-manager가 외부 CA에 CSR을 전달하고, 서명된 인증서를 반환

⚠️ 제한사항: cert-manager가 사용하는 외부 CA를 지원해야만 가능

🔗 참고 링크: Istio Plug-in CA Guide, Istio External CA + cert-manager, cert-manager 지원 외부 CA 목록

🔚 결론

방법장점단점
플러그인 CA쉽고 간단중간 CA 노출 시 위험
외부 CA 통합더 안전복잡하고 외부 CA 지원 필요ㅍ

🍎 멀티 클러스터 메시를 보안적으로 잘 구성하려면 조직의 보안 정책과 인증서 관리 전략에 맞춰 두 방식 중 하나를 선택해야 합니다.

1.3 다중 클러스터/네트워크/컨트롤플레인 서비스 메시

1.3.1 인프라 구성 환경

🌍 인프라 구성 개요

  • 목표: 서로 다른 리전과 네트워크에 있는 클러스터 간 다중 클러스터 서비스 메시 구성
  • 목적: 지리적으로 분산된 배포, 장애 복원력, Istio 멀티클러스터 시연

🧱 클러스터 구성

클러스터 리전네트워크실행 서비스
west-clusterus-west 사설 네트워크webapp
east-clusterus-east 사설 네트워크catalog

  • 서로 다른 리전에 클러스터를 배치함으로써 장애 발생 시 서비스 중단을 방지
  • webapp과 catalog가 별개 클러스터에 있는 이유는 기술적 필수는 아니고, 단지 시연 목적

📌 참고 사항

  • 서비스 간 통신은 Istio 멀티클러스터 메시를 통해 가능
  • 실제 운영 환경에선, 지연 시간 최소화를 위해 빈번히 통신하는 워크로드는 가까이 배치하는 것이 이상적

1.3.2 다중 클러스터 배포 모델 선택하기

🔁 네트워크 연결

  • 클러스터가 서로 다른 네트워크에 있으므로
    → east-west 게이트웨이로 클러스터 간 연결 필요

🧠 컨트롤 플레인 모델 선택

  • 두 가지 배포 방식 중 선택 가능:
    • 단일 컨트롤 플레인
    • 복제된(Primary-Primary) 컨트롤 플레인
  • 선택 기준: 비즈니스 요구 사항

💼 ACME의 선택 (사례)

  • ACME는 고가용성이 최우선 과제
    → 각 클러스터에 Istio 컨트롤 플레인 배포 (Primary-Primary 모델)
    → 서비스 중단 시 수백만 달러 손실 방지

🏗️ 최종 아키텍처 구성

  • Multi-cluster
  • Multi-network
  • Multi-control-plane
  • East-west gateway로 네트워크 연결
  • Primary-primary (복제 컨트롤 플레인) 배포

1.3.3 클라우드 인프라 준비하기

[실습 환경 구성] k8s(1.23.17) 배포

  • west k8s 클러스터 배포 , east k8s 클러스터 배포 - (참고)Github & mypc 컨테이너

🗜️ west k8s 클러스터 배포

# 
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

Creating cluster "west" ...
 ✓ Ensuring node image (kindest/node:v1.23.17) 🖼
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-west"
You can now use your cluster with:

kubectl cluster-info --context kind-west --kubeconfig ./west-kubeconfig

# 설치 확인
docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                             NAMES
bb963e761e63   kindest/node:v1.23.17   "/usr/local/bin/entr…"   36 seconds ago   Up 35 seconds   0.0.0.0:30000-30005->30000-30005/tcp, 127.0.0.1:56471->6443/tcp   west-control-plane

cat west-kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJ
...
    server: https://127.0.0.1:56471
  name: kind-west
contexts:
- context:
    cluster: kind-west
    user: kind-west
  name: kind-west
current-context: kind-west
kind: Config
preferences: {}
users:
- name: kind-west
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJRnBlM3Z
...
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBK1IweGF
...
    
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

LAST DEPLOYED: Sat May 24 08:27:11 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace kube-system -o jsonpath="{.spec.ports[0].nodePort}" services kube-ops-view)
  export NODE_IP=$(kubectl get nodes --namespace kube-system -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
  
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view --kubeconfig=./west-kubeconfig
NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kube-ops-view   1/1     1            1           43s

NAME                                 READY   STATUS    RESTARTS   AGE
pod/kube-ops-view-79df45849b-fpjfb   1/1     Running   0          43s

NAME                    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/kube-ops-view   NodePort   10.100.0.146   <none>        8080:30005/TCP   43s

NAME                      ENDPOINTS        AGE
endpoints/kube-ops-view   10.10.0.5:8080   43s

## kube-ops-view 접속 URL 확인
open "http://localhost:30005/#scale=1.5"
open "http://localhost:30005/#scale=1.3"

🗜️ east k8s 클러스터 배포

# 
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'

# (옵션) 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

## kube-ops-view 접속 URL 확인
open "http://localhost:31005/#scale=1.5"
open "http://localhost:31005/#scale=1.3"

▶️ kind docker network 에 테스트용 PC(실제로는 컨테이너) 배포

# kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
73d09aa3bbc3   bridge    bridge    local
3d26d2cec1c7   host      host      local
3fb176856219   kind      bridge    local
cf676bb6e447   none      null      local

docker inspect kind
[
    {
        "Name": "kind",
        "Id": "3fb176856219b1257b521c51c749116277bc01fae56b80e418826c251dd1fda2",
        "Created": "2025-05-20T23:51:26.849522289+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": true,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                },
                {
                    "Subnet": "fc00:f853:ccd:e793::/64",
                    "Gateway": "fc00:f853:ccd:e793::1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "bb963e761e6366242cdc5fbe0f86d8295bac5bbaea1c1e0d9e780b391b03d4ee": {
                "Name": "west-control-plane",
                "EndpointID": "a73a373c187f29e64c4221626aeff7bc75e3c19229f7beb432640341db936bc3",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": "fc00:f853:ccd:e793::2/64"
            },
            "dfaf1d0dd0fbc43807d5eec2d069e8dce3132d673655ab840b9feb5d7613a099": {
                "Name": "east-control-plane",
                "EndpointID": "9f8df45a4afccaee3c2ff3197cbf4db7d7a964d94b1c25de7e297dcf2ff53023",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": "fc00:f853:ccd:e793::3/64"
            }
        },
        "Options": {
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

# mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 없이 혹은 지정 해서 사용
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시
혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시
docker ps

# kind network 중 컨테이너(노드) IP(대역) 확인
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
/mypc 172.18.0.100
/east-control-plane 172.18.0.3
/west-control-plane 172.18.0.2

# 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인!
docker exec -it mypc ping -c 1 172.18.0.2
docker exec -it mypc ping -c 1 172.18.0.3
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 east-control-plane
docker exec -it east-control-plane ping -c 1 west-control-plane

docker exec -it west-control-plane ping -c 1 mypc
docker exec -it east-control-plane ping -c 1 mypc

[실습 환경 구성] (편리성 설정) MetalLB 배포

  • Github & Alias 설정(kwest, keast)

  • MetalLB 배포

# MetalLB 배포
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml \
  --kubeconfig=./west-kubeconfig
namespace/metallb-system created
customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgpadvertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/bgppeers.metallb.io created
customresourcedefinition.apiextensions.k8s.io/communities.metallb.io created
customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
customresourcedefinition.apiextensions.k8s.io/servicel2statuses.metallb.io created
serviceaccount/controller created
serviceaccount/speaker created
role.rbac.authorization.k8s.io/controller created
role.rbac.authorization.k8s.io/pod-lister created
clusterrole.rbac.authorization.k8s.io/metallb-system:controller created
clusterrole.rbac.authorization.k8s.io/metallb-system:speaker created
rolebinding.rbac.authorization.k8s.io/controller created
rolebinding.rbac.authorization.k8s.io/pod-lister created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:controller created
clusterrolebinding.rbac.authorization.k8s.io/metallb-system:speaker created
configmap/metallb-excludel2 created
secret/metallb-webhook-cert created
service/metallb-webhook-service created
deployment.apps/controller created
daemonset.apps/speaker created
validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created  

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml \
  --kubeconfig=./east-kubeconfig

# 확인
kubectl get crd --kubeconfig=./west-kubeconfig
NAME                           CREATED AT
bfdprofiles.metallb.io         2025-05-24T00:03:11Z
bgpadvertisements.metallb.io   2025-05-24T00:03:11Z
bgppeers.metallb.io            2025-05-24T00:03:11Z
communities.metallb.io         2025-05-24T00:03:11Z
ipaddresspools.metallb.io      2025-05-24T00:03:11Z
l2advertisements.metallb.io    2025-05-24T00:03:11Z
servicel2statuses.metallb.io   2025-05-24T00:03:11Z

kubectl get crd --kubeconfig=./east-kubeconfig           
       
kubectl get pod -n metallb-system --kubeconfig=./west-kubeconfig
NAME                          READY   STATUS    RESTARTS   AGE
controller-686c7db689-x5c8k   1/1     Running   0          106s
speaker-nprb2                 1/1     Running   0          106s

kubectl get pod -n metallb-system --kubeconfig=./east-kubeconfig
NAME                          READY   STATUS    RESTARTS   AGE
controller-686c7db689-fnzdp   1/1     Running   0          53s
speaker-g22cc                 1/1     Running   0          53s

# IPAddressPool, L2Advertisement 설정
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
ipaddresspool.metallb.io/default created
l2advertisement.metallb.io/default created

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

# 확인
kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./west-kubeconfig
NAMESPACE        NAME                               AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
metallb-system   ipaddresspool.metallb.io/default   true          false             ["172.18.255.101-172.18.255.120"]

NAMESPACE        NAME                                 IPADDRESSPOOLS   IPADDRESSPOOL SELECTORS   INTERFACES
metallb-system   l2advertisement.metallb.io/default   ["default"]    

kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./east-kubeconfig
NAMESPACE        NAME                               AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
metallb-system   ipaddresspool.metallb.io/default   true          false             ["172.18.255.201-172.18.255.220"]

NAMESPACE        NAME                                 IPADDRESSPOOLS   IPADDRESSPOOL SELECTORS   INTERFACES
metallb-system   l2advertisement.metallb.io/default   ["default"]    
  • 샘플 애플리케이션 배포 후 확인
#
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

deployment.apps/nginx created
service/nginx-service created
#
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

deployment.apps/nginx created
service/nginx-service created

# 확인
kubectl get deploy,pod,svc,ep --kubeconfig=./west-kubeconfig
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           57s

NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-8d545c96d-jflbw   1/1     Running   0          57s

NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
service/kubernetes      ClusterIP      10.100.0.1     <none>           443/TCP        47m
service/nginx-service   LoadBalancer   10.100.0.143   172.18.255.101   80:32268/TCP   57s

NAME                      ENDPOINTS         AGE
endpoints/kubernetes      172.18.0.2:6443   47m
endpoints/nginx-service   10.10.0.7:80      57s

kubectl get deploy,pod,svc,ep --kubeconfig=./east-kubeconfig
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           45s

NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-8d545c96d-fgj5j   1/1     Running   0          45s

NAME                    TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
service/kubernetes      ClusterIP      10.200.0.1    <none>           443/TCP        37m
service/nginx-service   LoadBalancer   10.200.0.68   172.18.255.201   80:30906/TCP   45s

NAME                      ENDPOINTS         AGE
endpoints/kubernetes      172.18.0.3:6443   37m
endpoints/nginx-service   10.20.0.7:80      45s

kubectl get svc nginx-service --kubeconfig=./west-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
172.18.255.101

kubectl get svc nginx-service --kubeconfig=./east-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
172.18.255.201

WNIP=$(kubectl get svc nginx-service --kubeconfig=./west-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
ENIP=$(kubectl get svc nginx-service --kubeconfig=./east-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# 외부(mypc)에서 각 클러스터의 Service(LB)로 접속(인입) 확인 : TCP 80 포트 사용으로 편리하다!
docker exec -it mypc curl -s $WNIP
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

docker exec -it mypc curl -s $WNIP -v -I
*   Trying 172.18.255.101:80...
* Connected to 172.18.255.101 (172.18.255.101) port 80
> HEAD / HTTP/1.1
> Host: 172.18.255.101
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx/1.27.5
Server: nginx/1.27.5
< Date: Sat, 24 May 2025 00:11:10 GMT
Date: Sat, 24 May 2025 00:11:10 GMT
< Content-Type: text/html
Content-Type: text/html
< Content-Length: 615
Content-Length: 615
< Last-Modified: Wed, 16 Apr 2025 12:01:11 GMT
Last-Modified: Wed, 16 Apr 2025 12:01:11 GMT
< Connection: keep-alive
Connection: keep-alive
< ETag: "67ff9c07-267"
ETag: "67ff9c07-267"
< Accept-Ranges: bytes
Accept-Ranges: bytes
< 

* Connection #0 to host 172.18.255.101 left intact

docker exec -it mypc curl -s $ENIP
docker exec -it mypc curl -s $ENIP -v -I
*   Trying 172.18.255.201:80...
* Connected to 172.18.255.201 (172.18.255.201) port 80
> HEAD / HTTP/1.1
> Host: 172.18.255.201
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx/1.27.5
Server: nginx/1.27.5
< Date: Sat, 24 May 2025 00:11:56 GMT
Date: Sat, 24 May 2025 00:11:56 GMT
< Content-Type: text/html
Content-Type: text/html
< Content-Length: 615
Content-Length: 615
< Last-Modified: Wed, 16 Apr 2025 12:01:11 GMT
Last-Modified: Wed, 16 Apr 2025 12:01:11 GMT
< Connection: keep-alive
Connection: keep-alive
< ETag: "67ff9c07-267"
ETag: "67ff9c07-267"
< Accept-Ranges: bytes
Accept-Ranges: bytes
< 

* Connection #0 to host 172.18.255.201 left intact

# 확인 후 삭제
kubectl delete deploy,svc --all --kubeconfig=./west-kubeconfig
deployment.apps "nginx" deleted
service "kubernetes" deleted
service "nginx-service" deleted

kubectl delete deploy,svc --all --kubeconfig=./east-kubeconfig
deployment.apps "nginx" deleted
service "kubernetes" deleted
service "nginx-service" deleted
  • 자주 사용하는 Alias
alias kwest='kubectl --kubeconfig=./west-kubeconfig'
alias keast='kubectl --kubeconfig=./east-kubeconfig'

alias iwest='docker exec -it west-control-plane istioctl'
alias ieast='docker exec -it east-control-plane istioctl'

1.3.4 플러그인 CA 인증서 설정하기

🔐 Istio의 기본 CA 동작

  • Istio 설치 시 기본적으로 자체 CA를 생성하여,
    → 워크로드 인증서에 서명

  • 이 CA는 istio-ca-secret 시크릿으로
    → istio-system 네임스페이스에 저장되고
    → 모든 istiod 인스턴스에 공유됨

🧩 사용자 정의 CA 사용 (Plug-in CA)

  • 기본 CA 대신 자체 중간 CA를 사용하려면
    → cacerts라는 시크릿을 istio-system 네임스페이스에 생성

🤐 이 시크릿에는 다음 항목 포함:

파일명설명
ca-cert.pem중간 CA 인증서
ca-key.pem중간 CA 개인 키
root-cert.pem루트 CA 인증서 (모든 클러스터가 신뢰)
cert-chain.pem중간 CA + 루트 CA 이어 붙인 체인 인증서

🏗️ 인증서 체계 구조

Root CA           → 모든 클러스터가 신뢰하는 최상위 인증서
     ↓
Intermediate CA   → 각 클러스터별로 발급되어 워크로드 인증서 서명에 사용
     ↓
Workload Cert     → 신뢰 체인을 구성하여 클라이언트가 인증서를 검증할 수 있도록 함
  • 루트 CA로 클러스터 간 상호 신뢰 확보
  • 체인 인증서로 클라이언트가 검증 가능한 신뢰 경로 확보

📌 핵심 요점:

  • 루트 CA를 공통으로 사용하고, 클러스터마다 중간 CA를 구성함으로써,
    → 멀티 클러스터 간 상호 인증과 신뢰가 가능해진다.

플러그인 CA 인증서 적용하기

# cat ./ch12/scripts/generate-certificates.sh
#!/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 설치 확인 : step CLI가 설치되어 있지 않으면 에러 출력 후 종료.
## macOS : brew install step
if ! [ -x "$(command -v step)" ]; then 
  echo 'Error: Install the smallstep cli (https://github.com/smallstep/cli)'
  exit 1
fi

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 

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 
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

# (참고) 인증서 체인 생성 (기 인증서 생성되어 있음)
## 중간 CA 인증서(ca-cert.pem)와 루트 CA 인증서(root-cert.pem)를 결합 -> 결과는 {west/east}-cluster/cert-chain.pem 에 저장
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

# 인증서 계층 구조
root.istio.in.action (Root CA)
   └── east.intermediate.istio.in.action (Intermediate CA)

# 루트 CA 인증서 확인
openssl x509 -in ch12/certs/root-cert.pem -noout -text
...
        Issuer: CN=root.istio.in.action
        Validity
            Not Before: Jun 28 14:11:35 2022 GMT
            Not After : Jun 25 14:11:35 2032 GMT
        Subject: CN=root.istio.in.action
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:1
...

#
openssl x509 -in ch12/certs/east-cluster/ca-cert.pem -noout -text
...
        Issuer: CN=root.istio.in.action
        Validity
            Not Before: Jun 28 14:11:35 2022 GMT
            Not After : Jun 25 14:11:35 2032 GMT
        Subject: CN=east.intermediate.istio.in.action
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0 # 이 중간 CA는 추가적인 하위 CA를 만들 수 없음
...

openssl x509 -in ch12/certs/east-cluster/cert-chain.pem -noout -text
        Issuer: CN=root.istio.in.action
        Validity
            Not Before: Jun 28 14:11:35 2022 GMT
            Not After : Jun 25 14:11:35 2032 GMT
        Subject: CN=east.intermediate.istio.in.action
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0

#
openssl x509 -in ch12/certs/west-cluster/ca-cert.pem -noout -text
openssl x509 -in ch12/certs/west-cluster/cert-chain.pem -noout -text

✅ (옵션) step 로 인증서 생성 시

# step CLI 설치
# macOS : brew install step

# 스크립트 실행
chmod +x ch12/scripts/generate-certificates.sh
./ch12/scripts/generate-certificates.sh
...

# 생성 결과 확인
tree ch12/certs
ch12/certs
├── east-cluster
│   ├── ca-cert.pem
│   ├── ca-key.pem
│   └── cert-chain.pem
├── root-ca.key
├── root-cert.pem
└── west-cluster
    ├── ca-cert.pem
    ├── ca-key.pem
    └── cert-chain.pem

3 directories, 8 files

📌 istio-system 네임스페이스를 만든 후 인증서를 cacerts 라는 시크릿으로 배포하여 각 클러스터에 중간 CA를 만들어두기

# 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
>> k8s cluster : west <<
NAME           STATUS   AGE
istio-system   Active   77s

>> k8s cluster : east <<
NAME           STATUS   AGE
istio-system   Active   20s

for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret cacerts  -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME      TYPE     DATA   AGE
cacerts   Opaque   4      66s

>> k8s cluster : east <<
NAME      TYPE     DATA   AGE
cacerts   Opaque   4      48s

for i in west east; do echo ">> k8s cluster : $i <<"; kubectl view-secret cacerts -n istio-system --all --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
ca-cert.pem='-----BEGIN CERTIFICATE-----
MIIDOzCCAiOgAwIBAgIQSqOt0fx21muREMs47sO1rjANBgkqhkiG9w0BAQsFADAf
...
-----END CERTIFICATE-----'
ca-key.pem='-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvajcTkjrfb4pGh22UGGTyqiTxAwNBY5iYvMOGUlnOJ5NWP9U
...
-----END RSA PRIVATE KEY-----'
cert-chain.pem='-----BEGIN CERTIFICATE-----
MIIDOzCCAiOgAwIBAgIQSqOt0fx21muREMs47sO1rjANBgkqhkiG9w0BAQsFADAf
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIQGq1YcjF2woccVP0GX4qnKjANBgkqhkiG9w0BAQsFADAf
...
-----END CERTIFICATE-----'
root-cert.pem='-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIQGq1YcjF2woccVP0GX4qnKjANBgkqhkiG9w0BAQsFADAf
...
-----END CERTIFICATE-----'

>> k8s cluster : east <<
ca-cert.pem='-----BEGIN CERTIFICATE-----
MIIDPDCCAiSgAwIBAgIRAPuIC1hSYpzkxBjkHnS7NS8wDQYJKoZIhvcNAQELBQAw
...
-----END CERTIFICATE-----'
ca-key.pem='-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAv9j6bhn1RYhyjepOlr3/uwUS4Ssqj40C/qyKl3KaTGYTbgNR
...
-----END RSA PRIVATE KEY-----'
cert-chain.pem='-----BEGIN CERTIFICATE-----
MIIDPDCCAiSgAwIBAgIRAPuIC1hSYpzkxBjkHnS7NS8wDQYJKoZIhvcNAQELBQAw
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIQGq1YcjF2woccVP0GX4qnKjANBgkqhkiG9w0BAQsFADAf
...
-----END CERTIFICATE-----'
root-cert.pem='-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIQGq1YcjF2woccVP0GX4qnKjANBgkqhkiG9w0BAQsFADAf
...
-----END CERTIFICATE-----'

플러그인 인증서가 설정되면 이스티오 컨트롤 플레인을 설치할 수 있다. 컨트롤 플레인은 플러그인 CA 인증서(사용자가 정의한 중간 인증서)를 갖고 워크로드 인증서에 서명한다.

1.3.5 각 클러스터에 Istiod 설치하기

🌐 네트워크 메타데이터 설정
Istio 컨트롤 플레인 설치 전에, 각 클러스터에 네트워크 메타데이터(network name)를 추가해야 함

📍 네트워크 메타데이터의 목적과 효과

  • 네트워크 토폴로지 인식
    • Istio가 클러스터 및 네트워크 간의 구조를 이해
  • 로컬 우선 트래픽 라우팅
    • 워크로드는 지역(locality) 정보를 기반으로 가까운 워크로드로 우선 트래픽을 전달함
  • east-west 게이트웨이 자동 사용
    • 원격 클러스터로 트래픽 전달 시 east-west 게이트웨이 경유하도록 자동 설정됨

핵심 요점:

  • Istio는 네트워크 메타데이터를 통해 지능적인 트래픽 최적화와 경로 지정을 할 수 있다.
    → 로컬 우선 처리 + 올바른 게이트웨이 사용 가능

1.3.5.1 클러스터 간 연결을 위해 네트워크에 레이블 붙이기

  • 네트워크 토롤로지는 이스티오를 설치할 때 MeshNetwork 설정을 사용해 설정할 수 있다 - Docs
  • 그러나 이것은 드문 고급 사용 사례에서나 유지하는 레거시 설정이며, 더 간단한 방법은 이스티오를 설치한 네임스페이스에 네트워크 토폴로지 정보레이블로 붙이는 것이다.
  • 우리의 경우 이스티오를 설치한 네임스페이스는 istio-system 이고, west-cluster 의 네트워크는 west-network 이다.
  • 그러므로 west-clusteristio-systemtopology.istio.io/network=**west-network** 레이블을 붙인다. east 도 설정하자.
#
kwest label namespace istio-system topology.istio.io/network=west-network
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
NAME           STATUS   AGE   LABELS
istio-system   Active   36m   kubernetes.io/metadata.name=istio-system,topology.istio.io/network=west-network

>> k8s cluster : east <<
NAME           STATUS   AGE   LABELS
istio-system   Active   35m   kubernetes.io/metadata.name=istio-system,topology.istio.io/network=east-network
  • 이 레이블들을 사용해 이스티오는 네트워크 토폴로지를 이해하고, 그 이해를 바탕으로 워크로드 설정법을 결정한다.

1.3.5.2 IstioOperator 리소스를 사용해 컨트롤 플레인 설치 & Alias 설정(iwest, ieast)

  • 많이 수정해야 하므로, IstioOperator 리소스를 사용해 west-cluster 의 이스티오 설치를 정의하자.
# 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 # 이 설치가 이뤄지는 네트워크
# 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-cluster 를 성공적으로 설치한 후, east-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

# 빠져나오기
exit
-----------------------------------

# 설치 확인 : istiod, istio-ingressgateway, crd 등
kwest get istiooperators -n istio-system -o yaml
...
        meshID: usmesh
        meshNetworks: {}
        mountMtlsCerts: false
        multiCluster:
          clusterName: west-cluster
          enabled: false
        network: west-network
...

kwest get all,svc,ep,sa,cm,secret,pdb -n istio-system
NAME                                        READY   STATUS    RESTARTS   AGE
pod/grafana-b854c6c8-km4w7                  1/1     Running   0          86s
pod/istio-ingressgateway-5db74c978c-wvn65   1/1     Running   0          106s
pod/istiod-5585445f4c-7v25t                 1/1     Running   0          2m3s
pod/jaeger-5556cd8fcf-sxr67                 1/1     Running   0          85s
pod/kiali-648847c8c4-cc85f                  1/1     Running   0          85s
pod/prometheus-7b8b9dd44c-hs8x6             2/2     Running   0          85s

NAME                           TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                                                      AGE
service/grafana                ClusterIP      10.100.0.58    <none>           3000/TCP                                                                     86s
service/istio-ingressgateway   LoadBalancer   10.100.0.85    172.18.255.101   15021:32239/TCP,80:31651/TCP,443:30754/TCP,31400:32736/TCP,15443:32637/TCP   106s
service/istiod                 ClusterIP      10.100.0.190   <none>           15010/TCP,15012/TCP,443/TCP,15014/TCP                                        2m3s
service/jaeger-collector       ClusterIP      10.100.0.63    <none>           14268/TCP,14250/TCP,9411/TCP                                                 85s
service/kiali                  ClusterIP      10.100.0.180   <none>           20001/TCP,9090/TCP                                                           85s
service/prometheus             ClusterIP      10.100.0.245   <none>           9090/TCP                                                                     85s
service/tracing                ClusterIP      10.100.0.43    <none>           80/TCP,16685/TCP                                                             85s
service/zipkin                 ClusterIP      10.100.0.77    <none>           9411/TCP                                                                     85s

NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/grafana                1/1     1            1           86s
deployment.apps/istio-ingressgateway   1/1     1            1           106s
deployment.apps/istiod                 1/1     1            1           2m3s
deployment.apps/jaeger                 1/1     1            1           85s
deployment.apps/kiali                  1/1     1            1           85s
deployment.apps/prometheus             1/1     1            1           85s

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/grafana-b854c6c8                  1         1         1       86s
replicaset.apps/istio-ingressgateway-5db74c978c   1         1         1       106s
replicaset.apps/istiod-5585445f4c                 1         1         1       2m3s
replicaset.apps/jaeger-5556cd8fcf                 1         1         1       85s
replicaset.apps/kiali-648847c8c4                  1         1         1       85s
replicaset.apps/prometheus-7b8b9dd44c             1         1         1       85s

NAME                             ENDPOINTS                                                     AGE
endpoints/grafana                10.10.0.10:3000                                               86s
endpoints/istio-ingressgateway   10.10.0.9:15443,10.10.0.9:15021,10.10.0.9:31400 + 2 more...   106s
endpoints/istiod                 10.10.0.8:15012,10.10.0.8:15010,10.10.0.8:15017 + 1 more...   2m2s
endpoints/jaeger-collector       10.10.0.11:9411,10.10.0.11:14250,10.10.0.11:14268             85s
endpoints/kiali                  10.10.0.12:9090,10.10.0.12:20001                              85s
endpoints/prometheus             10.10.0.13:9090                                               85s
endpoints/tracing                10.10.0.11:16685,10.10.0.11:16686                             85s
endpoints/zipkin                 10.10.0.11:9411                                               85s

NAME                                                  SECRETS   AGE
serviceaccount/default                                1         43m
serviceaccount/grafana                                1         86s
serviceaccount/istio-ingressgateway-service-account   1         106s
serviceaccount/istio-reader-service-account           1         2m3s
serviceaccount/istiod                                 1         2m3s
serviceaccount/istiod-service-account                 1         2m3s
serviceaccount/kiali                                  1         85s
serviceaccount/prometheus                             1         85s

NAME                                            DATA   AGE
configmap/grafana                               4      86s
configmap/istio                                 2      2m3s
configmap/istio-ca-root-cert                    1      108s
configmap/istio-gateway-deployment-leader       0      109s
configmap/istio-gateway-status-leader           0      109s
configmap/istio-grafana-dashboards              2      86s
configmap/istio-leader                          0      109s
configmap/istio-namespace-controller-election   0      109s
configmap/istio-services-grafana-dashboards     4      85s
configmap/istio-sidecar-injector                2      2m3s
configmap/kiali                                 1      85s
configmap/kube-root-ca.crt                      1      43m
configmap/prometheus                            5      85s

NAME                                                      TYPE                                  DATA   AGE
secret/cacerts                                            Opaque                                4      42m
secret/default-token-ndvmn                                kubernetes.io/service-account-token   3      43m
secret/grafana-token-8zzsv                                kubernetes.io/service-account-token   3      86s
secret/istio-ingressgateway-service-account-token-hl5px   kubernetes.io/service-account-token   3      106s
secret/istio-reader-service-account-token-mjw94           kubernetes.io/service-account-token   3      2m3s
secret/istiod-service-account-token-f6482                 kubernetes.io/service-account-token   3      2m3s
secret/istiod-token-4qrrt                                 kubernetes.io/service-account-token   3      2m3s
secret/kiali-token-r7vvb                                  kubernetes.io/service-account-token   3      85s
secret/prometheus-token-5rhpb                             kubernetes.io/service-account-token   3      85s

NAME                                              MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
poddisruptionbudget.policy/istio-ingressgateway   1               N/A               0                     106s
poddisruptionbudget.policy/istiod                 1               N/A               0                     2m3s

kwest get secret -n istio-system cacerts -o json # 미리 만들어둔 인증서/키 확인

# 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}]}}'
service/istio-ingressgateway patched

kwest patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
kwest describe svc -n istio-system istio-ingressgateway
Name:                     istio-ingressgateway
Namespace:                istio-system
Labels:                   app=istio-ingressgateway
                          install.operator.istio.io/owning-resource=istio-controlplane
                          install.operator.istio.io/owning-resource-namespace=istio-system
                          istio=ingressgateway
                          istio.io/rev=default
                          operator.istio.io/component=IngressGateways
                          operator.istio.io/managed=Reconcile
                          operator.istio.io/version=1.17.8
                          release=istio
Annotations:              metallb.io/ip-allocated-from-pool: default
Selector:                 app=istio-ingressgateway,istio=ingressgateway
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.100.0.85
IPs:                      10.100.0.85
LoadBalancer Ingress:     172.18.255.101
Port:                     status-port  15021/TCP
TargetPort:               15021/TCP
NodePort:                 status-port  32239/TCP
Endpoints:                10.10.0.9:15021
Port:                     http2  80/TCP
TargetPort:               8080/TCP
NodePort:                 http2  30000/TCP
Endpoints:                10.10.0.9:8080
Port:                     https  443/TCP
TargetPort:               8443/TCP
NodePort:                 https  30754/TCP
Endpoints:                10.10.0.9:8443
Port:                     tcp  31400/TCP
TargetPort:               31400/TCP
NodePort:                 tcp  32736/TCP
Endpoints:                10.10.0.9:31400
Port:                     tls  15443/TCP
TargetPort:               15443/TCP
NodePort:                 tls  32637/TCP
Endpoints:                10.10.0.9:15443
Session Affinity:         None
External Traffic Policy:  Local
Internal Traffic Policy:  Cluster
HealthCheck NodePort:     30820
Events:
  Type    Reason                 Age                From                Message
  ----    ------                 ----               ----                -------
  Normal  IPAllocated            3m21s              metallb-controller  Assigned IP ["172.18.255.101"]
  Normal  nodeAssigned           0s (x3 over 3m9s)  metallb-speaker     announcing from node "west-control-plane" with protocol "layer2"
  Normal  ExternalTrafficPolicy  0s                 service-controller  Cluster -> Local

# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
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}]}}'

# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:30001

# Grafana 접속
open http://127.0.0.1:30002

# Kiali 접속 : NodePort
open http://127.0.0.1:30003

# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:30004
  • east-cluster 용 IstioOperator 정의는 west-cluster 용과 비교했을 때 클러스터 이름과 네트워크만 다르다. (west-cluster 를 설치할 때 사용했던 meshID 를 그대로 지정)
# west-control-plane 진입 후 설치 진행
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

# 컨트롤 플레인 배포
istioctl install -f east-istio.yaml --set values.global.proxy.privileged=true -y

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

# 빠져나오기
exit
-----------------------------------

# 설치 확인 : istiod, istio-ingressgateway, crd 등
keast get istiooperators -n istio-system -o yaml
...
        meshID: usmesh
        meshNetworks: {}
        mountMtlsCerts: false
        multiCluster:
          clusterName: east-cluster
          enabled: false
        network: east-network
...

keast get all,svc,ep,sa,cm,secret,pdb -n istio-system
keast get secret -n istio-system cacerts -o json # 미리 만들어둔 인증서/키 확인


# NodePort 변경 및 nodeport 31001~31003으로 변경 : prometheus(31001), grafana(31002), kiali(31003), tracing(31004)
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}]}}'

# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:31001

# Grafana 접속
open http://127.0.0.1:31002

# Kiali 접속
open http://127.0.0.1:31003

# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:31004

1.3.5.3 istioctl alias 설정(iwest, ieast)

  • 미리 만들어둔 인증서 적용 확인
#
docker exec -it west-control-plane istioctl -h
docker exec -it east-control-plane istioctl -h

#
alias iwest='docker exec -it west-control-plane istioctl'
alias ieast='docker exec -it east-control-plane istioctl'

#
iwest proxy-status
NAME                                                   CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                      VERSION
istio-ingressgateway-5db74c978c-wvn65.istio-system     west-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-5585445f4c-7v25t     1.17.8

ieast proxy-status
NAME                                                   CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
istio-ingressgateway-7f6f8f8d99-lgvfc.istio-system     east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-sb2dc     1.17.8

#
iwest proxy-config secret deploy/istio-ingressgateway.istio-system
RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                              NOT AFTER                NOT BEFORE
default           Cert Chain     ACTIVE     true           52818176540999573926987976529334739020     2025-05-25T01:40:03Z     2025-05-24T01:38:03Z
ROOTCA            CA             ACTIVE     true           35459989146298323207776648461378299690     2035-05-22T00:52:38Z     2025-05-24T00:52:38Z

iwest proxy-config secret deploy/istio-ingressgateway.istio-system -o json
{
    "dynamicActiveSecrets": [
        {
            "name": "default",
            "lastUpdated": "2025-05-24T01:40:03.292Z",
            "secret": {
                "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
                "name": "default",
                "tlsCertificate": {
                    "certificateChain": {
                        "inlineBytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURkakNDQWw2Z0F3SUJBZ0lRSjd4cE
 ...                       TFZOV0tsdTZWdm50RzdVMGFZRT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
                    },
                    "privateKey": {
                        "inlineBytes": "W3JlZGFjdGVkXQ=="
                    }
                }
            }
        },
        {
            "name": "ROOTCA",
            "lastUpdated": "2025-05-24T01:40:03.288Z",
            "secret": {
                "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
                "name": "ROOTCA",
                "validationContext": {
                    "trustedCa": {
                        "inlineBytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUREVENDQWZXZ0F3SUJBZ0lRR3ExWW
 ...                       BTlAKdXRMaExWTldLbHU2VnZudEc3VTBhWUU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0="
                    }
                }
            }
        }
    ]
}
...

# 아래는 default 에 inlineBytes 값을 decode64 -d 시 3개의 인증서 정보 출력 후 각 개별 인증서를 openssl x509 -in Y.pem -noout -text 로 출력 확인 
## (1) 사용자 인증서
-----BEGIN CERTIFICATE-----
MIIDdjCCAl6gAwIBAgIQPHLYaJhiIjAwJkg6cAVeWDANBgkqhkiG9w0BAQsFADAs
...
5xYNi7u0APTzE1swNbx2TF5eeTsFvYcbFh56ahLp0izGkahOv97bEgnZdeTsLRyH
K+5+1ZdJ2n8CuxoSY+FXUlMDwGjdvCXAKBM=
-----END CERTIFICATE-----

        Issuer: CN=west.intermediate.istio.in.action
        Validity
            Not Before: May 16 08:45:28 2025 GMT
            Not After : May 17 08:47:28 2025 GMT
        Subject: 
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier: 
                D3:83:9A:3A:51:D9:03:62:35:8F:6A:A4:DA:99:88:BB:74:70:4F:33
            X509v3 Subject Alternative Name: critical
                URI:spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account


## (2) 중간 CA 루트 인증서
-----BEGIN CERTIFICATE-----
MIIDPDCCAiSgAwIBAgIRAMkJ23sotpqiiWps+38Df/YwDQYJKoZIhvcNAQELBQAw
...
usSjiM6KR77xogslodbQw4QQG+w5HQOwMa1k8WTCNrplxdsnaQJjdqUwCdixicq2
DeHuSkz4cykAI/NWc2cZIw==
-----END CERTIFICATE-----

        Issuer: CN=root.istio.in.action
        Validity
            Not Before: May 24 00:52:38 2025 GMT
            Not After : May 22 00:52:38 2035 GMT
        Subject: CN=west.intermediate.istio.in.action
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0

## (3) 최상위 루트 인증서
-----BEGIN CERTIFICATE-----
MIIDDTCCAfWgAwIBAgIQS+jSffZX7itohjyrautczDANBgkqhkiG9w0BAQsFADAf
...
3fRtDApNHbbmi7WXrM+pG4D+Buk2FUEHJVpu16Ch2K2vpRzpkliqes+T/5E92uY9
ob7MBgt61g4VZ/p8+RMJWYw=
-----END CERTIFICATE-----

        Issuer: CN=root.istio.in.action
        Validity
            Not Before: May 24 00:52:38 2025 GMT
            Not After : May 22 00:52:38 2035 GMT
        Subject: CN=root.istio.in.actionll
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:1

#
iwest proxy-config listener deploy/istio-ingressgateway.istio-system
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

iwest proxy-config route deploy/istio-ingressgateway.istio-system
NAME     DOMAINS     MATCH                  VIRTUAL SERVICE
         *           /healthz/ready*        
         *           /stats/prometheus*  
         
iwest proxy-config cluster deploy/istio-ingressgateway.istio-system
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS    

iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.10.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.10.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.10.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.10.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.10.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.10.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.10.0.2:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.2:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.10.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.10.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.10.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.10.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.10.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.10.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.100.0.77:9411                                        HEALTHY     OK                zipkin
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.2:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

iwest proxy-config secret deploy/istio-ingressgateway.istio-system
RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                              NOT AFTER                NOT BEFORE
default           Cert Chain     ACTIVE     true           52818176540999573926987976529334739020     2025-05-25T01:40:03Z     2025-05-24T01:38:03Z
ROOTCA            CA             ACTIVE     true           35459989146298323207776648461378299690     2035-05-22T00:52:38Z     2025-05-24T00:52:38Z

iwest proxy-config bootstrap deploy/istio-ingressgateway.istio-system
❯ iwest proxy-config bootstrap deploy/istio-ingressgateway.istio-system
{
    "bootstrap": {
        "node": {
            "id": "router~10.10.0.9~istio-ingressgateway-5db74c978c-wvn65.istio-system~istio-system.svc.cluster.local",
            "cluster": "istio-ingressgateway.istio-system",
            "metadata": {
                    "ANNOTATIONS": {
                                "kubernetes.io/config.seen": "2025-05-24T01:39:52.049902129Z",
                                "kubernetes.io/config.source": "api",
                                "prometheus.io/path": "/stats/prometheus",
                                "prometheus.io/port": "15020",
                                "prometheus.io/scrape": "true",
                                "sidecar.istio.io/inject": "false"
                            },
                    "CLUSTER_ID": "west-cluster",
                    "ENVOY_PROMETHEUS_PORT": 15090,
                    "ENVOY_STATUS_PORT": 15021,
                    "INSTANCE_IPS": "10.10.0.9",
                    "ISTIO_PROXY_SHA": "e1222b5645a29ad3d02b280797ac1219dd88cfbc",
                    "ISTIO_VERSION": "1.17.8",
                    "LABELS": {
                                "app": "istio-ingressgateway",
                                "chart": "gateways",
                                "heritage": "Tiller",
                                "install.operator.istio.io/owning-resource": "unknown",
                                "istio": "ingressgateway",
                                "istio.io/rev": "default",
                                "operator.istio.io/component": "IngressGateways",
                                "release": "istio",
                                "service.istio.io/canonical-name": "istio-ingressgateway",
                                "service.istio.io/canonical-revision": "latest",
                                "sidecar.istio.io/inject": "false"
                            },
                    "MESH_ID": "usmesh",
                    "NAME": "istio-ingressgateway-5db74c978c-wvn65",
                    "NAMESPACE": "istio-system",
                    "NETWORK": "west-network",
                    "NODE_NAME": "west-control-plane",
                    "OWNER": "kubernetes://apis/apps/v1/namespaces/istio-system/deployments/istio-ingressgateway",
...                    
        "bootstrapExtensions": [
            {
                "name": "envoy.bootstrap.internal_listener",
                "typedConfig": {
                    "@type": "type.googleapis.com/udpa.type.v1.TypedStruct",
                    "typeUrl": "type.googleapis.com/envoy.extensions.bootstrap.internal_listener.v3.InternalListener"
                }
            }
        ]
    },
    "lastUpdated": "2025-05-24T01:40:03.197Z"
}

iwest proxy-config ecds deploy/istio-ingressgateway.istio-system
Error: config dump has no configuration type type.googleapis.com/envoy.admin.v3.EcdsConfigDump
#
ieast proxy-config listener deploy/istio-ingressgateway.istio-system
ieast proxy-config route deploy/istio-ingressgateway.istio-system
ieast proxy-config cluster deploy/istio-ingressgateway.istio-system
ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system
ieast proxy-config secret deploy/istio-ingressgateway.istio-system
ieast proxy-config bootstrap deploy/istio-ingressgateway.istio-system
ieast proxy-config ecds deploy/istio-ingressgateway.istio-system
  • 두 클러스터 모두에 컨트롤 플레인을 설치하면 개별 메시 2개를 갖게 되는데, 각자 로컬 서비스만 찾는 istiod 복제복 하나와 수신 트래픽용 게이트웨이 하나를 실행한다.
    • 메시에는 다음 절에서 설정할 클러스터 간 워크로드 디스커버리 및 연결이 없다. 그러나 더 진행하기 전에 각 클러스터에 일부 워크로드를 실행해보자.
    • 이 워크로드는 클러스터 간 디스커버리와 연결성이 올바르게 준비됐는지 확인하는 데 유용할 것이다.

1.3.5.4 두 클러스터 모두에 워크로드 실행하기

  • 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 # Stub catalog service to which webapp makes request
cat ch12/catalog-svc.yaml
piVersion: 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
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/webapp   1/1     1            1           46s

NAME                          READY   STATUS    RESTARTS   AGE
pod/webapp-5c8b4fff64-rk6v2   2/2     Running   0          46s

NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.100.0.123   <none>        80/TCP    30s
service/webapp    ClusterIP   10.100.0.149   <none>        80/TCP    46s

NAME                ENDPOINTS         AGE
endpoints/catalog   <none>            30s
endpoints/webapp    10.10.0.14:8080   46s

kwest get svc,ep catalog -n istioinaction
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.100.0.123   <none>        80/TCP    45s

NAME                ENDPOINTS   AGE
endpoints/catalog   <none>      45s

kwest get gw,vs,dr -A
NAMESPACE       NAME                                            AGE
istioinaction   gateway.networking.istio.io/coolstore-gateway   72s

NAMESPACE       NAME                                                       GATEWAYS                HOSTS                         AGE
istioinaction   virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   72s

#
iwest proxy-status
NAME                                                   CLUSTER          CDS        LDS        EDS        RDS        ECDS         ISTIOD                      VERSION
istio-ingressgateway-5db74c978c-wvn65.istio-system     west-cluster     SYNCED     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-5585445f4c-7v25t     1.17.8
webapp-5c8b4fff64-rk6v2.istioinaction                  west-cluster     SYNCED     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-5585445f4c-7v25t     1.17.8

# endpoint 에 IP 는 10.10.0.0/16 대역들 다수 확인
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
>> k8s cluster : west - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> k8s cluster : west - istio-config route <<
NAME          DOMAINS                     MATCH                  VIRTUAL SERVICE
http.8080     webapp.istioinaction.io     /*                     webapp-virtualservice.istioinaction
              *                           /healthz/ready*        
              *                           /stats/prometheus*     

>> k8s cluster : west - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
webapp.istioinaction.svc.cluster.local                       80        -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> k8s cluster : west - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.10.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.10.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.10.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.10.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.10.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.10.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.10.0.14:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
10.10.0.2:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.2:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.10.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.10.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.10.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.10.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.10.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.10.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.100.0.77:9411                                        HEALTHY     OK                zipkin
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.2:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS   

iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog

🧩 Stub Service가 필요한 이유

  • catalog 워크로드는 east-cluster에서만 실행됨.
  • 그러나 webapp은 west-cluster에서 catalog.east의 FQDN으로 요청을 보냄.

⚠️ 문제:

  • catalog 서비스가 west-cluster에 존재하지 않으면, webapp 컨테이너는 FQDN을 IP 주소로 해석할 수 없음 → DNS 해석 실패
  • 요청이 프록시에 도달하기도 전에 실패함

✅ 해결:

  • Stub Service를 west-cluster에 생성
    → FQDN이 ClusterIP로 해석됨
    → 요청이 성공적으로 Envoy 프록시로 전달됨
    → 프록시가 실제로 cross-cluster 라우팅 수행

📌 참고:

  • 이 문제는 Istio DNS 프록시 개선 예정
    → 추후 버전에서 해결될 에지 케이스
  • east-cluster 에 catalog 서비스를 배포하자.
#
keast create ns istioinaction
keast label namespace istioinaction istio-injection=enabled
keast -n istioinaction apply -f ch12/catalog.yaml
cat ch12/catalog.yaml


# 확인
keast get deploy,pod,svc,ep -n istioinaction
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/catalog   1/1     1            1           16s

NAME                         READY   STATUS    RESTARTS   AGE
pod/catalog-6cf4b97d-p8xbj   2/2     Running   0          16s

NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.200.0.93   <none>        80/TCP    16s

NAME                ENDPOINTS         AGE
endpoints/catalog   10.20.0.14:3000   16s

keast get svc,ep catalog -n istioinaction
NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.200.0.93   <none>        80/TCP    32s

NAME                ENDPOINTS         AGE
endpoints/catalog   10.20.0.14:3000   32s

keast get gw,vs,dr -A # 없음

#
ieast proxy-status
NAME                                                   CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
catalog-6cf4b97d-p8xbj.istioinaction                   east-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-85976468f-sb2dc     1.17.8
istio-ingressgateway-7f6f8f8d99-lgvfc.istio-system     east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-sb2dc     1.17.8

# endpoint 에 IP 는 10.20.0.0/16 대역들 다수 확인
for i in listener route cluster endpoint; do echo ">> k8s cluster : east - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
>> k8s cluster : east - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> k8s cluster : east - istio-config route <<
NAME     DOMAINS     MATCH                  VIRTUAL SERVICE
         *           /stats/prometheus*     
         *           /healthz/ready*        

>> k8s cluster : east - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> k8s cluster : east - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.20.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.20.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.20.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.20.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.20.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.20.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
10.20.0.3:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.20.0.3:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.20.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.20.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.20.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.20.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.20.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.20.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.20.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.20.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.20.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.206:9411                                       HEALTHY     OK                zipkin
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.3:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

ieast proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS    

ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

클러스터 2개를 각자 연결해야 하는 워크로드가 있는 지금을 시작점이라고 해보자. 그러나 클러스터 간 워크로드 디스커버리 없이는 사이드카 프록시에 반대 클러스터의 워크로드가 설정되지 않는다. 그러므로 다음 단계는 클러스터 간 디스커버리를 활성화해보자.

1.3.6 클러스터 간 워크로드 디스커버리 활성화하기

📌 Istio는 원격 클러스터의 정보를 조회하려면 인증이 필요하므로, 설치 시 최소 권한의 istio-reader-service-account를 생성한다. 이 계정은 다른 컨트롤 플레인이 워크로드 정보(서비스, 엔드포인트 등)를 쿼리할 수 있게 하며, 이를 위해 서비스 어카운트 토큰과 인증서를 원격 클러스터에서 사용할 수 있도록 공유해야 한다.

🔐 원격 클러스터 접근용 시크릿 만들기

  • istioctl 에는 create-remote-secret 명령어가 있는데, 기본 istio-reader-service-account 서비스 어카운트를 사용해 원격 클러스터 접근용으로 시크릿을 만드는 것이다.
  • 시크릿을 만들 때는 이스티오를 설치할 때 IstioOperator 에서 사용한 클러스터 이름을 지정하는 것이 중요하다(east-cluster 및 west-cluster 값은 앞서 ‘IstioOperator 리소스를 사용해 컨트롤 플레인 설치하기’ 절 참조).

✅ 원격 클러스터에 접근하기 위한 설정의 식별자로 클러스터 이름이 어떻게 사용되는지에는 주의를 기울이자

#
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get sa -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME                                   SECRETS   AGE
default                                1         119m
grafana                                1         77m
istio-ingressgateway-service-account   1         78m
istio-reader-service-account           1         78m
istiod                                 1         78m
istiod-service-account                 1         78m
kiali                                  1         77m
prometheus                             1         77m

>> k8s cluster : east <<
NAME                                   SECRETS   AGE
default                                1         119m
grafana                                1         69m
istio-ingressgateway-service-account   1         69m
istio-reader-service-account           1         69m
istiod                                 1         69m
istiod-service-account                 1         69m
kiali                                  1         69m
prometheus                             1         69m

# east
keast describe sa -n istio-system istio-reader-service-account      
...
Mountable secrets:   istio-reader-service-account-token-r28c8
Tokens:              istio-reader-service-account-token-r28c8

keast get sa -n istio-system istio-reader-service-account -o yaml
...
  name: istio-reader-service-account
  namespace: istio-system
  resourceVersion: "10629"
  uid: cd6ce452-c5cb-42bf-98fd-6a4dc79964a4
secrets:
- name: istio-reader-service-account-token-r28c8

keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}'
istio-reader-service-account-token-r28c8
eirsa=$(keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}')
keast get secret -n istio-system $eirsa
NAME                                       TYPE                                  DATA   AGE
istio-reader-service-account-token-r28c8   kubernetes.io/service-account-token   3      72m

keast get secret -n istio-system $eirsa -o json
{
    "apiVersion": "v1",
    "data": {
        "ca.crt": "LS0t,,,==",
        "namespace": "aXN0aW8tc3lzdGVt", # istio-system
        "token": "ZXl...TmREdw=="
    },
...

kubectl rolesum istio-reader-service-account -n istio-system --kubeconfig=./east-kubeconfig
ServiceAccount: istio-system/istio-reader-service-account
Secrets:
• */istio-reader-service-account-token-r28c8

Policies:

• [CRB] */istio-reader-clusterrole-istio-system ⟶  [CR] */istio-reader-clusterrole-istio-system
  Resource                                                                                         Name  Exclude  Verbs  G L W C U P D DC  
  *.[config.istio.io,security.istio.io,networking.istio.io,authentication.istio.io,rbac.istio.io]  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  customresourcedefinitions.apiextensions.k8s.io                                                   [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  endpoints                                                                                        [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  endpointslices.discovery.k8s.io                                                                  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  namespaces                                                                                       [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  nodes                                                                                            [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  pods                                                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  replicasets.apps                                                                                 [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  replicationcontrollers                                                                           [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  secrets                                                                                          [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  serviceexports.multicluster.x-k8s.io                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✖ ✖ ✔ ✖   
  serviceimports.multicluster.x-k8s.io                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  services                                                                                         [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  subjectaccessreviews.authorization.k8s.io                                                        [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  tokenreviews.authentication.k8s.io                                                               [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  workloadentries.networking.istio.io                                                              [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   


• [CRB] */istio-reader-istio-system ⟶  [CR] */istio-reader-istio-system
  Resource                                                                                         Name  Exclude  Verbs  G L W C U P D DC  
  *.[config.istio.io,security.istio.io,networking.istio.io,authentication.istio.io,rbac.istio.io]  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  customresourcedefinitions.apiextensions.k8s.io                                                   [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  endpoints                                                                                        [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  endpointslices.discovery.k8s.io                                                                  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  namespaces                                                                                       [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  nodes                                                                                            [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  pods                                                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  replicasets.apps                                                                                 [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  replicationcontrollers                                                                           [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  secrets                                                                                          [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  serviceexports.multicluster.x-k8s.io                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  serviceimports.multicluster.x-k8s.io                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  services                                                                                         [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
  subjectaccessreviews.authorization.k8s.io                                                        [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  tokenreviews.authentication.k8s.io                                                               [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  workloadentries.networking.istio.io                                                              [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖  

keast auth can-i --list
Resources                                       Non-Resource URLs   Resource Names   Verbs
*.*                                             []                  []               [*]
                                                [*]                 []               [*]
selfsubjectaccessreviews.authorization.k8s.io   []                  []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                  []               [create]
                                                [/api/*]            []               [get]
                                                [/api]              []               [get]
                                                [/apis/*]           []               [get]
                                                [/apis]             []               [get]
                                                [/healthz]          []               [get]
                                                [/healthz]          []               [get]
                                                [/livez]            []               [get]
                                                [/livez]            []               [get]
                                                [/openapi/*]        []               [get]
                                                [/openapi]          []               [get]
                                                [/readyz]           []               [get]
                                                [/readyz]           []               [get]
                                                [/version/]         []               [get]
                                                [/version/]         []               [get]
                                                [/version]          []               [get]
                                                [/version]          []               [get]
                                                
keast auth can-i --as=system:serviceaccount:istio-system:istio-reader-service-account --list
Resources                                        Non-Resource URLs                     Resource Names   Verbs
tokenreviews.authentication.k8s.io               []                                    []               [create]
selfsubjectaccessreviews.authorization.k8s.io    []                                    []               [create]
selfsubjectrulesreviews.authorization.k8s.io     []                                    []               [create]
subjectaccessreviews.authorization.k8s.io        []                                    []               [create]
serviceexports.multicluster.x-k8s.io             []                                    []               [get list watch create delete]
endpoints                                        []                                    []               [get list watch]
namespaces                                       []                                    []               [get list watch]
nodes                                            []                                    []               [get list watch]
pods                                             []                                    []               [get list watch]
replicationcontrollers                           []                                    []               [get list watch]
secrets                                          []                                    []               [get list watch]
services                                         []                                    []               [get list watch]
customresourcedefinitions.apiextensions.k8s.io   []                                    []               [get list watch]
replicasets.apps                                 []                                    []               [get list watch]
*.authentication.istio.io                        []                                    []               [get list watch]
*.config.istio.io                                []                                    []               [get list watch]
endpointslices.discovery.k8s.io                  []                                    []               [get list watch]
serviceimports.multicluster.x-k8s.io             []                                    []               [get list watch]
*.networking.istio.io                            []                                    []               [get list watch]
*.rbac.istio.io                                  []                                    []               [get list watch]
*.security.istio.io                              []                                    []               [get list watch]
workloadentries.networking.istio.io              []                                    []               [get watch list]
                                                 [/.well-known/openid-configuration]   []               [get]
                                                 [/api/*]                              []               [get]
                                                 [/api]                                []               [get]
                                                 [/apis/*]                             []               [get]
                                                 [/apis]                               []               [get]
                                                 [/healthz]                            []               [get]
                                                 [/healthz]                            []               [get]
                                                 [/livez]                              []               [get]
                                                 [/livez]                              []               [get]
                                                 [/openapi/*]                          []               [get]
                                                 [/openapi]                            []               [get]
                                                 [/openid/v1/jwks]                     []               [get]
                                                 [/readyz]                             []               [get]
                                                 [/readyz]                             []               [get]
                                                 [/version/]                           []               [get]
                                                 [/version/]                           []               [get]
                                                 [/version]                            []               [get]
                                                 [/version]                            []               [get]

#
ieast x create-remote-secret --help
Create a secret with credentials to allow Istio to access remote Kubernetes apiservers

ieast x create-remote-secret --name="east-cluster"
# This file is autogenerated, do not edit.
apiVersion: v1
kind: Secret
metadata:
  annotations:
    networking.istio.io/cluster: east-cluster
  creationTimestamp: null
  labels:
    istio/multiCluster: "true" # 이 레이블이 true로 설정된 시크릿은 이스티오의 컨트롤 플레인이 새 클러스터를 등록하기 위해 감시한다
  name: istio-remote-secret-east-cluster
  namespace: istio-system
stringData:
  east-cluster: |
    apiVersion: v1
    clusters:
    - cluster: # 아래 'certificate-authority-data'는 이 클러스터에 보안 커넥션을 시작하는 데 사용하는 CA
        certificate-authority-data: LS0tLS1CR....
        server: https://east-control-plane:6443
      name: east-cluster
    contexts:
    - context:
        cluster: east-cluster
        user: east-cluster
      name: east-cluster
    current-context: east-cluster
    kind: Config
    preferences: {}
    users:
    - name: east-cluster
      user: # 아래 'token'은 서비스 어카운트의 ID를 나타내는 토큰
        token: eyJhb...
---

## certificate-authority-data 정보 : k8s 루트 인증서
openssl x509 -in YYY -noout -text
...
        Issuer: CN=kubernetes
        Validity
            Not Before: May 16 05:13:20 2025 GMT
            Not After : May 14 05:13:20 2035 GMT
        Subject: CN=kubernetes
        ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment, Certificate Sign
            X509v3 Basic Constraints: critical
                CA:TRUE

## user.token 정보 : 
jwt decode YYY
Token header
------------
{
  "alg": "RS256",
  "kid": "4BqK2FN-Bxz_3Q1mMc7IW3HZoH8dz1Z2svNfoJDwvl4"
}

Token claims
------------
{
  "iss": "kubernetes/serviceaccount",
  "kubernetes.io/serviceaccount/namespace": "istio-system",
  "kubernetes.io/serviceaccount/secret.name": "istio-reader-service-account-token-r28c8",
  "kubernetes.io/serviceaccount/service-account.name": "istio-reader-service-account",
  "kubernetes.io/serviceaccount/service-account.uid": "cd6ce452-c5cb-42bf-98fd-6a4dc79964a4",
  "sub": "system:serviceaccount:istio-system:istio-reader-service-account"
}

✅ 시크릿 내용을 출력하는 대신, kubectl 명령어에 파이프해 west-cluster 에 적용하자

# west 에 시크릿 생성
ieast x create-remote-secret --name="east-cluster" | kwest apply -f -
secret/istio-remote-secret-east-cluster created

# istiod 로그 확인 : 시크릿이 생성되면, 바로 istiod가 이 시크릿을 가지고 새로 추가된 원격 클러스터(east)에 워크로드를 쿼리한다.
kwest logs deploy/istiod -n istio-system | grep 'Adding cluster'
2025-05-24T03:09:45.516282Z     info    Adding cluster  cluster=east-cluster secret=istio-system/istio-remote-secret-east-cluster

#
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME                                               TYPE                                  DATA   AGE
cacerts                                            Opaque                                4      132m
default-token-ndvmn                                kubernetes.io/service-account-token   3      132m
grafana-token-8zzsv                                kubernetes.io/service-account-token   3      90m
istio-ingressgateway-service-account-token-hl5px   kubernetes.io/service-account-token   3      91m
istio-reader-service-account-token-mjw94           kubernetes.io/service-account-token   3      91m
istio-remote-secret-east-cluster                   Opaque                                1      77s
istiod-service-account-token-f6482                 kubernetes.io/service-account-token   3      91m
istiod-token-4qrrt                                 kubernetes.io/service-account-token   3      91m
kiali-token-r7vvb                                  kubernetes.io/service-account-token   3      90m
prometheus-token-5rhpb                             kubernetes.io/service-account-token   3      90m

>> k8s cluster : east <<
NAME                                               TYPE                                  DATA   AGE
cacerts                                            Opaque                                4      131m
default-token-s7stk                                kubernetes.io/service-account-token   3      131m
grafana-token-2gn8t                                kubernetes.io/service-account-token   3      82m
istio-ingressgateway-service-account-token-vppcz   kubernetes.io/service-account-token   3      82m
istio-reader-service-account-token-r28c8           kubernetes.io/service-account-token   3      82m
istiod-service-account-token-8h9sz                 kubernetes.io/service-account-token   3      82m
istiod-token-srt76                                 kubernetes.io/service-account-token   3      82m
kiali-token-xgkkw                                  kubernetes.io/service-account-token   3      82m
prometheus-token-x8m62                             kubernetes.io/service-account-token   3      82m

kwest get secret -n istio-system istio-remote-secret-east-cluster
NAME                               TYPE     DATA   AGE
istio-remote-secret-east-cluster   Opaque   1      110s

kwest get secret -n istio-system istio-remote-secret-east-cluster -o yaml
apiVersion: v1
data:
  east-cluster: YXBpVmVyc2lvbjogdjEKY2x1c3RlcnM6Ci0gY2x1c3RlcjoKICAgIGNlcnRpZmljYXRlLWF
...  
UzZwZnllVDAtdVBfaXQzOUZBTjdOQnFOZER3Cg==
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Secret","metadata":{"annotations":{"networking.istio.io/cluster":"east-cluster"},"creationTimestamp":null,"labels":{"istio/multiCluster":"true"},"name":"istio-remote-secret-east-cluster","namespace":"istio-system"},"stringData":{"east-cluster":"apiVersion: v1\nclusters:\n- cluster:\n    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJ
...
SEgKTGFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\n    server: https://east-control-plane:6443\n  name: east-cluster\ncontexts:\n- context:\n    cluster: east-cluster\n    user: east-cluster\n  name: east-cluster\ncurrent-context: east-cluster\nkind: Config\npreferences: {}\nusers:\n- name: east-cluster\n  user:\n    token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjRCcUsyRk4tQnh6XzNRMW1NYzdJVzNIWm9IOGR6MVo
...
mp4kJu3trYClJziJALR0msQOLRD2H5mDYb6TtMoau2ZSCF1y9kKJouqoUb4I940oxAhwIkOsMd-S6pfyeT0-uP_it39FAN7NBqNdDw\n"}}
    networking.istio.io/cluster: east-cluster
  creationTimestamp: "2025-05-24T03:09:45Z"
  labels:
    istio/multiCluster: "true"
  name: istio-remote-secret-east-cluster
  namespace: istio-system
  resourceVersion: "20443"
  uid: 85edcfa5-cc6b-4116-a8e0-6f648071757b
type: Opaque
...

# 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
>> k8s cluster : west - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> k8s cluster : west - istio-config route <<
NAME          DOMAINS                     MATCH                  VIRTUAL SERVICE
http.8080     webapp.istioinaction.io     /*                     webapp-virtualservice.istioinaction
              *                           /healthz/ready*        
              *                           /stats/prometheus*     

>> k8s cluster : west - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
webapp.istioinaction.svc.cluster.local                       80        -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> k8s cluster : west - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.10.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.10.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.10.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.10.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.10.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.10.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.10.0.14:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
10.10.0.2:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.2:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.10.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.10.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.10.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.10.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.10.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.10.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.100.0.77:9411                                        HEALTHY     OK                zipkin
10.20.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.20.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.20.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.20.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.20.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.20.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
10.20.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.20.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.2:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS  

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn catalog.istioinaction.svc.cluster.local -o json
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

# west 에서 10.20.0.14(10.20.0.0/16)로 라우팅이 가능한 상태인가?
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||catalog.istioinaction.svc.cluster.local' -o json
...
                "address": {
                    "socketAddress": {
                        "address": "10.20.0.14",
                        "portValue": 3000
...

# east 확인 : west 의 CDS/EDS 정보를 아직 모름!
for i in listener route cluster endpoint; do echo ">> k8s cluster : east - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
ieast proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS    
ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

✅ 이제 컨트롤 플레인이 상대 클러스터의 워크로드를 퀴리할 수 있다.

# 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.0.123   <none>        80/TCP    53m
service/webapp    ClusterIP   10.100.0.149   <none>        80/TCP    53m

NAME                ENDPOINTS         AGE
endpoints/catalog   <none>            53m
endpoints/webapp    10.10.0.14:8080   53m

# webapp stub service 생성하지 않았으므로 별도 west 의 webapp service 정보가 없다
keast get svc,ep -n istioinaction
NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.200.0.93   <none>        80/TCP    45m

NAME                ENDPOINTS         AGE
endpoints/catalog   10.20.0.14:3000   45m

✅ 다음으로는 클러스터 간 연결을 설정해보자.

1.3.7 클러스터 간 연결 설정하기

💁🏻‍♀️ 들어가며 : north-south 트래픽, east-west 트래픽

  • 이스티오에서 트래픽은 크게 두 가지로 나뉩니다:

    • North-South 트래픽: 퍼블릭 인터넷 등 외부 네트워크에서 내부 클러스터로 들어오는 트래픽입니다. 이 트래픽은 인그레스 게이트웨이(Envoy 프록시 기반)를 통해 수신되어 조직의 내부 서비스로 전달됩니다.

    • East-West 트래픽: 클러스터 간, 또는 내부 네트워크 간에 오가는 트래픽입니다. 예: us-west 리전의 클러스터에서 us-east 리전의 클러스터로 요청을 보내는 경우.

  • 대부분의 클라우드 제공업체는 가상 네트워크 피어링 기능을 통해 east-west 트래픽을 단순화합니다. 이때 조건은 네트워크 주소 공간이 겹치지 않아야 하며, 클러스터 간에는 직접 IP 통신이 가능합니다.
    하지만 이 방법은 클라우드 내부 전용이므로,

    • 서로 다른 클라우드 간
    • 온프레미스 환경과 클라우드 간
      처럼 네트워크 피어링이 불가능한 환경에선 사용할 수 없습니다.
  • 이 경우, 이스티오는 east-west 게이트웨이를 통해 해결책을 제공합니다.
    이 게이트웨이는:

    • 다른 클러스터의 워크로드에 접근할 수 있도록 외부에 로드 밸런서로 노출되고
    • 클러스터 간 통신을 위해 사용됩니다.
  • 즉, east-west 게이트웨이는 서로 다른 네트워크에 위치한 클러스터 간의 트래픽을 보안된 방식으로 전달하는 역할을 하며, 다음 절에서는 이를 어떻게 설정하고 내부적으로 작동하는지 다룰 예정입니다.

1.3.7.1 이스티오의 east-west 게이트웨이

  • 이스티오의 east-west 게이트웨이는 단순한 클러스터 간 트래픽의 진입점 역할을 넘어, 서비스 운영팀에게 투명한 방식으로 작동하도록 설계되었습니다. 이 목표를 위해 게이트웨이는 다음 기능을 반드시 수행해야 합니다:

    • 클러스터 간 정밀한 트래픽 관리
    • 암호화된 트래픽 라우팅으로 워크로드 간 상호 인증 지원
    • 운영자가 별도의 Istio 리소스를 추가 구성할 필요 없음
  • 이러한 설정 덕분에:

    • 클러스터 내부든, 간이든 동일한 방식으로 트래픽을 라우팅할 수 있으며,
    • 워크로드는 서비스를 세밀하게 지정하고 상호 인증된 연결을 설정할 수 있습니다.
    • (단, 클러스터 간일 경우 로드 밸런싱 방식이 약간 다릅니다 — 다음 절에서 다룸.)

이러한 자동화와 투명성이 어떻게 가능해지는지는 SNI clusters와 SNI auto passthrough라는 두 가지 이스티오 기능을 통해 설명됩니다.

1.3.7.2 SNI 클러스터로 east-west 게이트웨이 설정하기

📌 east-west 게이트웨이는 모든 서비스에 대해 SNI 클러스터 구성이 추가된 인그레스 게이트웨이입니다. 여기서 핵심은 SNI 클러스터인데, 이는 일반적인 Envoy 클러스터와 비슷하지만 다음과 같은 중요한 차이가 있습니다:

SNI 클러스터란?

  • Envoy 클러스터 정보(방향, 포트, 버전, 서비스 FQDN 등)를 SNI (Server Name Indication) 안에 인코딩한 클러스터입니다.
  • 클라이언트(예: webapp)는 원격 워크로드(예: catalog)에 접속할 때, 이 정보를 SNI에 담아 연결을 시작합니다.

east-west 게이트웨이의 역할

  • SNI 헤더를 읽어 클라이언트가 지정한 워크로드로 암호화된 트래픽을 라우팅합니다.
  • 이 과정은 상호 TLS 인증 하에 이루어집니다.
  • 덕분에 서비스 운영자는 별도의 라우팅 설정 없이도 정밀한 라우팅 + 보안 연결을 자동으로 누릴 수 있습니다.

🎯 결과적으로 SNI 클러스터 덕분에, Istio는 클러스터 간의 서비스 호출을 안전하고 정밀하게 처리할 수 있게 됩니다.

1.3.7.3 SNI 클러스터가 있는 east-west 게이트웨이 설치하기

✅ SNI 클러스터 설정은 옵트인 기능으로, 다음 IstioOpertor 정의처럼 환경 변수 ISTIO_META_ROUTER_MODE 로 게이트웨이 라우터 모드를 sni-dnat 으로 설정해서 활성화할 수 있다.

# cat ch12/gateways/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
  • IstioOperator 리소스 이름은 처음에 컨트롤 플레인 설치하는 데 사용한 리소스와 같아서는 안 된다. 같은 이름을 사용하면 앞 서 설치한 것을 덮어 쓸 것이다.
  • ISTIO_META_ROUTER_MODEsni-dnat 으로 설정하면 SNI 클러스터를 자동으로 구성한다. 지정하지 않으면 standard 모드로 돌아가며, 이는 SNI 클러스터를 설정하지 않는다.
  • ISTIO_META_REQUESTED_NETWORK_VIEW 는 네트워크 트래픽이 프록시되는 곳을 정의한다.

✅ IstioOperator 로 east 클러스터에 east-west 게이트웨이를 설치하자.

# 설치 전 확인
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-wvn65   1/1     Running   0          122m
istiod-5585445f4c-7v25t                 1/1     Running   0          122m

>> k8s cluster : east <<
NAME                                    READY   STATUS    RESTARTS   AGE
istio-ingressgateway-7f6f8f8d99-lgvfc   1/1     Running   0          113m
istiod-85976468f-sb2dc                  1/1     Running   0          113m

for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME                                 REVISION   STATUS   AGE
installed-state-istio-controlplane                       122m

>> k8s cluster : east <<
NAME                                 REVISION   STATUS   AGE
installed-state-istio-controlplane                       113m

kwest get IstioOperator -n istio-system installed-state-istio-controlplane -o yaml
keast get IstioOperator -n istio-system installed-state-istio-controlplane -o yaml

# 설치 전 확인 : west 에서 catalog endpoint 에 IP 확인
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
>> k8s cluster : west - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> k8s cluster : west - istio-config route <<
NAME          DOMAINS                     MATCH                  VIRTUAL SERVICE
http.8080     webapp.istioinaction.io     /*                     webapp-virtualservice.istioinaction
              *                           /healthz/ready*        
              *                           /stats/prometheus*     

>> k8s cluster : west - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
webapp.istioinaction.svc.cluster.local                       80        -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> k8s cluster : west - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.10.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.10.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.10.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.10.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.10.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.10.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.10.0.14:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
10.10.0.2:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.2:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.10.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.10.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.10.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.10.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.10.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.10.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.100.0.77:9411                                        HEALTHY     OK                zipkin
10.20.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.20.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.20.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.20.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.20.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.20.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
10.20.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.20.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.2:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

# IstioOperator 로 east 클러스터에 east-west 게이트웨이를 설치
cat ch12/gateways/cluster-east-eastwest-gateway.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-eastwestgateway
  namespace: istio-system
spec:
  meshConfig:
    accessLogFile: /dev/stdout
  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"
        # The network to which traffic is routed
        - 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
      
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
✔ Ingress gateways installed                                           ✔ Installation complete   

# east 클러스터에 east-west 게이트웨이를 설치 확인
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME                                 REVISION   STATUS   AGE
installed-state-istio-controlplane                       126m

>> k8s cluster : east <<
NAME                                    REVISION   STATUS   AGE
installed-state-istio-controlplane                          117m
installed-state-istio-eastwestgateway                       33s

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-wvn65   1/1     Running   0          126m
istiod-5585445f4c-7v25t                 1/1     Running   0          127m

>> k8s cluster : east <<
NAME                                     READY   STATUS    RESTARTS   AGE
istio-eastwestgateway-866794c798-nr29l   1/1     Running   0          58s
istio-ingressgateway-7f6f8f8d99-lgvfc    1/1     Running   0          118m
istiod-85976468f-sb2dc                   1/1     Running   0          118m

keast get IstioOperator -n istio-system installed-state-istio-eastwestgateway -o yaml
...
    ingressGateways:
    - 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
      label:
        app: istio-eastwestgateway
        istio: eastwestgateway
        topology.istio.io/network: east-network
      name: istio-eastwestgateway
...

# east 정보 확인
ieast proxy-status
NAME                                                    CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
catalog-6cf4b97d-p8xbj.istioinaction                    east-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-85976468f-sb2dc     1.17.8
istio-eastwestgateway-866794c798-nr29l.istio-system     east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-sb2dc     1.17.8
istio-ingressgateway-7f6f8f8d99-lgvfc.istio-system      east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-sb2dc     1.17.8

# east 에 istio-ingressgateway  에 istio-config 정보 확인 : west 의 CDS/EDS 모두 알고 있음!
for i in listener route cluster endpoint; do echo ">> east k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
>> east k8s cluster : ingressgateway - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> east k8s cluster : ingressgateway - istio-config route <<
NAME     DOMAINS     MATCH                  VIRTUAL SERVICE
         *           /stats/prometheus*     
         *           /healthz/ready*        

>> east k8s cluster : ingressgateway - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15012     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15017     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15021     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> east k8s cluster : ingressgateway - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.20.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.20.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.20.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.20.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.20.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.20.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
10.20.0.15:15012                                        HEALTHY     OK                outbound|15012||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.15:15017                                        HEALTHY     OK                outbound|15017||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.15:15021                                        HEALTHY     OK                outbound|15021||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.15:15443                                        HEALTHY     OK                outbound|15443||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.3:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.20.0.3:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.20.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.20.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.20.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.20.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.20.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.20.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.20.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.20.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.20.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.206:9411                                       HEALTHY     OK                zipkin
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.3:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

# east 에 istio-eastwestgateway 에 istio-config 정보 확인 : webapp(CDS) OK, west 에 EDS 아직 모름!
for i in listener route cluster endpoint; do echo ">> east k8s cluster : eastwestgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-eastwestgateway.istio-system; echo; done
>> east k8s cluster : eastwestgateway - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> east k8s cluster : eastwestgateway - istio-config route <<
NAME     DOMAINS     MATCH                  VIRTUAL SERVICE
         *           /healthz/ready*        
         *           /stats/prometheus*     

>> east k8s cluster : eastwestgateway - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15012     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15017     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15021     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> east k8s cluster : eastwestgateway - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.20.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.20.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.20.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.20.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.20.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.20.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.20.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.20.0.14:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
10.20.0.15:15012                                        HEALTHY     OK                outbound|15012||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.15:15017                                        HEALTHY     OK                outbound|15017||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.15:15021                                        HEALTHY     OK                outbound|15021||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.15:15443                                        HEALTHY     OK                outbound|15443||istio-eastwestgateway.istio-system.svc.cluster.local
10.20.0.3:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.20.0.3:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.20.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.20.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.20.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.20.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.20.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.20.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.20.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.20.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.20.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.20.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.206:9411                                       HEALTHY     OK                zipkin
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.3:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system | grep istioinaction
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS   

ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system --fqdn webapp.istioinaction.svc.cluster.local -o json
ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system --fqdn webapp.istioinaction.svc.cluster.local -o json | grep sni
                        "sni": "outbound_.80_._.webapp.istioinaction.svc.cluster.local"

ieast proxy-config endpoint deploy/istio-eastwestgateway.istio-system | grep istioinaction
ieast proxy-config endpoint deploy/istio-eastwestgateway.istio-system --cluster 'outbound|80||catalog.istioinaction.svc.cluster.local' -o json

# west 정보 확인
iwest proxy-status
NAME                                                   CLUSTER          CDS        LDS        EDS        RDS        ECDS         ISTIOD                      VERSION
istio-ingressgateway-5db74c978c-wvn65.istio-system     west-cluster     SYNCED     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-5585445f4c-7v25t     1.17.8
webapp-5c8b4fff64-rk6v2.istioinaction                  west-cluster     SYNCED     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-5585445f4c-7v25t     1.17.8

# west 에 istio-ingressgateway 에 istio-config 정보 확인
for i in listener route cluster endpoint; do echo ">> west k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
>> west k8s cluster : ingressgateway - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

>> west k8s cluster : ingressgateway - istio-config route <<
NAME          DOMAINS                     MATCH                  VIRTUAL SERVICE
http.8080     webapp.istioinaction.io     /*                     webapp-virtualservice.istioinaction
              *                           /healthz/ready*        
              *                           /stats/prometheus*     

>> west k8s cluster : ingressgateway - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE
BlackHoleCluster                                             -         -          -             STATIC         
agent                                                        -         -          -             STATIC         
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
grafana.istio-system.svc.cluster.local                       3000      -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15012     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15017     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15021     -          outbound      EDS            
istio-eastwestgateway.istio-system.svc.cluster.local         15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          80        -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          443       -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15021     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          15443     -          outbound      EDS            
istio-ingressgateway.istio-system.svc.cluster.local          31400     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        443       -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15010     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15012     -          outbound      EDS            
istiod.istio-system.svc.cluster.local                        15014     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              9411      -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14250     -          outbound      EDS            
jaeger-collector.istio-system.svc.cluster.local              14268     -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         9090      -          outbound      EDS            
kiali.istio-system.svc.cluster.local                         20001     -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       53        -          outbound      EDS            
kube-dns.kube-system.svc.cluster.local                       9153      -          outbound      EDS            
kube-ops-view.kube-system.svc.cluster.local                  8080      -          outbound      EDS            
kubernetes.default.svc.cluster.local                         443       -          outbound      EDS            
metallb-webhook-service.metallb-system.svc.cluster.local     443       -          outbound      EDS            
prometheus.istio-system.svc.cluster.local                    9090      -          outbound      EDS            
prometheus_stats                                             -         -          -             STATIC         
sds-grpc                                                     -         -          -             STATIC         
tracing.istio-system.svc.cluster.local                       80        -          outbound      EDS            
tracing.istio-system.svc.cluster.local                       16685     -          outbound      EDS            
webapp.istioinaction.svc.cluster.local                       80        -          outbound      EDS            
xds-grpc                                                     -         -          -             STATIC         
zipkin                                                       -         -          -             STRICT_DNS     
zipkin.istio-system.svc.cluster.local                        9411      -          outbound      EDS            

>> west k8s cluster : ingressgateway - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.10.0.10:3000                                         HEALTHY     OK                outbound|3000||grafana.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:9411                                         HEALTHY     OK                outbound|9411||zipkin.istio-system.svc.cluster.local
10.10.0.11:14250                                        HEALTHY     OK                outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:14268                                        HEALTHY     OK                outbound|14268||jaeger-collector.istio-system.svc.cluster.local
10.10.0.11:16685                                        HEALTHY     OK                outbound|16685||tracing.istio-system.svc.cluster.local
10.10.0.11:16686                                        HEALTHY     OK                outbound|80||tracing.istio-system.svc.cluster.local
10.10.0.12:9090                                         HEALTHY     OK                outbound|9090||kiali.istio-system.svc.cluster.local
10.10.0.12:20001                                        HEALTHY     OK                outbound|20001||kiali.istio-system.svc.cluster.local
10.10.0.13:9090                                         HEALTHY     OK                outbound|9090||prometheus.istio-system.svc.cluster.local
10.10.0.14:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
10.10.0.2:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.2:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.4:53                                            HEALTHY     OK                outbound|53||kube-dns.kube-system.svc.cluster.local
10.10.0.4:9153                                          HEALTHY     OK                outbound|9153||kube-dns.kube-system.svc.cluster.local
10.10.0.5:8080                                          HEALTHY     OK                outbound|8080||kube-ops-view.kube-system.svc.cluster.local
10.10.0.6:9443                                          HEALTHY     OK                outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.10.0.8:15010                                         HEALTHY     OK                outbound|15010||istiod.istio-system.svc.cluster.local
10.10.0.8:15012                                         HEALTHY     OK                outbound|15012||istiod.istio-system.svc.cluster.local
10.10.0.8:15014                                         HEALTHY     OK                outbound|15014||istiod.istio-system.svc.cluster.local
10.10.0.8:15017                                         HEALTHY     OK                outbound|443||istiod.istio-system.svc.cluster.local
10.10.0.9:8080                                          HEALTHY     OK                outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:8443                                          HEALTHY     OK                outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15021                                         HEALTHY     OK                outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:15443                                         HEALTHY     OK                outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.10.0.9:31400                                         HEALTHY     OK                outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.100.0.77:9411                                        HEALTHY     OK                zipkin
127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats
127.0.0.1:15020                                         HEALTHY     OK                agent
172.18.0.2:6443                                         HEALTHY     OK                outbound|443||kubernetes.default.svc.cluster.local
172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
unix://./etc/istio/proxy/XDS                            HEALTHY     OK                xds-grpc
unix://./var/run/secrets/workload-spiffe-uds/socket     HEALTHY     OK                sds-grpc

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep istioinaction
catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
webapp.istioinaction.svc.cluster.local                       80        -          outbound      EDS 

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn catalog.istioinaction.svc.cluster.local -o json

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn catalog.istioinaction.svc.cluster.local -o json | grep sni
                        "sni": "outbound_.80_._.catalog.istioinaction.svc.cluster.local"

# west 에 istio-ingressgateway : east EDS 모든 정보에서 east의 eastwestgateway에 mtls 정보로 변경!
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep istioinaction
10.10.0.14:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

# 출력되는 172.18.X.Y에 IP 확인 : east 에 eastwestgateway 의 Service(LoadBalancer)의 External-IP.
keast get svc,ep -n istio-system istio-eastwestgateway
NAME                            TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                                           AGE
service/istio-eastwestgateway   LoadBalancer   10.200.0.139   172.18.255.202   15021:30532/TCP,15443:30060/TCP,15012:30003/TCP,15017:31492/TCP   10m

NAME                              ENDPOINTS                                                        AGE
endpoints/istio-eastwestgateway   10.20.0.15:15021,10.20.0.15:15017,10.20.0.15:15012 + 1 more...   10m


# west 에 webapp 에 istio-config 정보 확인
for i in listener route cluster endpoint; do echo ">> west k8s cluster : webapp - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/webapp.istioinaction; echo; done
iwest proxy-config endpoint deploy/webapp.istioinaction | grep istioinaction
10.10.0.14:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

# west 에서 호출 시도
kwest get svc,ep -n istioinaction
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.100.0.123   <none>        80/TCP    91m
service/webapp    ClusterIP   10.100.0.149   <none>        80/TCP    92m

NAME                ENDPOINTS         AGE
endpoints/catalog   <none>            91m
endpoints/webapp    10.10.0.14:8080   92m

kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
*   Trying 10.100.0.123:80...
* connect to 10.100.0.123 port 80 failed: Connection refused
* Failed to connect to catalog.istioinaction.svc.cluster.local port 80 after 3 ms: Connection refused
* Closing connection 0
curl: (7) Failed to connect to catalog.istioinaction.svc.cluster.local port 80 after 3 ms: Connection refused
command terminated with exit code 7
  • east-west 게이트웨이를 설치하고 라우터 모드를 sni-dnat으로 설정했으면, 다음 단계는 SNI 자동 통과 모드 SNI auto passthrough mode 를 사용해 east-west 게이트웨이에서 다중 클러스터 상호 TLS 포트를 노출하는 것이다.
  • 이스티오는 영리해서 딱 그때만 게이트웨이에 SNI 클러스터를 설정한다.

1.3.7.4 SNI 자동 통과로 클러스터 간 트래픽 라우팅하기

✅ 문제: 수동 SNI 통과의 한계

  • 전통적으로는 SNI 기반 트래픽을 수용하려면, 인그레스 게이트웨이에 대해 VirtualService 리소스를 수작업으로 정의해야 했습니다.
  • 이 방식은 운영자가 매번 라우팅 규칙을 구성해야 하므로 번거롭고 자동화에 불리합니다.

✅ 해결책: SNI 자동 통과

  • SNI auto passthroughVirtualService 리소스 없이도 트래픽을 자동 라우팅합니다.
  • 라우팅은 SNI 클러스터를 기반으로 수행되며, east-west 게이트웨이에서 자동으로 설정됩니다.
  • 게이트웨이의 **router mode가 sni-dnat**일 때 자동 적용됩니다.

✅ 구성 방법

  • Istio Gateway 리소스에서 SNI auto passthrough를 활성화할 수 있습니다.
  • 예를 들어, *.local과 매칭되는 모든 SNI 트래픽에 자동 통과를 적용하면 모든 쿠버네티스 서비스가 자동 대상이 됩니다.
    ✅ 핵심 효과
  • 클러스터 간의 트래픽을 자동, 보안, 무중단 구성 변경 없이 라우팅할 수 있어 운영자 부담이 줄고 확장성이 향상됩니다.

✅ SNI 자동 통과는 이스티오 Gateway 리소스로 설정할 수 있다. 다음 정의에서는 SNI 헤더의 값이 *.local 인 모든 트래픽에 대해 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에 대해서만 트래픽을 허용한다.

✅ east 클러스터에 적용 시, east-cluster의 워크로드를 west-cluster 에 노출한다.

# east 클러스터에 적용하자. east-cluster의 워크로드를 west-cluster 에 노출한다.
cat ch12/gateways/expose-services.yaml
keast apply -n istio-system -f ch12/gateways/expose-services.yaml

# 확인
keast get gw,vs,dr -A
NAMESPACE      NAME                                                AGE
istio-system   gateway.networking.istio.io/cross-network-gateway   13s

# west 에서 호출 시도
kwest get svc,ep -n istioinaction
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.100.0.123   <none>        80/TCP    108m
service/webapp    ClusterIP   10.100.0.149   <none>        80/TCP    108m

NAME                ENDPOINTS         AGE
endpoints/catalog   <none>            108m
endpoints/webapp    10.10.0.14:8080   108m

kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
*   Trying 10.100.0.123:80...
* connect to 10.100.0.123 port 80 failed: Connection refused
...

# east 에 istio-ingressgateway 에 istio-config 정보 확인 : 이전 내용과 동일하게 west 의 CDS/EDS 모두 알고 있음!
for i in listener route cluster endpoint; do echo ">> east k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done

# east 에 istio-eastwestgateway 에 istio-config 정보 확인 : SNI 자동 통과를 위한 listener 추가 확인!
for i in listener route cluster endpoint; do echo ">> east k8s cluster : eastwestgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-eastwestgateway.istio-system; echo; done

ieast proxy-config listener deploy/istio-eastwestgateway.istio-system
ieast proxy-config listener deploy/istio-eastwestgateway.istio-system | grep istioinaction
0.0.0.0 15443 SNI: outbound_.80_._.webapp.istioinaction.svc.cluster.local; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2                    Cluster: outbound_.80_._.webapp.istioinaction.svc.cluster.local
0.0.0.0 15443 SNI: outbound_.80_._.catalog.istioinaction.svc.cluster.local; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2                   Cluster: outbound_.80_._.catalog.istioinaction.svc.cluster.local

ieast proxy-config listener deploy/istio-eastwestgateway.istio-system --port 15443  -o json
...
                "filterChainMatch": {
                    "serverNames": [
                        "outbound_.80_._.catalog.istioinaction.svc.cluster.local"
                 ...
                },
                "filters": [
                    ...
                    {
                        "name": "envoy.filters.network.tcp_proxy",
                        "typedConfig": {
                            "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
                            "statPrefix": "outbound_.80_._.catalog.istioinaction.svc.cluster.local",
                            "cluster": "outbound_.80_._.catalog.istioinaction.svc.cluster.local",
...



# west 정보 확인
iwest proxy-status

# west 에 istio-ingressgateway 에 istio-config 정보 확인
for i in listener route cluster endpoint; do echo ">> west k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done

# west 에 webapp 에 istio-config 정보 확인
for i in listener route cluster endpoint; do echo ">> west k8s cluster : webapp - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/webapp.istioinaction; echo; done

✅ 반대편 클러스터에도 작업 수행. 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-west 게이트웨이를 설치 확인
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
>> k8s cluster : west <<
NAME                                    REVISION   STATUS   AGE
installed-state-istio-controlplane                          162m
installed-state-istio-eastwestgateway                       12s

>> k8s cluster : east <<
NAME                                    REVISION   STATUS   AGE
installed-state-istio-controlplane                          153m
installed-state-istio-eastwestgateway                       36m

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-eastwestgateway-6f7995db66-ql658   1/1     Running   0          38s
istio-ingressgateway-5db74c978c-wvn65    1/1     Running   0          162m
istiod-5585445f4c-7v25t                  1/1     Running   0          163m

>> k8s cluster : east <<
NAME                                     READY   STATUS    RESTARTS   AGE
istio-eastwestgateway-866794c798-nr29l   1/1     Running   0          36m
istio-ingressgateway-7f6f8f8d99-lgvfc    1/1     Running   0          154m
istiod-85976468f-sb2dc                   1/1     Running   0          154m

kwest get IstioOperator -n istio-system installed-state-istio-eastwestgateway -o yaml
iwest proxy-status
NAME                                                    CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                      VERSION
istio-eastwestgateway-6f7995db66-ql658.istio-system     west-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-5585445f4c-7v25t     1.17.8
istio-ingressgateway-5db74c978c-wvn65.istio-system      west-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-5585445f4c-7v25t     1.17.8
webapp-5c8b4fff64-rk6v2.istioinaction                   west-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-5585445f4c-7v25t     1.17.8

# west 클러스터에 적용하자. east-cluster의 워크로드를 west-cluster 에 노출한다.
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
        name: tls
        protocol: TLS
      tls:
        mode: AUTO_PASSTHROUGH
      hosts:
        - "*.local"
        
kwest apply -n istio-system -f ch12/gateways/expose-services.yaml

# 확인
kwest get gw,vs,dr -A
NAMESPACE       NAME                                                AGE
istio-system    gateway.networking.istio.io/cross-network-gateway   5s
istioinaction   gateway.networking.istio.io/coolstore-gateway       89m

NAMESPACE       NAME                                                       GATEWAYS                HOSTS                         AGE
istioinaction   virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   89m

kwest get svc,ep -n istioinaction
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/catalog   ClusterIP   10.100.0.123   <none>        80/TCP    120m
service/webapp    ClusterIP   10.100.0.149   <none>        80/TCP    120m

NAME                ENDPOINTS         AGE
endpoints/catalog   <none>            120m
endpoints/webapp    10.10.0.14:8080   120m

ieast pc clusters deploy/istio-eastwestgateway.istio-system | grep catalog
catalog.istioinaction.svc.cluster.local                                       80        -          outbound      EDS            
outbound_.80_._.catalog.istioinaction.svc.cluster.local                       -         -          -             EDS 

# 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 # catalog 서비스용 SNI 클러스터
  • 출력은 catalog 워크로드에 SNI 클러스터가 정의됐음을 보여준다!
  • 그리고 SNI 자동 통과로 게이트웨이를 설정했으므로, 게이트웨이에 들어오는 트래픽은 SNI 클러스터를 사용해 의도한 워크로드로 라우팅한다.
  • 이스티오 컨트롤 플레인은 이 리소스들의 생성을 지켜보고 있다가, 이제 클러스터 간 트래픽을 라우팅할 수 있는 경로가 존재함을 발견한다.
  • 따라서 컨트롤 플레인은 원격 클러스터에서 새로 발견한 엔드포인트로 모든 워크로드를 설정한다.

1.3.7.5 클러스터 간 워크로드 디스커버리 검증하기

  • 이제 east-cluster 의 워크로드가 west-cluster 에 노출됐으므로, webapp 엔보이 클러스터가 catalog 워크로드로 향햐는 엔드포인트를 갖고 있으리라 기대된다.
  • 이 엔드포인트는 네트워크의 catalog 에 대한 요청을 프록시하는 east-west 게이트웨이 주소를 가리켜야 한다.
  • 이를 확인하기 위해 east-cluster 의 east-west 게이트웨이 주소(Service)를 알아보자
#
keast -n istio-system get svc istio-eastwestgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
172.18.255.202
  • 이제 이 값을 west-cluster 의 워크로드가 클러스터 간 트래픽을 라우팅할 때 사용하는 주소와 비교해보자.
# 
iwest pc endpoints deploy/webapp.istioinaction | grep catalog
172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

  • catalog 리소스의 엔드포인트가 east-west 게이트웨이의 주소와 일치하면, 워크로드를 찾을 수 있고 클러스터 간 트래픽이 가능하다.
  • 프록시 설정을 고려하면 모든 것이 올바르게 설정됐다.
  • 직접 요청을 트리거해 마지막으로 확인해보자.
# west 에 istio-ingressgateway 인입을 위한 접속 정보 확인
kwest get svc -n istio-system istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                                                                      AGE
istio-ingressgateway   LoadBalancer   10.100.0.85   172.18.255.101   15021:32239/TCP,80:30000/TCP,443:30754/TCP,31400:32736/TCP,15443:32637/TCP   171m

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
[
  {
    "id": 1,
    "color": "amber",
    "department": "Eyewear",
    "name": "Elinor Glasses",
    "price": "282.00"
  },
  {
    "id": 2,
    "color": "cyan",
    "department": "Clothing",
    "name": "Atlas Shirt",
    "price": "127.00"
  },
  {
    "id": 3,
    "color": "teal",
    "department": "Clothing",
    "name": "Small Metal Shoes",
    "price": "232.00"
  },
  {
    "id": 4,
    "color": "red",
    "department": "Watches",
    "name": "Red Dragon Watch",
    "price": "232.00"
  }
]

# 신규 터미널 : 반복 접속
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
  • kiali : west-cluster 확인

  • kiali : east-cluster 확인 , mTLS 통신이며 TCP Traffic 와 HTTP(S) 2개 트래픽 흐름으로 확인됨.

  • istio-proxy 로그 확인

# west 에 istio-ingressgateway 로그
kwest logs -n istio-system -l app=istio-ingressgateway -f
[2025-05-24T04:43:56.484Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 8 7 "172.18.0.100" "curl/8.7.1" "e8660ec1-a451-95c2-98f0-f366af767c0e" "webapp.istioinaction.io" "10.10.0.14:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.9:47236 10.10.0.9:8080 172.18.0.100:51548 - -

# west 에 webapp 로그
kwest logs -n istioinaction -l app=webapp -c istio-proxy -f
[2025-05-24T04:44:34.448Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 2 2 "172.18.0.100" "beegoServer" "8a493851-2606-97a3-b8e1-0348d7a272f5" "catalog.istioinaction.svc.cluster.local:80" "172.18.255.202:15443" outbound|80||catalog.istioinaction.svc.cluster.local 10.10.0.14:51284 10.100.0.123:80 172.18.0.100:0 - default

kwest logs -n istioinaction -l app=webapp -c webapp -f
2025/05/24 04:44:54.248 [M] [router.go:1014]  172.18.0.100 - - [24/May/2025 04:44:54] "GET /api/catalog HTTP/1.1 200 0" 0.019467  curl/8.7.1

# west 에 istio-eastwestgateway 로그
kwest exec -it -n istio-system deploy/istio-eastwestgateway -- curl -X POST http://localhost:15000/logging?level=debug
kwest logs -n istio-system -l app=istio-eastwestgateway -f
2025-05-24T04:45:25.666899Z     debug   envoy pool external/envoy/source/common/http/http1/conn_pool.cc:53      [C3] response complete  thread=30
2025-05-24T04:45:25.666910Z     debug   envoy pool external/envoy/source/common/conn_pool/conn_pool_base.cc:215 [C3] destroying stream: 0 remaining     thread=30
2025-05-24T04:45:25.666996Z     debug   envoy connection external/envoy/source/common/network/connection_impl.cc:720    [C713] write flush complete     thread=30
2025-05-24T04:45:25.667000Z     debug   envoy connection external/envoy/source/common/network/connection_impl.cc:250    [C713] closing socket: 1        thread=30
2025-05-24T04:45:25.667014Z     debug   envoy conn_handler external/envoy/source/extensions/listener_managers/listener_manager/active_stream_listener_base.cc:120        [C713] adding to cleanup list   thread=30
2025-05-24T04:45:27.264865Z     debug   envoy dns external/envoy/source/extensions/network/dns_resolver/cares/dns_impl.cc:354   dns resolution for zipkin.istio-system started   thread=21
2025-05-24T04:45:27.267066Z     debug   envoy dns external/envoy/source/extensions/network/dns_resolver/cares/dns_impl.cc:275   dns resolution for zipkin.istio-system completed with status 0   thread=21
2025-05-24T04:45:27.267198Z     debug   envoy upstream external/envoy/source/common/upstream/upstream_impl.cc:451       transport socket match, socket default selected for host with address 10.100.0.77:9411   thread=21
2025-05-24T04:45:27.267319Z     debug   envoy upstream external/envoy/source/extensions/clusters/strict_dns/strict_dns_cluster.cc:180   DNS refresh rate reset for zipkin.istio-system, refresh rate 30000 ms    thread=21
2025-05-24T04:45:27.439750Z     debug   envoy main external/envoy/source/server/server.cc:265   flushing stats  thread=21
failed to create fsnotify watcher: too many open files
...

# 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
[2025-05-24T04:46:06.861Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 1 1 "172.18.0.100" "beegoServer" "22e938a3-4e9a-9568-abd0-499fa3f8df39" "catalog.istioinaction.svc.cluster.local:80" "10.20.0.14:3000" inbound|3000|| 127.0.0.6:52925 10.20.0.14:3000 172.18.0.100:0 outbound_.80_._.catalog.istioinaction.svc.cluster.local default

keast logs -n istioinaction -l app=catalog -c catalog -f
request path: /items
blowups: {}
number of blowups: 0
GET catalog.istioinaction.svc.cluster.local:80 /items 200 502 - 0.295 ms
GET /items 200 0.295 ms - 502
request path: /items
blowups: {}
number of blowups: 0
GET catalog.istioinaction.svc.cluster.local:80 /items 200 502 - 0.589 ms
GET /items 200 0.589 ms - 502
  • west : webapp
#
kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i any tcp -nn
kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i lo tcp -nn
kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i eth0 tcp -nn
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
04:48:18.432381 IP 10.10.0.9.47236 > 10.10.0.14.8080: Flags [P.], seq 1195725563:1195727205, ack 3236564876, win 4280, options [nop,nop,TS val 4060446648 ecr 1706652361], length 1642: HTTP
04:48:18.432413 IP 10.10.0.14.8080 > 10.10.0.9.47236: Flags [.], ack 1642, win 812, options [nop,nop,TS val 1706657818 ecr 4060446648], length 0
04:48:18.437333 IP 10.10.0.14.57408 > 172.18.255.202.15443: Flags [P.], seq 1763303002:1763304380, ack 425370800, win 890, options [nop,nop,TS val 1761527357 ecr 605657766], length 1378
04:48:18.440319 IP 172.18.255.202.15443 > 10.10.0.14.57408: Flags [P.], seq 1:1789, ack 1378, win 814, options [nop,nop,TS val 605659977 ecr 1761527357], length 1788
...

# 
keast get svc -n istio-system istio-eastwestgateway
NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                                           AGE
istio-eastwestgateway   LoadBalancer   10.200.0.139   172.18.255.202   15021:30532/TCP,15443:30060/TCP,15012:30003/TCP,15017:31492/TCP   63m
  • east : catalog
# 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/
total 112
-rw-r--r-- 1 tcpdump tcpdump 114139 May 24 04:49 dump.pcap

# 출력 결과 파일을 로컬로 다운로드
keast get pod -n istioinaction -l app=catalog -oname
pod/catalog-6cf4b97d-p8xbj

keast cp -n istioinaction -c istio-proxy catalog-6cf4b97d-p8xbj:var/lib/istio/data/dump.pcap ./dump.pcap

# 로컬로 다운 받은 파일을 wireshark 로 불러오기 : XFF 로 요청 Client IP 확인
wireshark dump.pcap


1. west의 인그레스 게이트웨이에 요청을 트리거하면 요청이 west-cluster 의 webapp 으로 라우팅됨을 알 수 있다.
2. 그런 다음, 요청을 처리하는 east-cluster 의 catalog 워크로드로 해석된다.

  • 이로써 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시가 설정됐고 두 클러스터가 서로의 워크로드를 찾을 수 있음을 확인했다.
  • 워크로드들은 east-west 게이트웨이를 통과 지점으로 사용해 상호 인증 커넥션을 시작한다.

1.3.7.6 다중 클러스터 서비스 메시를 설정하는데 필요한 것

  1. 클러스터 간 워크로드 디스커버리
    • 서비스 어카운트 토큰과 인증서가 포함된 kubeconfig 를 사용해 각 컨트롤 플레인에 동료 클러스터에 대한 접근 권한을 제공함으로써 구현한다.
    • 이 과정은 istioctl 을 사용해 쉽게 진행했고, east-cluster 에만 적용했다.
  2. 클러스터 간 워크로드 연결
    • 다른 클러스터의 워크로드(다른 네트워크에 위치) 간에 트래픽을 라우팅하도록 east-west 게이트웨이를 설정하고, 이스티오가 워크로드가 위치한 네트워크를 알 수 있도록 각 클러스터에 네트워크 정보 레이블을 지정해 구현한다.
  3. 클러스터 간 신뢰 설정
    • 상대 클러스터와 동일한 루트 신뢰로 중간 인증서를 발급함으로써 설정한다.
  • 겨우 몇 단계 정도이며, 대부분 자동화돼 다중 클러스터 서비스 메시를 설정한다.
  • 다음 절에서는 클러스터 간의 서비스 메시 동작을 몇 가지 확인해본다.

1.3.8 클러스터 간 로드 밸런싱

1.3.8.1 클러스터 간 로드 밸런싱

  • 이제 다중 클러스터 서비스 메시가 있으니 그럴 준비가 됐다. 이를 시연하기 위해 2개의 샘플 서비스를 배포할 것이다.
  • 이 서비스들은 워크로드가 실행 중인 클러스터의 이름을 반환하도록 설정돼 있다. 그러므로 요청을 처리하는 워크로드의 위치를 쉽게 확인할 수 있다.
  • west-cluster 에 첫 번째 서비스를 배포해보자
#
tree ch12/locality-aware/west
ch12/locality-aware/west
├── simple-backend-deployment.yaml
├── simple-backend-dr.yaml
├── simple-backend-gw.yaml
├── simple-backend-svc.yaml
└── simple-backend-vs.yaml

# west-cluster 에 간단한 백엔드 디플로이먼트/서비스를 배포
cat ch12/locality-aware/west/simple-backend-deployment.yaml
...
        - name: "MESSAGE"
          value: "Hello from WEST"    
...

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
kwest get deploy -n istioinaction simple-backend-west
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
simple-backend-west   1/1     1            1           14s

kwest get svc,ep -n istioinaction simple-backend
NAME                     TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/simple-backend   ClusterIP   10.100.0.62   <none>        80/TCP    27s

NAME                       ENDPOINTS         AGE
endpoints/simple-backend   10.10.0.16:8080   27s

# 트래픽을 허용하기 위해 Gateway, 게이트웨이에서 백엔드 워크로드로 트래픽을 라우팅하기 위해 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
kwest get gw,vs,dr -n istioinaction
NAME                                                 AGE
gateway.networking.istio.io/coolstore-gateway        154m
gateway.networking.istio.io/simple-backend-gateway   46s

NAME                                                               GATEWAYS                     HOSTS                                 AGE
virtualservice.networking.istio.io/simple-backend-vs-for-gateway   ["simple-backend-gateway"]   ["simple-backend.istioinaction.io"]   46s
virtualservice.networking.istio.io/webapp-virtualservice           ["coolstore-gateway"]        ["webapp.istioinaction.io"]           154m
  • west-cluster의 서비스로 요청하고 클러스터 이름을 반환하는지 확인하자.
# west-cluster의 서비스로 요청하고 클러스터 이름을 반환하는지 확인
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"

docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP
{
  "name": "simple-backend-west",
  "uri": "/",
  "type": "HTTP",
  "ip_addresses": [
    "10.10.0.16"
  ],
  "start_time": "2025-05-24T05:01:47.801083",
  "end_time": "2025-05-24T05:01:47.952141",
  "duration": "151.058ms",
  "body": "Hello from WEST",
  "code": 200
}

# 신규 터미널 : 반복 접속
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 에 서비스를 배포하자.
#
tree ch12/locality-aware/east
ch12/locality-aware/east
├── simple-backend-deployment.yaml
└── simple-backend-svc.yaml

# east-cluster 에 서비스를 배포
cat ch12/locality-aware/east/simple-backend-deployment.yaml
...
        - name: "MESSAGE"
          value: "Hello from EAST"
...

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
keast get deploy -n istioinaction simple-backend-east
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
simple-backend-east   1/1     1            1           13s

keast get svc,ep -n istioinaction simple-backend
NAME                     TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/simple-backend   ClusterIP   10.200.0.94   <none>        80/TCP    20s

NAME                       ENDPOINTS         AGE
endpoints/simple-backend   10.20.0.16:8080   20s
  • 양쪽 클러스터에서 서비스가 실행되면서 인그레스 게이트웨이에 엔드포인트가 설정돼 요청이 양쪽으로 분산된다
  • kiali : west, east
    • west-cluster
    • east-cluster
  • 기본적으로, 이스티오는 라운드 로빈 알고리듬으로 워크로드 간에 로드 밸런싱한다. 그러므로 트래픽은 고르게 분산된다.
# 10회 요청 후 확인
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
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
  10 
   5 "Hello from EAST"
   5 "Hello from WEST"

# 정보 확인
kwest get svc,ep -n istioinaction simple-backend
NAME                     TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/simple-backend   ClusterIP   10.100.0.62   <none>        80/TCP    12m

NAME                       ENDPOINTS         AGE
endpoints/simple-backend   10.10.0.16:8080   12m # k8s service 에 endpoint 에는 west 에 파드 ip만 출력

#
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
>> k8s cluster : west - istio-config listener <<
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
...

>> k8s cluster : west - istio-config route <<
NAME          DOMAINS                             MATCH                  VIRTUAL SERVICE
http.8080     simple-backend.istioinaction.io     /*                     simple-backend-vs-for-gateway.istioinaction
...

>> k8s cluster : west - istio-config cluster <<
SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE      
simple-backend.istioinaction.svc.cluster.local               80        -          outbound      EDS     
...

>> k8s cluster : west - istio-config endpoint <<
ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
10.10.0.14: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
...

#
iwest proxy-config listener deploy/istio-ingressgateway.istio-system
ADDRESS PORT  MATCH DESTINATION
0.0.0.0 8080  ALL   Route: http.8080
0.0.0.0 15021 ALL   Inline Route: /healthz/ready*
0.0.0.0 15090 ALL   Inline Route: /stats/prometheus*

iwest proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json

iwest proxy-config route deploy/istio-ingressgateway.istio-system
NAME          DOMAINS                             MATCH                  VIRTUAL SERVICE
http.8080     simple-backend.istioinaction.io     /*                     simple-backend-vs-for-gateway.istioinaction
http.8080     webapp.istioinaction.io             /*                     webapp-virtualservice.istioinaction
              *                                   /healthz/ready*        
              *                                   /stats/prometheus*     
iwest proxy-config route deploy/istio-ingressgateway.istio-system --name http.8080
NAME          DOMAINS                             MATCH     VIRTUAL SERVICE
http.8080     simple-backend.istioinaction.io     /*        simple-backend-vs-for-gateway.istioinaction
http.8080     webapp.istioinaction.io             /*        webapp-virtualservice.istioinaction

iwest proxy-config route deploy/istio-ingressgateway.istio-system --name http.8080 -o json

iwest proxy-config cluster deploy/istio-ingressgateway.istio-system
iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn simple-backend.istioinaction.svc.cluster.local -o json

iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep simple
10.10.0.16: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

iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
[
    {
        "name": "outbound|80||simple-backend.istioinaction.svc.cluster.local",
        "addedViaApi": true,
        "hostStatuses": [
            {
                "address": {
                    "socketAddress": {
                        "address": "10.10.0.16",
                        "portValue": 8080
                    }
                "weight": 1,
                "locality": {}
            ...
            {
                "address": {
                    "socketAddress": {
                        "address": "172.18.255.202",
                        "portValue": 15443
                    }
                "weight": 1,
                "locality": {}
...
  • 좋다! 그러나 워크로드가 트래픽을 라우팅할 때 자신의 지역 내 워크로드를 우선하도록 지역 인식 로드 밸런싱을 사용하면 성능을 더 개선할 수 있다.
  • 앞선 장들에서 클라우드 프로바이더가 지역성 정보를 노드에 레이블로 추가한다는 점을 언급한 바 있다.
  • 이스티오는 레이블을 추출한 이 정보를 사용해 워크로드의 지역을 설정한다.

1.3.8.2 클러스터 간 지역 인식 라우팅 검증하기

  • 실습을 위해 노드에 지역성 정보 레이블을 설정하자
#
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"
...

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 eds 에 정보 반영을 위해 파드 재기동하자 : isiotd 가 노드의 지역성 정보 레이블을 엔드포인트 설정할 때 워크로드로 전파.
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
...
                "weight": 1,
                "locality": {}
...

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"
                }
...
  • 지역성 정보를 사용하려면 수동적 passive 헬스 체크가 필수라고 했던 것을 기억하자.
  • 엔드포인트 상태를 수동적으로 확인하도록, 이상값 감지를 사용하는 DestinationRole 을 적용해보자.
#
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
destinationrule.networking.istio.io/simple-backend-dr created

kwest get gw,vs,dr -n istioinaction
NAME                                                 AGE
gateway.networking.istio.io/coolstore-gateway        3h
gateway.networking.istio.io/simple-backend-gateway   25m

NAME                                                               GATEWAYS                     HOSTS                                 AGE
virtualservice.networking.istio.io/simple-backend-vs-for-gateway   ["simple-backend-gateway"]   ["simple-backend.istioinaction.io"]   25m
virtualservice.networking.istio.io/webapp-virtualservice           ["coolstore-gateway"]        ["webapp.istioinaction.io"]           3h

NAME                                                    HOST                                             AGE
destinationrule.networking.istio.io/simple-backend-dr   simple-backend.istioinaction.svc.cluster.local   24s

# 확인
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
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.19: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
  • 몇 초 정도 걸려 설정이 전파되면, 요청이 지역 정보를 사용해 동일 클러스터 안에서 라우팅 되는 것을 확인할 수 있다.
# 
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
{
  "name": "simple-backend-west",
  "uri": "/",
  "type": "HTTP",
  "ip_addresses": [
    "10.10.0.19"
  ],
  "start_time": "2025-05-24T05:27:32.144871",
  "end_time": "2025-05-24T05:27:32.296805",
  "duration": "151.935ms",
  "body": "Hello from WEST",
  "code": 200
}

docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
"Hello from 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"
  • 기대한 대로 모든 요청은 west-cluster 내에서 라우팅되는데, 트래픽을 라우팅하는 인그레스 게이트웨이에서 가장 가깝기 때문이다.
  • 또한 모든 라우팅 결정을 엔보이 프록시가 내리므로, 컨트롤 플레인이 엔보이 프록시의 설정을 수정했으리라고 결론지을 수 있다.
    • 우선순위가 가장 높은 호스트가 사용할 수 없는 상태가 되면, 트래픽은 우선순위가 낮은 호스트로 라우팅된다.
#
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"
                }
...

1.3.8.3 클러스터 간 장애 극복 확인하기

  • 간단한 백엔드 디플로이먼트가 실패하는 상황을 시뮬레이션하려면, 환경 변수 ERROR_RATE 값을 1로 설정해 요청이 실패하게 만들자.
  • 시간이 조금 지나면, 이상값 감지가 호스트가 비정상임을 감지하고 트래픽을 우선순위가 두 번째인 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-18 09:31:21

"Hello from EAST" # failover 시점 
                 2025-05-18 09:31:23
...


#
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

#
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.20: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

  • 이 결과는 클러스터 간 장애 극복이 작동함을 보여준다.
  • 클러스터 간 트래픽은 상대 클러스터의 east-west 게이트웨이를 통과하며 SNI 통과로 처리된다는 것을 알 수 있다.
  • 이는 원격 클러스터에 도달한 트래픽의 로드 밸런싱에 영향을 미친다.
  • 이 호출은 SNI/TCP 커넥션이라 게이트웨이가 TLS 커넥션을 종료하지 않으므로, east-west 게이트웨이는 커넥션을 그대로 백엔드 서비스에 전달할 수 밖에 없다.
  • 이렇게 되면 커넥션이 east-west 게이트웨이에서 백엔드 서비스까지 이어지기 때문에 요청 단위로 로드 밸런싱되지 않는다.
  • 그러므로 클러스터 사이의 장애 극복이나 로드 밸런싱에서는, 클라이언트의 관점으로는 부하 분산이나 장애 극복이 수행되지만 트래픽이 원격 클러스터의 모든 인스턴스 사이에서 반드시 균등하게 분산되는 것은 아니다.

1.3.8.4 인가정책을 사용해 클러스터 간 접근 제어 확인하기

  • 마지막으로 확인할 기능은 클러스터 사이의 접근 제어다.
  • 접근 제어를 하려면 워크로드가 트래픽을 서로 인증해, 트래픽을 승인하거나 거부하는 결정을 내리는 데 사용 가능한 믿을 수 있는 메타데이터를 만들어야 한다는 점을 기억하자.
  • 이를 시연하기 위해 시나리오를 생각해보자.
  • 트래픽 출처가 인그레스 게이트웨이인 경우에만 서비스로의 트래픽을 허용하고 싶다고 해보자. 출처가 다르면 거절한다.
  • 그렇게 하기 위한 정책을 정의해 ch12/security/allow-only-ingress-policy.yaml 파일에 저장해뒀다. 이를 east-cluster 에 적용하자.
# 적용 전에 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
authorizationpolicy.security.istio.io/allow-only-ingress created
keast get authorizationpolicy -A
NAMESPACE       NAME                 AGE
istioinaction   allow-only-ingress   11s
  • 업데이트가 전파되고 나면, west-cluster 의 워크로드에서 요청을 만들어 정책을 시험해보자. 이를 위해 임시 파드를 실행한다.
#
kwest run netshoot -n istioinaction --rm -it --image=nicolaka/netshoot -- zsh
-----------------------------------
#
curl -s webapp.istioinaction/api/catalog
[{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}

# 직접 요청하면 실패!
curl -s simple-backend.istioinaction.svc.cluster.local
RBAC: access denied

# 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.19"
  ],
  "start_time": "2025-05-24T05:42:16.015504",
  "end_time": "2025-05-24T05:42:16.166680",
  "duration": "151.176ms",
  "body": "Hello from EAST",
  "code": 200
}

# kiali 등 확인을 위해 반복 접속 실행
watch curl -s simple-backend.istioinaction.svc.cluster.local
watch 'curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system'

exit
-----------------------------------
  • 정책이 인그레스 게이트웨이에서 들어온 트래픽을 허용한 것을 볼 수 있다.
  • 이는 워크로드가 클러스터 간에 상호 인증해서 정책이 ID 인증서에 인코딩된 인증 데이터를 접근 제어에 사용할 수 있음을 보여준다.
    • 로드 밸런싱, 지역 인식 라우팅, 클러스터 간 장애 극복, 상호 인증 트래픽, 접근 제어에 대한 우리의 모든 예제는 다중 클러스터 서비스 메시에서의 워크로드가 실행 중인 클러스터가 어디든 상관없이 이스티오의 모든 기능을 사용할 수 있음을 보여준다. 그리고 설정을 따로 더 하지 않아도 그럴 수 있다.
  • 바라건데, 이번 장이 이스티오가 조직 내에서 어떻게 확장하는지, 어떻게 여러 클러스터를 단일 메시로 통합하는지, 여러 조직에게 그것이 왜 중요한지를 충분히 보여줬길 바란다.
  • 다름장에서는 가상머신을 서비스 메시로 통합해본다. 이는 레거시 워크로드를 운영해야 하는 성숙한 기업에게 아주 가치 있는 기능이다.

1.3.9 Summary

  • 이스티오는 단일 컨트롤 플레인(기본-원격), 복제된 컨트롤 플레인(기본-기본), 외부 컨트롤 플레인이라는 세 가지 다중 서비스 메시 배포 모델을 지원한다.
  • istio-system 네임스페이스에 중간 인증서를 설치해 플러그인 CA 인증서를 사용하면 클러스터 간에 공통 신뢰를 구축할 수 있다.
  • 복제된 컨트롤 플레인 배포 모델에서 클러스터 간 워크로드를 찾는 방법은 원격 클러스터의 서비스 어카운트를 ID로 사용하고 시크릿으로 서비스 어카운트 토큰을 상대편 클러스터에서 사용할 수 있게 하는 것이다.
  • east-west 게이트웨이를 사용해 다중 네트워크 서비스 메시의 네트워크를 연결할 수 있다. sni-dnat 라우터 모드는 클러스터 간 트래픽을 세밀한 방식으로 라우팅하도록 SNI 클러스터를 설정한다.
  • east-west 게이트웨이는 트래픽을 자동으로 통과시키고 자동으로 설정된 SNI 클러스터를 바탕으로 라우팅하도록 설정할 수 있다.
  • 이스티오의 기능은 클러스터 간에도 단일 클러스터일 때와 같은 방식으로 동작한다.

1.3.10 실습 후 kind 삭제

⛔️ kind delete cluster --name west && kind delete cluster --name east && docker rm -f mypc

2. 이스티오 요청처리 기능 확장

✅ Istio 확장의 필요성

  • Istio는 애플리케이션 네트워킹에 많은 가치를 제공하지만, 모든 조직의 제약과 요구 사항을 기본 기능만으로는 충족하기 어렵다.
  • 따라서 조직의 상황에 맞게 Istio 기능을 확장해야 할 필요가 있다.

✅ Envoy는 Istio의 핵심

  • Istio의 핵심 구성 요소는 Envoy 프록시이며, 이는 서비스 메시 내에서 트래픽을 처리한다.
  • 기본 기능만으로도 강력하지만, 특정 요구 사항을 위해 Envoy를 커스터마이징해야 할 수도 있다.

✅ Envoy 확장 예시

  • 속도 제한 또는 외부 인가 서비스와의 연동
  • HTTP 헤더 조작 (추가, 제거, 수정)
  • 요청 보강을 위한 외부 서비스 호출
  • HMAC 같은 사용자 정의 프로토콜 구현
  • 비표준 보안 토큰 처리

✅ 핵심 메시지

  • Istio를 확장한다는 것은 결국 Envoy를 확장하는 것이며, 이는 요청 경로 상의 동작을 조정하는 데 필수적이다.

2.1 Envoy 확장 기능

2.1.1 Envoy Life of a Request - Docs






📌 Envoy의 전체 요청 처리 흐름을 설명한 12단계 과정

  1. 다운스트림으로부터 TCP 연결이 들어오면, Envoy의 리스너(listener)가 실행 중인 워커 스레드에서 해당 연결을 수락합니다.

  2. 리스너 필터 체인이 생성되고 실행됩니다. 이 필터는 SNI 등 TLS 이전의 정보를 제공할 수 있습니다. 필터 체인이 완료되면, 리스너는 네트워크 필터 체인을 매칭합니다. 리스너는 대상 IP CIDR 범위, SNI, ALPN, 소스 포트 등 조합으로 여러 필터 체인을 가질 수 있습니다. 이 필터 체인에는 전송 소켓(이 경우 TLS 소켓)이 연결됩니다.

  3. 네트워크로부터 데이터를 읽는 과정에서, TLS 전송 소켓은 TCP 연결에서 읽은 암호화된 데이터를 복호화하여 처리 가능한 데이터 스트림으로 변환합니다.

  4. 네트워크 필터 체인이 생성되고 실행됩니다. 이 중 가장 중요한 필터는 HTTP 연결 관리자(HTTP connection manager)로, 네트워크 필터 체인의 마지막 필터입니다.

  5. HTTP 연결 관리자의 HTTP/2 코덱은 TLS 연결에서 복호화된 데이터를 프레임 해제 및 멀티플렉싱하여, 독립적인 여러 스트림으로 분리합니다. 각 스트림은 하나의 요청과 응답을 처리합니다.

  6. 각 HTTP 스트림마다 다운스트림 HTTP 필터 체인이 생성되고 실행됩니다. 요청은 CustomFilter를 먼저 통과하며, 이 필터는 요청을 읽고 수정할 수 있습니다. 필터 체인의 마지막에는 라우터 필터(router filter)가 있으며, 이 필터에서 decodeHeaders가 호출되면 라우트를 선택하고 클러스터를 결정합니다. 선택된 클러스터 내의 업스트림 엔드포인트로 요청 헤더가 전달됩니다. 이 때 라우터 필터는 매칭된 클러스터에 대해 HTTP 커넥션 풀을 가져옵니다.

  7. 클러스터에 따라 로드 밸런싱이 수행되어 엔드포인트가 선택됩니다. 클러스터의 서킷 브레이커(circuit breaker)를 확인하여 새로운 스트림이 허용 가능한지 확인한 후, 커넥션 풀이 비었거나 용량이 부족하면 새로운 연결이 생성됩니다.

  8. 각 스트림마다 업스트림 HTTP 필터 체인이 생성되어 실행됩니다. 기본적으로는 CodecFilter만 포함되며, 이 필터는 데이터를 적절한 코덱으로 전송합니다. 클러스터가 업스트림 HTTP 필터 체인을 구성하고 있는 경우, 재시도섀도 요청 처리를 위한 필터 체인도 별도로 실행됩니다.

  9. 업스트림 엔드포인트와의 연결에 존재하는 HTTP/2 코덱은 해당 스트림을 다른 스트림들과 함께 하나의 TCP 연결로 멀티플렉싱 및 프레이밍합니다.

  10. 업스트림 연결의 TLS 전송 소켓은 이러한 바이트를 암호화한 뒤, 업스트림으로 향하는 TCP 소켓에 기록합니다.

  11. 요청(헤더, 선택적 본문 및 트레일러로 구성됨)은 업스트림으로 프록시되고, 응답은 다운스트림으로 프록시됩니다. 응답은 HTTP 필터들을 요청의 반대 순서로 통과합니다. 즉, CodecFilter부터 시작하여 업스트림 HTTP 필터 → 라우터 필터 → CustomFilter를 거쳐 다운스트림으로 전달됩니다.

  12. 스트림의 종료 처리는 다음 조건에 따라 다릅니다:

    • 독립적인 half-close가 활성화된 경우: 요청과 응답 양쪽 모두 완료되고(HTTP/2에서 양방향으로 END_STREAM 수신됨), 응답 상태 코드가 성공(2xx)이면 스트림이 종료됩니다.
    • 그렇지 않으면: 응답이 완료되면 스트림이 종료되며, 요청이 완료되지 않았더라도 종료됩니다.
      이후 처리로는 통계 업데이트, 액세스 로그 기록, 트레이스 스팬 종료가 포함됩니다.

2.1.2 필터 확장

  • 엔보이 프록시의 강점 중 하나는 확장할 수 있게 만들어졌다는 점이다.
  • 엔보이의 API를 설계하는 데 많은 고민과 주의가 반영됐으며, 다른 사람들이 작성한 확장 기능도 인기를 얻게 해준 큰 이유 중 하나다.
  • 엔보이를 확장할 수 있는 주요 방법은 필터 확장이다.
  • 엔보이를 확장할 수 있는 지점이 어디인지, 무엇이 애플리케이션에 가장 큰 이점을 주는지 이해하려면 먼저 엔보이의 아키텍처를 이해해야 한다.

2.1.3 엔보이의 필터 체인 이해하기

📘 Envoy의 리스너와 필터 개념
3장에서 소개한 리스너, 라우트, 클러스터는 고수준 개념이었고, 이번 장에서는 그 중 리스너와 필터 체인에 대해 더 자세히 다룸.

  • 리스너(listener)는 네트워크 인터페이스의 포트를 열고 수신 트래픽을 처리하기 시작하는 Envoy의 기본 단위.

  • Envoy는 L3/L4 계층 프록시로서, 네트워크 연결에서 바이트를 읽어 다양한 방식으로 처리함.

  • 이 처리 과정에서 핵심이 되는 구성 요소는 바로 필터(filter).

  • 리스너는 네트워크 스트림에서 바이트를 읽은 후, 이를 여러 필터 단계(filter chain)를 통해 순차적으로 처리함.

🔧 Envoy의 네트워크 필터와 필터 체인

  • 네트워크 필터는 Envoy에서 가장 기본적인 필터로, 바이트 스트림을 인코딩/디코딩하는 데 사용됨.

  • 여러 필터를 순차적으로 구성한 것을 필터 체인(filter chain)이라고 하며, 이를 통해 프록시의 다양한 기능을 구현할 수 있음.

  • Envoy는 다양한 프로토콜을 지원하는 네트워크 필터를 제공함:

    • MongoDB
    • Redis
    • Thrift
    • Kafka
    • HttpConnectionManager (HCM)
  • 이 중 HttpConnectionManager는 가장 널리 사용되는 필터 중 하나이며, 바이트 스트림을 HTTP 헤더, 바디, 트레일러 등으로 변환하는 역할.
    HTTP 1.1, HTTP/2, gRPC, HTTP/3 등의 HTTP 기반 프로토콜을 추상화함.
    이 필터는 Envoy가 L7 기능을 수행할 수 있게 해주는 핵심 컴포넌트입니다.

🌐 HttpConnectionManager (HCM)의 주요 기능

  • HTTP 요청 처리 외에도 다음을 지원:

    • 헤더 기반 라우팅
    • 경로 접두사(path prefix) 기반 라우팅
    • 액세스 로그 작성
    • 헤더 조작
  • 필터 기반 아키텍처를 사용:

    • HTTP 요청에 대해 여러 HTTP 필터를 순서대로 구성해 동작 가능.
    • 필터 체인의 마지막에는 업스트림 클러스터로 요청을 보내는 터미널 필터가 반드시 있어야 함.
  • 주요 HTTP 필터 예시 (전체 목록은 공식 문서 참조):

    • CORS, CSRF
    • External Auth, Rate Limit
    • Fault Injection, gRPC/JSON Transcoding
    • Gzip, Lua, Tap, WebAssembly (Wasm)
    • RBAC, Router (터미널 필터)
  • Router 필터:

    • 필터 체인의 마지막에 위치
    • 업스트림 클러스터 선택 및 요청 전달을 담당
    • 타임아웃재시도 설정 가능
  • 즉, HCM은 L7 HTTP 요청을 처리하고, 필터 체인을 통해 요청을 세밀하게 조작하거나 제어하며, 최종적으로 라우터 필터를 통해 업스트림으로 전송합니다.

  • 또한 사용자는 엔보이의 핵심 코드를 변경하지 않고도 자신만의 필터를 직접 작성하고 프록시 위에 얹어 계층화할 수 있다.

  • 예를 들어, 이스티오의 프록시는 데이터 플레인용으로 엔보이 위에 필터를 추가해 커스텀 엔보이를 빌드한다.

  • 그렇지만 이렇게 커스텀 엔보이 프록시 빌드를 도입하면, 유지 보수할 것이 많아질 수 있고 개발자가 C++를 사용해야 한다.

2.1.4 확장용 필터

  • 자신만의 필터를 C++로 작성해 프록시에 내장할 수 있지만, 이와 관련된 내용은 이 책의 범위를 벗어난다.
  • 필터 작성 등과 같이 엔보이 바이너리 자체에 변경 사항을 컴파일하지 않고도 엔보이의 HTTP 기능을 확장하는 방법이 있는데, 이 경우 다음 HTTP 필터들을 사용한다.
    • 외부 처리
    • 루아
    • 웹어셈블리 Wasm
  • 이런 필터를 사용하면 외부 서비스를 호출하도록 설정하거나, 루아 스크립트를 실행하거나, 커스텀 코드를 실행해 HTTP 요청이나 응답을 처리할 때 HCM의 기능을 강화할 수 있다.
  • 외부 서비스를 호출해 처리하는 부분에 대해 이야기할 때는 속도 제한 필터를 중점을 두겠다.
  • 또한 9장에서 다룬 것처럼 외부 인가를 요청할 수도 있다.

    엔보이에는 범용 처리 목적으로 외부 서비스를 호출하기 위한 외부 처리 필터가 있다. 이 필터는 코드베이스에 존재하기는 하지만, 저술 시점에는 아무것도 하지 않는다. 우리는 전역 속도 제한 필터를 사용하는 등 외부 서비스를 호출하는 다른 방법에 초점을 맞춘다.

2.1.5 이스티오의 데이터 플레인 커스터마이징하기

  • 엔보이의 필터 아키텍처를 깊이 있게 이해한 것을 바탕으로, 이후 절들에서 다음 방법 중 하나를 사용해 엔보이 데이터 플레인의 기능을 확장한다.
    • 엔보이 HTTP 필터를 이스티오 API에서 EnvoyFilter 리소스로 설정하기
    • 속도 제한 서버 RLS, Rate-Limit Server 호출하기
    • 루아 스크립트를 구현해 루아 HTTP 필터에 불러오기
    • 웹어셈블리 HTTP 필터용 웹어셈블리 모듈(Wasm 모듈) 구현하기
  • 엔보이의 필터를 직접 설정하는 방법을 이해해야 하며, 이를 위해 이스티오의 EnvoyFilter 리소스를 사용할 것이다.
  • 이전 장들에서 이미 사용해봤지만, 여기서 더 자세히 살펴본다.

2.2 EnvoyFilter 리소스 설정

2.2.1 [실습 환경 구성] k8s(1.23.17) 배포 & mypc 컨테이너

#
git clone https://github.com/AcornPublishing/istio-in-action
cd istio-in-action/book-source-code-master
pwd # 각자 자신의 pwd 경로
code .

# 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능
kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000 # Sample Application (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 # Sample Application (istio-ingrssgateway) HTTPS
    hostPort: 30005
  - containerPort: 30006 # TCP Route
    hostPort: 30006
  - containerPort: 30007 # kube-ops-view
    hostPort: 30007
  extraMounts: # 해당 부분 생략 가능
  - hostPath: /Users/sjkim/Labs/CloudNeta/istio/book-source-code-master # 각자 자신의 pwd 경로로 설정
    containerPath: /istiobook
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.0.0/22
EOF

# 설치 확인
docker ps

# 노드에 기본 툴 설치
docker exec -it myk8s-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 repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view

## kube-ops-view 접속 URL 확인
open "http://localhost:30007/#scale=1.5"
open "http://localhost:30007/#scale=1.3"

# (옵션) metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
  • kind docker network 에 테스트용 PC(실제로는 컨테이너) 배포
# kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
docker network ls
docker inspect kind

# mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 없이 혹은 지정 해서 사용
docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시
혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시
docker ps

# kind network 중 컨테이너(노드) IP(대역) 확인
docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
/mypc 172.18.0.100
/myk8s-control-plane 172.18.0.2

# 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인!
docker exec -it mypc ping -c 1 172.18.0.2
docker exec -it mypc ping -c 1 myk8s-control-plane
docker exec -it myk8s-control-plane ping -c 1 mypc

2.2.2 [실습 환경 구성] (편리성 설정) MetalLB 배포

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

# 확인
kubectl get crd  
kubectl get pod -n metallb-system

# IPAddressPool, L2Advertisement 설정 : MetalLB 파드(speaker) 배포 정상 완료 후 아래 설정 실행하자.
cat << EOF | kubectl apply -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

# 확인
kubectl get IPAddressPool,L2Advertisement -A
NAMESPACE        NAME                               AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
metallb-system   ipaddresspool.metallb.io/default   true          false             ["172.18.255.101-172.18.255.120"]

NAMESPACE        NAME                                 IPADDRESSPOOLS   IPADDRESSPOOL SELECTORS   INTERFACES
metallb-system   l2advertisement.metallb.io/default   ["default"]  
  • 샘플 애플리케이션 배포 후 확인
#
cat << EOF | kubectl apply -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


# 확인
kubectl get deploy,pod,svc,ep
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           101s

NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-8d545c96d-sd66k   1/1     Running   0          101s

NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
service/kubernetes      ClusterIP      10.200.0.1     <none>           443/TCP        7m3s
service/nginx-service   LoadBalancer   10.200.1.231   172.18.255.101   80:30981/TCP   101s

NAME                      ENDPOINTS         AGE
endpoints/kubernetes      172.18.0.2:6443   7m3s
endpoints/nginx-service   10.10.0.6:80      101s

kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
172.18.255.101

LBIP=$(kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# 외부?에서 각 클러스터의 Service(LB)로 접속(인입) 확인 : TCP 80 포트 사용으로 편리하다!
docker exec -it mypc curl -s $LBIP
docker exec -it mypc curl -s $LBIP -v -I
*   Trying 172.18.255.101:80...
* Connected to 172.18.255.101 (172.18.255.101) port 80
> HEAD / HTTP/1.1
> Host: 172.18.255.101
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx/1.27.5
Server: nginx/1.27.5
< Date: Sat, 24 May 2025 07:38:45 GMT
Date: Sat, 24 May 2025 07:38:45 GMT
< Content-Type: text/html
Content-Type: text/html
< Content-Length: 615
Content-Length: 615
< Last-Modified: Wed, 16 Apr 2025 12:01:11 GMT
Last-Modified: Wed, 16 Apr 2025 12:01:11 GMT
< Connection: keep-alive
Connection: keep-alive
< ETag: "67ff9c07-267"
ETag: "67ff9c07-267"
< Accept-Ranges: bytes
Accept-Ranges: bytes
< 

* Connection #0 to host 172.18.255.101 left intact


# 확인 후 삭제
kubectl delete deploy,svc --all

2.2.3 [실습 환경 구성] istio 1.17.8 설치

# myk8s-control-plane 진입 후 설치 진행
docker exec -it myk8s-control-plane bash
-----------------------------------
# (옵션) 코드 파일들 마운트 확인
tree /istiobook/ -L 1
혹은
git clone ... /istiobook

# 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

# demo 프로파일 컨트롤 플레인 배포
istioctl install --set profile=demo --set values.global.proxy.privileged=true -y

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

# 빠져나오기
exit
-----------------------------------

# 설치 확인 : istiod, istio-ingressgateway, crd 등
kubectl get istiooperators -n istio-system -o yaml
kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
kubectl get cm -n istio-system istio -o yaml
kubectl get crd | grep istio.io | sort

# 실습을 위한 네임스페이스 설정
kubectl create ns istioinaction
kubectl label namespace istioinaction istio-injection=enabled
kubectl get ns --show-labels

# istio-ingressgateway 서비스 : nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}'
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
kubectl describe svc -n istio-system istio-ingressgateway

# 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}]}}'

# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:30001

# Grafana 접속
open http://127.0.0.1:30002

# Kiali 접속 : NodePort
open http://127.0.0.1:30003

# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:30004

2.2.4 들어가며 : tap 필터 (실습~)

🧩 Istio 데이터 플레인 확장의 첫 단계: EnvoyFilter

  • 먼저 Envoy의 기존 필터로 원하는 확장이 가능한지 판단해야 함.
  • 기존 필터로 충분하다면, Istio의 EnvoyFilter 리소스를 사용해 직접 Envoy 설정 가능.

🎛️ EnvoyFilter vs Istio 고수준 API

  • Istio의 리소스(VirtualService, DestinationRule, AuthorizationPolicy 등)는 일반적으로 Envoy 설정을 추상화함.

  • 하지만 Istio는 모든 Envoy 기능을 노출하지 않음 → 일부 고급 시나리오에서는 Envoy를 직접 설정해야 함.

  • 이때 사용하는 것이 바로 EnvoyFilter 리소스:

    • 리스너, 라우트, 클러스터, 필터 등 거의 모든 Envoy 설정 가능
    • 단, 일부 제한은 존재

⚠️ EnvoyFilter 사용 시 주의사항

  • EnvoyFilter고급 사용자용이며, 일종의 비상 수단(break-glass solution)
  • Istio 버전 간 Envoy API가 변경될 수 있음
    → 반드시 배포 전 유효성 검증 필수
  • 이 API를 잘못 설정하면 Istio 데이터 플레인을 전체 중단시킬 위험도 있음
    하위 호환성 보장 안 됨

📌 요약하자면, EnvoyFilter는 Istio가 추상화하지 않은 Envoy의 고급 기능을 활용할 수 있는 강력하지만 위험한 도구로, 신중하게 사용해야 합니다.

✅ 예제를 보고 어떻게 작동하는지 이해해보자. 사용할 서비스를 배포하자.

#
kubectl get envoyfilter -A
NAMESPACE      NAME                    AGE
istio-system   stats-filter-1.13       7m7s
istio-system   stats-filter-1.14       7m7s
istio-system   stats-filter-1.15       7m7s
istio-system   stats-filter-1.16       7m7s
istio-system   stats-filter-1.17       7m7s
istio-system   tcp-stats-filter-1.13   7m7s
istio-system   tcp-stats-filter-1.14   7m7s
istio-system   tcp-stats-filter-1.15   7m7s
istio-system   tcp-stats-filter-1.16   7m7s
istio-system   tcp-stats-filter-1.17   7m7s

# 배포
kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
kubectl apply -f ch9/sleep.yaml -n istioinaction
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created

# 확인
kubectl get gw,vs,dr -n istioinaction
NAME                                            AGE
gateway.networking.istio.io/coolstore-gateway   31s

NAME                                                       GATEWAYS                HOSTS                         AGE
virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   31s


# 호출 확인 : mypc
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Web App - Istio in Action</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
  <link rel="stylesheet" href="/static/css/main.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.19.0/cytoscape.min.js" integrity="sha512-TOWs340KHbJjY/a2SCtsUcXYBg7/xPX72NKpJ3VITogkHJTy2yMyoJE0pxJjumMGHCg46ud89KO5q1Toe3Aeaw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
...

</script>

</body>
</html>

docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
[{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]

# 신규 터미널 : mypc 에서 반복 접속 
EXT_IP=$(kubectl -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 ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done

# 호출 확인 : 자신의 호스트 PC
curl -s http://webapp.istioinaction.io:30000
curl -s http://webapp.istioinaction.io:30000/api/catalog

🛠️ 특정 요청 디버깅을 위한 tap 필터 설정

  • webapp 서비스의 특정 요청을 디버깅하기 위해 Istio 데이터 플레인을 확장하려 함.
  • 이를 위해 Envoy의 tap 필터를 사용:
    • 요청/응답을 샘플링하고
    • CLI/콘솔로 스트리밍

🔧 Istio에서 tap 필터 사용하기

  • tap 필터는 Istio의 고수준 API에 노출되어 있지 않음
    EnvoyFilter 리소스를 사용해야 함

⚠️ EnvoyFilter 사용 시 핵심 사항 3가지

  1. 기본 적용 범위: 네임스페이스 전체
    • 별도 지정 없으면 네임스페이스 내 모든 워크로드에 적용
    • istio-system에 생성 시 메시 전체에 영향
    • 👉 특정 워크로드에만 적용하려면 workloadSelector 사용
  2. 적용 시점: 다른 리소스 이후
    • VirtualService, DestinationRule 등의 설정이 먼저 적용
    • EnvoyFilter는 마지막에 덧붙여짐
  3. 위험성: 고급/비상 수단
    • Envoy 명명 규칙과 설정 구조에 대한 깊은 이해 필요
    • 잘못 설정하면 메시 전체가 마비될 수 있음
    • 고급 사용자 전용, 반드시 신중하게 사용

📌 요약하면, EnvoyFilter를 이용한 tap 필터 설정은 특정 요청을 디버깅하는 데 강력한 도구지만, 잘못 쓰면 위험하므로 대상 워크로드 지정과 설정 순서, 안정성을 반드시 고려해야 합니다.

EnvoyFilter 리소스는 다음과 같다

# cat ch14/tap-envoy-filter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: tap-filter
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: webapp # 워크로드 셀렉터
  configPatches:
  - applyTo: HTTP_FILTER # 설정할 위치
    match:
      context: SIDECAR_INBOUND
      listener:
        portNumber: 8080
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch: # 엔보이 설정 패치
      operation: INSERT_BEFORE
      value:
       name: envoy.filters.http.tap
       typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap"
          commonConfig:
            adminConfig:
              configId: tap_config
  1. EnvoyFilter 를 istioinaction 네임스페이스에 배포했다.
    • workloadSelector를 사용해 이 설정을 적용할 워크로드를 구체적으로 지정했다.
  2. 다음으로 엔보이 설정에서 패치할 위치를 지정해야 한다.
    • 이 예제에서는 인바운드 리스너(SIDECAR_INBOUDN)의 HTTP_FILTER가 되도록 지정했다.
    • 앞서 언근한 바와 같이 리스너를 위한 네트워크 필터들이 있으며, 그중 하나가 HCM 이다.
    • HCM에도 HTTP 요청을 처리하는 HTTP용 필터 체인이 있다.
    • 또한 이 예제에서는 특정 리스너도 지정했는데, HCM을 8080 포트에 바인딩된 리스너를 지정했다.
    • 마지막으로, 이 HCM HTTP 필터 체인에서 envoy.filters.http.router HTTP 필터를 골랐다.
    • 이 필터를 고른 이유는, 다음 절에서 보겠지만 새로운 tap 필터를 이 router 필터 바로 앞에 배치할 것이기 때문이다.
  3. EnvoyFilter 리소스의 patch 부분에서 설정을 어떻게 패치할지 지정한다.
    • 여기서는 앞서 설정 부분에서 선택한 필터 앞에 설정을 병합한다.
    • 추가하는 필터인 envoy_filters.http.tap 은 HCM 필터 체인에서 envoy.filters.http.router 앞에 위치한다.
    • tap 필터 설정의 구조를 명확하게 해야 하므로 명시적인 타입을 지정한다 - Docs

이 EnvoyFilter를 istioinaction 네임스페이스에 webapp 워크로드에 적용하자

#
cat ch14/tap-envoy-filter.yaml
kubectl apply -f ch14/tap-envoy-filter.yaml
kubectl get envoyfilter -n istioinaction
NAME         AGE
tap-filter   10s

#
docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction
ADDRESS      PORT  MATCH                                                                                           DESTINATION
10.200.0.10  53    ALL                                                                                             Cluster: outbound|53||kube-dns.kube-system.svc.cluster.local
0.0.0.0      80    Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 80
0.0.0.0      80    ALL                                                                                             PassthroughCluster
10.200.0.1   443   ALL                                                                                             Cluster: outbound|443||kubernetes.default.svc.cluster.local
10.200.1.144 443   ALL                                                                                             Cluster: outbound|443||istio-egressgateway.istio-system.svc.cluster.local
10.200.2.30  443   ALL                                                                                             Cluster: outbound|443||istiod.istio-system.svc.cluster.local
10.200.2.80  443   ALL                                                                                             Cluster: outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.200.3.36  443   Trans: raw_buffer; App: http/1.1,h2c                                                            Route: metallb-webhook-service.metallb-system.svc.cluster.local:443
10.200.3.36  443   ALL                                                                                             Cluster: outbound|443||metallb-webhook-service.metallb-system.svc.cluster.local
10.200.0.21  3000  Trans: raw_buffer; App: http/1.1,h2c                                                            Route: grafana.istio-system.svc.cluster.local:3000
10.200.0.21  3000  ALL                                                                                             Cluster: outbound|3000||grafana.istio-system.svc.cluster.local
0.0.0.0      9090  Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 9090
0.0.0.0      9090  ALL                                                                                             PassthroughCluster
10.200.0.10  9153  Trans: raw_buffer; App: http/1.1,h2c                                                            Route: kube-dns.kube-system.svc.cluster.local:9153
10.200.0.10  9153  ALL                                                                                             Cluster: outbound|9153||kube-dns.kube-system.svc.cluster.local
0.0.0.0      9411  Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 9411
0.0.0.0      9411  ALL                                                                                             PassthroughCluster
10.200.2.135 14250 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: jaeger-collector.istio-system.svc.cluster.local:14250
10.200.2.135 14250 ALL                                                                                             Cluster: outbound|14250||jaeger-collector.istio-system.svc.cluster.local
10.200.2.135 14268 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: jaeger-collector.istio-system.svc.cluster.local:14268
10.200.2.135 14268 ALL                                                                                             Cluster: outbound|14268||jaeger-collector.istio-system.svc.cluster.local
0.0.0.0      15001 ALL                                                                                             PassthroughCluster
0.0.0.0      15001 Addr: *:15001                                                                                   Non-HTTP/Non-TCP
0.0.0.0      15006 Addr: *:15006                                                                                   Non-HTTP/Non-TCP
0.0.0.0      15006 Trans: tls; App: istio-http/1.0,istio-http/1.1,istio-h2; Addr: 0.0.0.0/0                        InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: raw_buffer; App: http/1.1,h2c; Addr: 0.0.0.0/0                                           InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: tls; App: TCP TLS; Addr: 0.0.0.0/0                                                       InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: raw_buffer; Addr: 0.0.0.0/0                                                              InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: tls; Addr: 0.0.0.0/0                                                                     InboundPassthroughClusterIpv4
0.0.0.0      15006 Trans: tls; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2; Addr: *:8080 Cluster: inbound|8080||
0.0.0.0      15006 Trans: raw_buffer; Addr: *:8080                                                                 Cluster: inbound|8080||
0.0.0.0      15010 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 15010
0.0.0.0      15010 ALL                                                                                             PassthroughCluster
10.200.2.30  15012 ALL                                                                                             Cluster: outbound|15012||istiod.istio-system.svc.cluster.local
0.0.0.0      15014 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 15014
0.0.0.0      15014 ALL                                                                                             PassthroughCluster
0.0.0.0      15021 ALL                                                                                             Inline Route: /healthz/ready*
10.200.2.80  15021 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: istio-ingressgateway.istio-system.svc.cluster.local:15021
10.200.2.80  15021 ALL                                                                                             Cluster: outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
0.0.0.0      15090 ALL                                                                                             Inline Route: /stats/prometheus*
10.200.2.80  15443 ALL                                                                                             Cluster: outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
0.0.0.0      16685 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 16685
0.0.0.0      16685 ALL                                                                                             PassthroughCluster
0.0.0.0      20001 Trans: raw_buffer; App: http/1.1,h2c                                                            Route: 20001
0.0.0.0      20001 ALL                                                                                             PassthroughCluster
10.200.2.80  31400 ALL                                                                                             Cluster: outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local


docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006
ADDRESS PORT  MATCH                                                                                           DESTINATION
0.0.0.0 15006 Addr: *:15006                                                                                   Non-HTTP/Non-TCP
0.0.0.0 15006 Trans: tls; App: istio-http/1.0,istio-http/1.1,istio-h2; Addr: 0.0.0.0/0                        InboundPassthroughClusterIpv4
0.0.0.0 15006 Trans: raw_buffer; App: http/1.1,h2c; Addr: 0.0.0.0/0                                           InboundPassthroughClusterIpv4
0.0.0.0 15006 Trans: tls; App: TCP TLS; Addr: 0.0.0.0/0                                                       InboundPassthroughClusterIpv4
0.0.0.0 15006 Trans: raw_buffer; Addr: 0.0.0.0/0                                                              InboundPassthroughClusterIpv4
0.0.0.0 15006 Trans: tls; Addr: 0.0.0.0/0                                                                     InboundPassthroughClusterIpv4
0.0.0.0 15006 Trans: tls; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2; Addr: *:8080 Cluster: inbound|8080||
0.0.0.0 15006 Trans: raw_buffer; Addr: *:8080                                                                 Cluster: inbound|8080||

docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json
...
                                {
                                    "name": "envoy.filters.http.tap",
                                    "typedConfig": {
                                        "@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap",
                                        "commonConfig": {
                                            "adminConfig": {
                                                "configId": "tap_config"
                                            }
                                        }
                                    }
                                },
                                {
                                    "name": "envoy.filters.http.router",
                                    "typedConfig": {
                                        "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
                                    }
                                }
...

✅ tap 기능이 동작하는지 확인해보자. 2개의 터미널 창이 필요한다. 창 하나에서는 curl 로 tap 설정을 전달해 webapp 워크로드에서 tap을 시작하자.

# 터미널 1 : 포트 포워딩 설정 후 tap 시작
kubectl port-forward -n istioinaction deploy/webapp 15000 &
curl -X POST -d @./ch14/tap-config.json localhost:15000/tap

# 터미널 2 : 기존 반복 접속하는 것 활용
EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
while true; do docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done

# 터미널 3 : 로그 확인
docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level http:debug
docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level tap:debug
kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
2025-05-24T08:08:04.975535Z     debug   envoy http external/envoy/source/common/http/conn_manager_impl.cc:1049  [C895][S12870699181831768119] request headers complete (end_stream=true):
':authority', 'webapp.istioinaction.io'
':path', '/api/catalog'
':method', 'GET'
'user-agent', 'curl/8.7.1'
'accept', '*/*'
'x-app-tap', 'true'
...
2025-05-24T08:08:47.713852Z	debug	envoy tap external/envoy/source/extensions/filters/http/tap/tap_config_impl.cc:172	submitting buffered trace sink	thread=31
2025-05-24T08:08:47.713860Z	debug	envoy tap external/envoy/source/extensions/common/tap/admin.cc:145	admin submitting buffered trace to main thread	thread=31
...
  • tap 을 시작한 창에 tap 출력이 표시돼야 한다. 이 출력은 헤더, 바디, 트레일러 등 요청에 대한 모드 정보를 준다.
  • 엔보이 tap 필터와 이 필터를 이스티오에서 사용해 네트워크 전반의 요청을 디버깅하는 방법을 계속해서 알아보자.

2.3 외부 호출 요청속도 제한

🎯 외부 호출 기반의 속도 제한 기능으로 Istio 데이터 플레인 확장

  • 이전에는 내장 HTTP 필터만으로 확장했다면,
    이번에는 외부 호출(call-out) 기능을 활용한 필터를 사용함.
  • 이 필터는 외부 서비스(속도 제한 서버)를 호출하여
    요청을 차단할지 계속할지 결정하는 기능을 수행.

📈 적용 예: 글로벌 속도 제한(Global Rate Limiting)

  • 특정 워크로드의 모든 Envoy 프록시
    공통된 속도 제한 서버를 호출하여
    글로벌한 요청 제한을 적용.

✅ 특징

  • 서비스 복제본 수와 무관하게 제한 적용 가능
  • 요청 시 속성(예: 경로, 메서드 등)을 속도 제한 서버로 전달 → 처리 결정
  • 속도 제한 서버는 Redis(또는 Memcached)를 백엔드로 사용해 카운터 저장

⚙️ 구성 요소
1. Envoy HTTP 필터 (Global Rate Limit Filter)

  1. 속도 제한 서버

    • Envoy 커뮤니티 제공: GitHub Repo
    • Redis 등과 연동해 요청 수 추적 및 제한
  2. Istio EnvoyFilter 리소스

    • Envoy에 필터 삽입하여 글로벌 속도 제한 동작 구성

🛑 주의점

  • 속도 제한 서버를 배포하기 전,
    원하는 정책 및 동작을 정의한 설정 필요.
  • Istio는 기본적으로 이 기능을 노출하지 않으므로,
    EnvoyFilter로 직접 구성해야 함.

요약하면, 이 절은 속도 제한 기능을 외부 서버와 연동해 Istio 데이터 플레인에 통합하는 방법을 설명하며, 이를 위해 EnvoyFilter, 속도 제한 서버, Redis 등을 활용합니다.

2.3.1 엔보이 속도 제한 이해하기

  • 엔보이 속도 제한 서버 RLS 를 구성하기 전에 속도 제한이 어떻게 작동하는지 이해해야 한다.
  • 특히 엔보이의 HTTP 전역 속도 제한을 살펴볼 것인데, 이 속도 제한은 HTTP 필터로 존재하고 HCM에서 HTTP 필터 체인으로 설정해야 한다.
  • 속도 제한 필터는 HTTP 요청을 처리할 때 요청에서 특정 속성을 가져오고 RLS로 보내 평가를 받는다.
  • 엔보이 속도 제한에서 이런 속성들 혹은 속성 그룹들을 가리켜 디스크립터 descripter 라고 한다.
  • 요청이 디스크립터 또는 속성은 원격 주소일 수도, 요청 헤더일 수도, 목적지일 수도, 혹은 요청의 어떤 일반 속성 같은 것일 수도 있다.
  • 속성 제한 서버 RLS는 그림 14.8처럼 전송된 요청의 속성을 미리 정의한 속성 집합과 비교해 일치하는 속성의 카운터를 늘린다.
  • 카운트할 속성은 트리 형태로 그룹화하거나 정의해 결정할 수 있다.
  • 속성 또는 속성 집합이 속도 제한 서버 정의와 일치하면 해당 제한의 횟수를 늘린다.
  • 횟수가 임계값을 초과하면 해당 요청에는 속도 제한이 적용된다.

2.3.2 엔보이 속도 제한 서버 설정하기

  • 속성 카운터와 한도가 포함된 속도 제한 서버 설정을 만들어보자

  • 우리 예제에서는 예제 조직에서 보유한 로열티 등급에 따라 특정 사용자 집단을 제한하고자 한다.

  • 요청의 로열티 등급은 x-loyalty 헤더를 검사해 판단할 수 있다.

  • 골드 등급(x-loyalty: gold)의 사용자 그룹에는 요청을 분당 10개까지 허용하고, 실버 등급(x-loyalty: silver)에는 요청을 분당 5개까지 허용한다. 또한 브론즈 등급(x-loyalty: bronze)에는 요청을 분당 3개까지 허용한다.

  • 식별할 수 없는 로열티 등급의 경우, 분당 요청이 하나를 넘어가면 속도 제한이 처리를 제한한다.

  • 요청이 디스크립터를 포착하는 속도 제한 서버 설정은 다음과 같이 표현할 수 있다.

# cat ch14/rate-limit/rlsconfig.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: catalog-ratelimit-config
  namespace: istioinaction
data:
  config.yaml: |
    domain: catalog-ratelimit
    descriptors:
      - key: header_match
        value: no_loyalty
        rate_limit:
          unit: MINUTE
          requests_per_unit: 1
      - key: header_match
        value: gold_request
        rate_limit:
          unit: MINUTE
          requests_per_unit: 10
      - key: header_match
        value: silver_request
        rate_limit:
          unit: MINUTE
          requests_per_unit: 5
      - key: header_match
        value: bronze_request
        rate_limit:
          unit: MINUTE
          requests_per_unit: 3
  • 실제 요청 헤더를 직접 다루지 않고, 요청의 일부로 전송된 속성만 다루고 있음을 유의하자. 다음 절에서는 이 속성들을 정의하는 방법을 살펴볼 것이다.
  • 앞서 언급했듯이, 속도 제한 서버 설정은 속도를 제한하기 위해 따라야 하는 규칙을 정의한다.
  • 이스티오 데이터 플레인을 거쳐 요청이 처리될 때 속성들이 속도 제한 서버로 보내진다. 속성이 규칙 조건에 일치하면 그에 따라 처리를 제한한다.

2.3.3 요청 경로에 속도 제한 걸기

  • 속도 제한 서버 설정을 만들고 나면, 특정 요청에 대해 어떤 속성을 전송할 것인지 엔보이를 설정해야 한다.
  • 엔보이 용어로는 이 설정을 특정 요청 경로에 취하는 속도 제한 조치 rate-limit action 라고 부른다.
  • 예를 들어, catalog 서비스를 /items 경로로 호출하면 요청에 x-loyalty 헤더가 있는지와 어느 그룹에 속하는지를 포착하려고 한다.
  • 적절한 속성 action 을 속도 제한 서버로 보내도록 설정하려면 특정 엔보이 루트 설정에 rate_limit 설정을 지정해야 한다.
  • 이스티오에는 아직 전용 API가 없으므로(이 책을 저술하는 시점에는) EnvoyFilter 리소스를 사용해야 한다.
  • catalog 서비스의 모든 경로에 속도 제한 조치를 지정하는 방법은 다음과 같다.
# cat ch14/rate-limit/catalog-ratelimit-actions.yaml 
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: catalog-ratelimit-actions
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: catalog
  configPatches:
    - applyTo: VIRTUAL_HOST
      match:
        context: SIDECAR_INBOUND
        routeConfiguration:
          vhost:
            route:
              action: ANY
      patch:
        operation: MERGE
        # Applies the rate limit rules.
        value:
          rate_limits: # 속도 제한 조치
            - actions:
              - header_value_match:
                  descriptor_value: no_loyalty
                  expect_match: false
                  headers:
                  - name: "x-loyalty"
            - actions:
              - header_value_match:
                  descriptor_value: bronze_request
                  headers:
                  - name: "x-loyalty"
                    exact_match: bronze
            - actions:
              - header_value_match:
                  descriptor_value: silver_request
                  headers:
                  - name: "x-loyalty"
                    exact_match: silver
            - actions:
              - header_value_match:
                  descriptor_value: gold_request
                  headers:
                  - name: "x-loyalty"
                    exact_match: gold
  • 이제 이 규칙들을 속도 제한 서버와 함께 배포하고 데이터 플레인을 설정하는 방법을 살펴보자.
  • k8s configmap 으로 규칙을 배포하고, 속도 제한 서버를 레디스 백엔드와 함께 배포하자.
#
tree ch14/rate-limit
ch14/rate-limit
├── catalog-ratelimit-actions.yaml
├── catalog-ratelimit.yaml
├── rls.yaml
└── rlsconfig.yaml

cat ch14/rate-limit/rlsconfig.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: catalog-ratelimit-config
  namespace: istioinaction
data:
  config.yaml: |
    domain: catalog-ratelimit
    descriptors:
      - key: header_match
        value: no_loyalty
        rate_limit:
          unit: MINUTE
          requests_per_unit: 1
      - key: header_match
        value: gold_request
        rate_limit:
          unit: MINUTE
          requests_per_unit: 10
      - key: header_match
        value: silver_request
        rate_limit:
          unit: MINUTE
          requests_per_unit: 5
      - key: header_match
        value: bronze_request
        rate_limit:
          unit: MINUTE
          requests_per_unit: 3
          
cat ch14/rate-limit/rls.yaml
# Copyright Istio Authors
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: istioinaction
  labels:
    app: redis
spec:
  ports:
  - name: redis
    port: 6379
  selector:
    app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  namespace: istioinaction
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"    
      labels:
        app: redis
    spec:
      containers:
      - image: redis:alpine
        imagePullPolicy: IfNotPresent
        name: redis
        ports:
        - name: redis
          containerPort: 6379
      restartPolicy: Always
      serviceAccountName: ""
---
apiVersion: v1
kind: Service
metadata:
  name: ratelimit
  namespace: istioinaction
  labels:
    app: ratelimit
spec:
  ports:
  - name: http-port
    port: 8080
    targetPort: 8080
    protocol: TCP
  - name: grpc-port
    port: 8081
    targetPort: 8081
    protocol: TCP
  - name: http-debug
    port: 6070
    targetPort: 6070
    protocol: TCP
  selector:
    app: ratelimit
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ratelimit
  namespace: istioinaction
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ratelimit
  strategy:
    type: Recreate
  template:
    metadata:
      annotations:
        sidecar.istio.io/inject: "false"    
      labels:
        app: ratelimit
    spec:
      containers:
      - image: envoyproxy/ratelimit:6f5de117
        imagePullPolicy: IfNotPresent
        name: ratelimit
        command: ["/bin/ratelimit"]
        env:
        - name: LOG_LEVEL
          value: debug
        - name: REDIS_SOCKET_TYPE
          value: tcp
        - name: REDIS_URL
          value: redis:6379
        - name: USE_STATSD
          value: "false"
        - name: RUNTIME_ROOT
          value: /data
        - name: RUNTIME_SUBDIRECTORY
          value: ratelimit
        - name: RUNTIME_WATCH_ROOT
          value: "false"
        ports:
        - containerPort: 8080
        - containerPort: 8081
        - containerPort: 6070
        volumeMounts:
        - name: config-volume
          mountPath: /data/ratelimit/config/config.yaml
          subPath: config.yaml
      volumes:
      - name: config-volume
        configMap:
          name: catalog-ratelimit-config

#
kubectl apply -f ch14/rate-limit/rlsconfig.yaml -n istioinaction
configmap/catalog-ratelimit-config created
kubectl apply -f ch14/rate-limit/rls.yaml -n istioinaction
service/redis created
deployment.apps/redis created
service/ratelimit created
deployment.apps/ratelimit created

# 확인
kubectl get cm -n istioinaction catalog-ratelimit-config
NAME                       DATA   AGE
catalog-ratelimit-config   1      34s

kubectl get pod -n istioinaction                                
NAME                       READY   STATUS    RESTARTS      AGE
catalog-6cf4b97d-8w4k9     2/2     Running   0             4h34m
ratelimit-99d5d9c5-prxsw   1/1     Running   2 (23s ago)   30s
redis-6cf4ff9768-xzsq8     1/1     Running   0             30s
sleep-6f8cfb8c8f-xtx4s     2/2     Running   0             4h34m
webapp-7685bcb84-bl7xq     2/2     Running   0             4h34m
  • 엔보이가 속성을 속도 제한 서버로 보내게 해야 한다. 그래야 속도 제한 서버가 개수를 세어 속도 제한을 처리할 수 있기 때문이다.
  • 이를 위해 그 역할을 하는 EnvoyFilter 리소스를 적용해보자.
# 기존에 반복 호출은 취소해두자

# 
cat ch14/rate-limit/catalog-ratelimit.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: catalog-ratelimit-filter
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: catalog
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        portNumber: 3000
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.ratelimit
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit

          domain: catalog-ratelimit
          failure_mode_deny: true
          rate_limit_service:
            grpc_service:
              envoy_grpc:
                cluster_name: outbound|8081||ratelimit.istioinaction.svc.cluster.local
              timeout: 10s
            transport_api_version: V3
            
cat ch14/rate-limit/catalog-ratelimit-actions.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: catalog-ratelimit-actions
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: catalog
  configPatches:
    - applyTo: VIRTUAL_HOST
      match:
        context: SIDECAR_INBOUND
        routeConfiguration:
          vhost:
            route:
              action: ANY
      patch:
        operation: MERGE
        # Applies the rate limit rules.
        value:
          rate_limits:
            - actions:
              - header_value_match:
                  descriptor_value: no_loyalty
                  expect_match: false
                  headers:
                  - name: "x-loyalty"
            - actions:
              - header_value_match:
                  descriptor_value: bronze_request
                  headers:
                  - name: "x-loyalty"
                    exact_match: bronze
            - actions:
              - header_value_match:
                  descriptor_value: silver_request
                  headers:
                  - name: "x-loyalty"
                    exact_match: silver
            - actions:
              - header_value_match:
                  descriptor_value: gold_request
                  headers:
                  - name: "x-loyalty"
                    exact_match: gold
                    
kubectl apply -f ch14/rate-limit/catalog-ratelimit.yaml -n istioinaction
envoyfilter.networking.istio.io/catalog-ratelimit-filter created

kubectl apply -f ch14/rate-limit/catalog-ratelimit-actions.yaml -n istioinaction
envoyfilter.networking.istio.io/catalog-ratelimit-actions created

kubectl get envoyfilter -A
NAMESPACE       NAME                        AGE
istio-system    stats-filter-1.13           4h44m
istio-system    stats-filter-1.14           4h44m
istio-system    stats-filter-1.15           4h44m
istio-system    stats-filter-1.16           4h44m
istio-system    stats-filter-1.17           4h44m
istio-system    tcp-stats-filter-1.13       4h44m
istio-system    tcp-stats-filter-1.14       4h44m
istio-system    tcp-stats-filter-1.15       4h44m
istio-system    tcp-stats-filter-1.16       4h44m
istio-system    tcp-stats-filter-1.17       4h44m
istioinaction   catalog-ratelimit-actions   16s
istioinaction   catalog-ratelimit-filter    29s
istioinaction   tap-filter                  4h24m
  • 속도 제한 기능을 시험해보기 위해 sleep 앱을 배포하고 catalog 서비스를 호출하는 클라이언트를 시뮬레이션해보자.
# sleep 앱에서 catalog 서비스를 호출 시도 : 대략 1분에 한 번 정도 호출 성공! >> x-loyalty 헤더가 없을 때 속도 제한 값!
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v
* Host catalog:80 was resolved.
* IPv6: (none)
* IPv4: 10.200.3.112
*   Trying 10.200.3.112:80...
* Connected to catalog (10.200.3.112) port 80
> GET /items HTTP/1.1
> Host: catalog
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 500 Internal Server Error
< date: Sat, 24 May 2025 12:28:00 GMT
< server: envoy
< content-length: 0
< x-envoy-upstream-service-time: 30
< 
* Connection #0 to host catalog left intact

kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v
...
< HTTP/1.1 429 Too Many Requests
< x-envoy-ratelimited: true
...

# silver 헤더는? 
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
...

#
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4'
NAME                              DOMAINS     MATCH     VIRTUAL SERVICE
InboundPassthroughClusterIpv4     *           /*        
InboundPassthroughClusterIpv4     *           /* 

docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4' -o json | grep actions
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [

docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||'
NAME               DOMAINS     MATCH     VIRTUAL SERVICE
inbound|3000||     *           /*        
inbound|3000||     *           /*     

docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json | grep actions
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        "actions": [
                        
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json
...
                "rateLimits": [
                    {
                        "actions": [
                            {
                                "headerValueMatch": {
                                    "descriptorValue": "no_loyalty",
                                    "expectMatch": false,
                                    "headers": [
                                        {
                                            "name": "x-loyalty"
                                        }
                                    ]
                                }
                            }
                        ]
                    },
                    {
                        "actions": [
                            {
                                "headerValueMatch": {
                                    "descriptorValue": "bronze_request",
                                    "headers": [
                                        {
                                            "name": "x-loyalty",
                                            "exactMatch": "bronze"
...

  • 다음 실습을 위해 리소스 삭제
#
kubectl delete envoyfilter -n istioinaction --all
kubectl get envoyfilter -A

#
kubectl delete -f ch14/rate-limit/rlsconfig.yaml -n istioinaction
kubectl delete -f ch14/rate-limit/rls.yaml -n istioinaction

2.4 루아로 이스티오의 데이터 플레인 확장하기

🎯 기본 Envoy 필터에 없는 기능을 커스텀 로직으로 구현

  • 기존의 기본 필터로는 부족할 때, 요청 경로에 원하는 로직을 직접 구현해야 할 수 있음.
  • 이를 위해 Envoy의 Lua 필터를 사용하면, Lua 스크립트로 요청/응답 경로의 동작을 커스터마이징할 수 있음.

🔧 Lua 필터 개요

  • Lua는 경량 스크립트 언어이며, Envoy는 LuaJIT 기반으로 실행.
  • Envoy 필터 체인에 Lua 필터를 추가하고,
    Lua 스크립트를 삽입하여 프록시 동작을 확장.

📌 Lua로 할 수 있는 일

  • 요청/응답 헤더 조작
  • 요청/응답 바디 검사 및 처리

⚠️ 주의사항

  • 요청 바디를 다루는 경우,
    바디 전체를 메모리에 적재할 수 있어 성능에 영향 가능.
  • 복잡한 작업을 할수록 프록시 처리 방식에 영향을 줄 수 있음.
  • 공식 문서 참고: Envoy Lua 필터 Docs

설정 방법

  • 여전히 EnvoyFilter 리소스를 사용하여
    Lua 스크립트를 Envoy에 주입함으로써 커스텀 로직을 적용.

2.4.1 실습 : 요청 경로의 동작을 커스텀

  • 요청 경로의 동작을 커스텀하는 일반적인 예를 들어본다. 들어오는 요청 모두를 A/B 테스트 그룹의 일부로 처리하고 싶다고 해보자.
  • 어떤 그룹인지는 요청의 속성을 기반으로 런타임에서만 판단할 수 있다.
  • 특정 요청이 어느 그룹에 속했는지 판단하려면 A/B 테스트 엔진을 호출해야 한다.
  • 이 호출의 응답은 요청의 헤더로 추가해야 하며, 업스트림 서비스는 이 헤더를 사용해 A/B 테스트 목적에 맞는 라우팅 결정을 내릴 수 있다.
  • 이 예제를 위해 몇 가지 보조 서비스를 배포해보자. 받은 요청의 헤더를 되돌려 보내는 샘플 httpbin 서비스를 배포할 것이다.
  • 또한 샘플 A/B 테스트 버킷 서비스도 배포해보겠다. 이 서비스는 요청의 헤더를 평가해 요청이 속해야 하는 그룹을 나타내는 문자열을 반환한다.
#
cat ch14/bucket-tester-service.yaml
...
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: bucket-tester
    version: v1
  name: bucket-tester
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bucket-tester
      version: v1
  template:
    metadata:
      labels:
        app: bucket-tester
        version: v1
    spec:
      containers:
      - image: hashicorp/http-echo:1.0 # 수정 https://hub.docker.com/r/hashicorp/http-echo/tags
        imagePullPolicy: IfNotPresent
        name: bucket-tester
        args:
        - "-text=dark-launch-7"        
        ports:
        - containerPort: 5678
          name: http
          protocol: TCP
        securityContext:
          privileged: false

#
kubectl apply -f ch14/httpbin.yaml -n istioinaction
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created

kubectl apply -f ch14/bucket-tester-service.yaml -n istioinaction
service/bucket-tester created
deployment.apps/bucket-tester created

# 확인
kubectl get pod -n istioinaction
NAME                             READY   STATUS    RESTARTS      AGE
bucket-tester-688c598b47-86fbr   2/2     Running   0             25s
httpbin-85d76b4bb6-dz6b5         2/2     Running   0             2m56s
...
  • 요청이나 응답 헤더를 조작하는 용도로 작성할 수 있는 루아 스크립트를 살펴보고, 이 사용 사례를 구현하는 방법도 살펴보자.
  • 엔보이에서는 루아 함수 envoy_on_reqest() 혹은 envoy_on_response() 를 구현해 요청응답 각각을 확인하고 조작할 수 있다.
  • 루아 내에서 다른 서비스를 호출해야 한다면 엔보이가 제공하는 함수를 사용해야 한다 (RPC 호출을 하는데 범용 루아 라이브러리를 사용하면 안된다. 우리는 엔보이가 자체 논블로킹 non-blocking 스레딩 아키텍처로 호출을 올바르게 관리하길 원하기 때문이다).
  • httpCall() 함수를 사용하면 외부 서비스와 통신할 수 있다. 다음 스크립트는 우리의 사용 사례를 구현한 것이다.
# cat ch14/lua-filter.yaml
...
            function envoy_on_request(request_handle)
              local headers, test_bucket = request_handle:httpCall(
              "bucket_tester",
              {
                [":method"] = "GET",
                [":path"] = "/",
                [":scheme"] = "http",
                [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
                ["accept"] = "*/*"
              }, "", 5000) 

              request_handle:headers():add("x-test-cohort", test_bucket)               
            end          
            function envoy_on_response(response_handle)
              response_handle:headers():add("istioinaction", "it works!")
            end
...
  • envoy_on_reqest() 함수를 구현하고, httpCall() 내장 함수를 사용해 외부 서비스와 통신하고 있다.
  • 또한 응답을 받아 x-test-cohort 라는 헤더를 추가하고 있다. httpCall() 포함한 내장 함수는 문서 참조 - Docs
  • 앞 절에서 했던 것처럼 이 스크립트를 EnvoyFilter 리소스에 추가할 수 있다.
# cat ch14/lua-filter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: httpbin-lua-extension
  namespace: istioinaction
spec:
  workloadSelector:
    labels:
      app: httpbin
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
      listener:
        portNumber: 80
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
            subFilter:
              name: "envoy.filters.http.router"
    patch:
      operation: INSERT_BEFORE
      value: 
       name: envoy.lua
       typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
          inlineCode: |
            function envoy_on_request(request_handle) # 아래 줄에 코드 입력
              local headers, test_bucket = request_handle:httpCall(
              "bucket_tester",
              {
                [":method"] = "GET",
                [":path"] = "/",
                [":scheme"] = "http",
                [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
                ["accept"] = "*/*"
              }, "", 5000) 

              request_handle:headers():add("x-test-cohort", test_bucket)               
            end          
            function envoy_on_response(response_handle) # 아래 줄에 코드 입력
              response_handle:headers():add("istioinaction", "it works!")
            end
  - applyTo: CLUSTER
    match:
      context: SIDECAR_OUTBOUND
    patch:
      operation: ADD
      value: # cluster specification
        name: bucket_tester
        type: STRICT_DNS
        connect_timeout: 0.5s
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: bucket_tester
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    protocol: TCP
                    address: bucket-tester.istioinaction.svc.cluster.local
                    port_value: 80
  • workloadSelector 정의한 대로 이 필터를 httpbin 워크로드에 적용 후 httpbin 서비스 호출 확인! ⇒ 실습은 실패!
#
kubectl apply -f ch14/lua-filter.yaml
kubectl get envoyfilter -n istioinaction

# istio-proxy config 확인 내용 추가해두자


# httpbin 서비스 호출 확인! 
kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/ -v
...
< HTTP/1.1 503 Service Unavailable
< content-length: 39
< content-type: text/plain
< istioinaction: it works!
< date: Sun, 18 May 2025 07:59:34 GMT
< server: envoy
< x-envoy-upstream-service-time: 51
...
invalid header value for: x-test-cohort

kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/headers
...
invalid header value for: x-test-cohort


# 정상 실습 시..
{
    "headers": {
        "Accept": "*/*",
        "Content-Length": "0",
        "Host": "httpbin.istioinaction:8000",
        "User-Agent": "curl/7.69.1",
        "X-B3-Sampled": "1",
        "X-B3-Spanid": "1d066f4b17ee147b",
        "X-B3-Traceid": "1ec27110e4141e131d066f4b17ee147b",
        "X-Test-Cohort": "dark-launch-7" # A/B 테스트 서비스를 호출할 때 덧붙이는 새 헤더 x-test-cohort 가 보임
    }
}
  • 자세한 내용은 이 책 소스 코드에 있는 ch14/lua-filter.yaml 파일에서 살펴볼 수 있다.
  • 이 예제에서는 데이터 플레인의 기능을 확장하기 위해 의도적으로 구축된 필터를 사용하는 방법을 알아봤다.
  • 루아 스크립트 언어를 사용해 이 기능을 구현했으며, 내장 함수를 사용해 다른 함수를 호출했다.
  • 다음 절에서는 웹어셈블리를 사용해 다른 언어로 우리의 커스텀 기능을 구현하는 방법을 다룬다.
  • 다음 실습을 위해 리소스 삭제
#
kubectl delete envoyfilter -n istioinaction --all
kubectl get envoyfilter -A

#
kubectl delete -f ch14/httpbin.yaml -n istioinaction
kubectl delete -f ch14/bucket-tester-service.yaml -n istioinaction
profile
I'm SJ

0개의 댓글