이스티오 확장을 통한 맞춤형 네트워킹 구현 기본 기능으로 부족한 조직별 요구사항 충족을 위해 Envoy 프록시의 설정을 직접 수정하거나 확장한다. EnvoyFilter 리소스를 활용해 엔보이의 동작을 세부 제어하며, Lua 스크립트나 WebAssembly(Wasm) 모듈을 적용해 요청/응답 경로를 커스터마이징할 수 있다.
주요 확장 사례
외부 인가 서비스 연동, HMAC 서명 검증 등 비표준 프로토콜 처리
헤더 조작, 페이로드 보강을 위한 추가 서비스 호출
조직별 보안 토큰 처리 로직 구현
이를 통해 Istio의 코어 변경 없이 특정 사용 사례에 최적화된 네트워킹 레이어를 구축할 수 있다.
실습환경 구성
#
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/gasida/Downloads/istio-in-action/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 설치 시 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
# 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
#
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
kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
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
# 확인 후 삭제
kubectl delete deploy,svc --all
# 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
이스티오 고급 설정을 위한 EnvoyFilter 활용
기존 API로 구현 불가한 시나리오에서 EnvoyFilter를 사용해 엔보이 프록시의 리스너·라우트·클러스터·필터를 직접 설정한다. 이 리소스는 이스티오의 고수준 추상화를 벗어나 저수준 엔보이 기능을 제어할 수 있지만, 버전 간 호환성 보장 없어 신중한 검증이 필수다. 잘못된 구성은 데이터 플레인 전체 장애로 이어질 수 있으므로, 반드시 테스트 환경에서 검증 후 배포해야 한다. 비상 시에만 사용하는 것이 권장되며, 일반적인 경우에는 VirtualService·DestinationRule 등 표준 API를 우선 적용한다.
Tap 필터를 적용해 webapp 서비스의 HTTP 트래픽을 샘플링·스트리밍하며, workloadSelector로 특정 워크로드에만 적용한다. VirtualService/DestinationRule 이후 적용되며, 엔보이 저수준 설정 변경으로 인한 메시 장애 리스크 존재해 신중한 구성이 필수다.
workloadSelector로 istioinaction 네임스페이스의 특정 워크로드에 적용 대상을 지정한다.
SIDECAR_INBOUND 리스너의 8080 포트 HTTP 필터 체인에서 envoy.filters.http.router 필터 앞에 tap 필터를 추가한다.
tap 필터는 envoy.filters.http.tap 타입으로 설정되며, streaming_admin 출력을 통해 실시간 트래픽 디버깅이 가능하다.
# 터미널 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-18T05:59:47.704918Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:329 [C2100] new stream thread=31
2025-05-18T05:59:47.705028Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1049 [C2100][S11845543740779751481] 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-18T05:59: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-18T05:59:47.713860Z debug envoy tap external/envoy/source/extensions/common/tap/admin.cc:145 admin submitting buffered trace to main thread thread=31
...
14.3 외부 호출로 요청 속도 제한하기
엔보이 글로벌 속도 제한은 모든 서비스 인스턴스가 동일한 외부 속도 제한 서버(예: Redis 백엔드)를 호출해 요청 허용 여부를 결정하며, 서비스 복제본 수와 무관하게 일관된 트래픽 제한을 적용할 수 있다.
속도 제한 서버 설정 x-loyalty 헤더 값에 따라 골드(10/분), 실버(5/분), 브론즈(3/분) 등급별 요청 제한을 적용하고, 미확인 등급은 1/분으로 제한한다. Redis 백엔드를 사용해 글로벌 카운터를 관리하며, Envoy의 RateLimitService 설정을 통해 외부 서버와 통합한다.
요청 경로에 속도 제한 걸기* CONFIGURING THE REQUEST PATH FOR RATE LIMITING
EnvoyFilter를 통해 /items 경로 요청의 x-loyalty 헤더를 포착하는 rate-limit action을 정의해, 특정 로열티 등급별로 속도 제한 서버에 전달할 descriptor 생성을 구성한다. VirtualService 라우트에 rate_limits 섹션 추가 없이, EnvoyFilter의 header_value_match 액션으로 헤더 기반 속성 추출 및 Redis 기반 글로벌 제한 정책을 적용한다.
Lua를 활용한 이스티오 데이터 플레인 확장 Lua 스크립트를 엔보이 필터 체인에 주입해 요청/응답 경로의 헤더 조작·바디 검사 등 커스텀 로직 구현이 가능하다. 예를 들어 A/B 테스트 그룹 분류를 위해 외부 서비스 호출 후 결과를 헤더에 추가하는 동적 라우팅을 구성할 수 있다. 단, 바디 전체 메모리 적재 등 무거운 연산은 성능 저하를 유발할 수 있으니 주의가 필요하다.
| 일전에 실 예로 내가 운영하던 서비스중 프론트/백 사이에서 데이터가 안들어온다는 개발자 확인 요청으로 Lua 활용해서 바디 전체를 로깅하는 루아 스크립트 작성했다가 뻗었던 기억이 있다.
#
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 가 보임
}
}
14.5 웹어셈블리로 이스티오의 데이터 플레인 확장하기
**웹어셈블리(Wasm)**를 통해 Envoy 필터를 동적 로드해 Istio 데이터 플레인을 확장하며, WasmPlugin CRD로 OCI 레지스트리에서 모듈을 배포해 커스텀 인증·트래픽 제어 기능을 구현할 수 있다. **웹어셈블리(Wasm)**는 이식성·고성능·안전성을 갖춘 바이너리 포맷으로, 다양한 언어로 작성된 커스텀 로직을 엔보이 필터에 동적 로드해 이스티오 데이터 플레인을 확장한다.
웹어셈블리(Wasm) 사용 이유 C++ 종속성·정적 빌드 문제 해결: 런타임 동적 로드로 커스텀 필터를 즉시 적용하며, 다양한 언어 지원으로 유연한 확장 가능. 안전한 샌드박스 환경에서 실행되어 메모리/CPU 제어·프록시 장애 격리되며, OCI 레지스트리 통합으로 배포 관리가 간소화된다.
**WebAssembly(Wasm)**로 Envoy 필터를 생성하려면 wasme 도구를 사용해 어셈블리스크립트·러스트 등 지원 언어로 코드 작성 후 빌드하며, 실험적 기능이므로 운영 전 철저한 테스트가 필요하다.
Distributing WebAssembly Modules 실습
Istio의 WasmPlugin을 사용해 OCI 레지스트리에서 원격 Wasm 모듈을 동적으로 로드하며, HTTP 기본 인증 확장 등을 구성할 수 있다. WasmPlugin 리소스로 필터 단계(phase)와 설정을 정의하면 Istio 에이전트가 자동으로 모듈을 다운로드해 Envoy에 주입한다.
# 설정 전 호출 확인
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 -v
#
kubectl explain wasmplugins.extensions.istio.io
...
#
kubectl apply -f - <<EOF
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: basic-auth
namespace: istio-system # 모든 네임스페이스에 영향
spec:
selector:
matchLabels:
istio: ingressgateway
url: oci://ghcr.io/istio-ecosystem/wasm-extensions/basic_auth:1.12.0
phase: AUTHN
pluginConfig:
basic_auth_rules:
- prefix: "/api"
request_methods:
- "GET"
- "POST"
credentials:
- "ok:test"
- "YWRtaW4zOmFkbWluMw=="
EOF
#
kubectl get WasmPlugin -A
NAMESPACE NAME AGE
istio-system basic-auth 21s
#구성된 Wasm 모듈을 확인
# 자격 증명 없이 테스트
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 -v
# 자격 증명으로 테스트
docker exec -it mypc curl -s -H "Authorization: Basic YWRtaW4zOmFkbWluMw==" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v
다중 클러스터 서비스 메시는 장애 격리 및 고가용성을 통해 클러스터 장애 시 트래픽을 다른 클러스터로 자동 전환하며, 다중 클라우드 지원 및 지연 시간 최소화를 위해 다양한 환경에서 워크로드를 실행하고 지리적 근접성을 기반으로 라우팅할 수 있다.
다중 클러스터 서비스 메시는 클러스터 간 워크로드 디스커버리·연결성·공통 신뢰를 통해 트래픽 제어·보안·관측 기능을 유지하며, Istio는 쿠버네티스 API 연동으로 서비스 프록시를 자동 구성한다. 보안 리스크 시 **메시 연합(Mesh Federation)**을 적용해 클러스터 API 접근 권한 분리와 독립적인 메시 운영이 가능하다.
이스티오 컨트롤 플레인은 쿠버네티스 API 서버에 접근해 서비스와 엔드포인트 정보를 수집한다. API 접근은 서비스 어카운트와 토큰, RBAC 권한으로 제어한다. istiod에 원격 클러스터의 서비스 어카운트 토큰
을 제공해 인증 후 워크로드를 디스커버리한다.
클러스터가 플랫 네트워크라면 워크로드가 IP로 바로 연결된다. 서로 다른 네트워크라면 이스티오 인그레스 게이트웨이(동서 게이트웨이)가 클러스터 간 트래픽을 프록시한다. 이 구조는 중복 IP, 네트워크 경계, 장애 허용, 네트워크 분리 등 다양한 요구를 충족한다.
다중 클러스터 메시에서는 클러스터 간 공통 신뢰가 필요하다. 공통 루트 CA에서 발급한 플러그인 중간 CA 인증서나 외부 CA 통합 방식이 있다. 플러그인 CA 방식은 간단하지만 노출 위험이 있고, 외부 CA 통합은 더 안전하지만 복잡하다.
12.3 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시 개요
인프라 구성 환경
실세계 엔터프라이즈 서비스를 모방하는 인프라를 준비해보자. 이 서비스는 여러 클러스터에서 실행되고, 여러 리전에 걸쳐 배포되며, 서로 다른 네트워크에 위치한다.
west-cluster : 사설 네트워크가 us-west 리전에 있는 쿠버네티스 클러스터. 여기서 webapp 서비스를 실행.
east-cluster : 사설 네트워크가 us-east 리전에 있는 쿠버네티스 클러스터. 여기서 catalog 서비스를 실행할 것이다.
#실습환경 - west 배포
#
kind create cluster --name west --image kindest/node:v1.23.17 --kubeconfig ./west-kubeconfig --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000 # istio-ingrssgateway HTTP
hostPort: 30000
- containerPort: 30001 # Prometheus
hostPort: 30001
- containerPort: 30002 # Grafana
hostPort: 30002
- containerPort: 30003 # Kiali
hostPort: 30003
- containerPort: 30004 # Tracing
hostPort: 30004
- containerPort: 30005 # kube-ops-view
hostPort: 30005
networking:
podSubnet: 10.10.0.0/16
serviceSubnet: 10.100.0.0/24
EOF
# 설치 확인
docker ps
cat west-kubeconfig
kubectl get node --kubeconfig=./west-kubeconfig
kubectl get pod -A --kubeconfig=./west-kubeconfig
# 노드에 기본 툴 설치
docker exec -it west-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
# (옵션) kube-ops-view
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30005 --set env.TZ="Asia/Seoul" --namespace kube-system --kubeconfig=./west-kubeconfig
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view --kubeconfig=./west-kubeconfig
## kube-ops-view 접속 URL 확인
open "http://localhost:30005/#scale=1.5"
open "http://localhost:30005/#scale=1.3"
# 실습환경 - east 배포
#
kind create cluster --name east --image kindest/node:v1.23.17 --kubeconfig ./east-kubeconfig --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 31000 # istio-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 설치 시 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
/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
#metal LB 배포
# MetalLB 배포
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml \
--kubeconfig=./west-kubeconfig
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
kubectl get crd --kubeconfig=./east-kubeconfig
kubectl get pod -n metallb-system --kubeconfig=./west-kubeconfig
kubectl get pod -n metallb-system --kubeconfig=./east-kubeconfig
# 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
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
kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./east-kubeconfig
12.3.3 플러그인 CA 인증서 설정 기본 CA 대신 사용자 정의 CA를 적용해 클러스터 간 공통 신뢰를 구축한다. cacerts 시크릿에 루트/중간 CA 인증서와 키를 포함시켜 Istio가 워크로드 인증서에 서명하도록 한다. 제공된 스크립트로 동일 루트 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 개인zl
│ └── 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
# 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
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret cacerts -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl view-secret cacerts -n istio-system --all --kubeconfig=./$i-kubeconfig; echo; done
12.3.4 각 클러스터에 (Istiod) 컨트롤 플레인 설치하기* Installing the control planes in each cluster
클러스터에 네트워크 메타데이터 추가로 이스티오가 토폴로지 인지 → 근접 워크로드 우선 라우팅 및 동서 게이트웨이 자동 설정 가능하다.
#클러스터 간 연결을 위해 네트워크에 레이블 붙이기
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
# 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
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}]}}'
kwest patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
kwest describe svc -n istio-system istio-ingressgateway
# 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 클러스터에도 비슷하게 설정해준다.
# 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
# istioctl alias 설정
#
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-9qmt9.istio-system west-cluster SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-5585445f4c-zf8g6 1.17.8
ieast proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
istio-ingressgateway-7f6f8f8d99-4mlp5.istio-system east-cluster SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-85976468f-vp4zg 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 80349990876331570640723939723244297816 2025-05-17T08:47:28Z 2025-05-16T08:45:28Z
ROOTCA CA ACTIVE true 100900981840825465297757884708490534092 2032-06-25T14:11:35Z 2022-06-28T14:11:35Z
iwest proxy-config secret deploy/istio-ingressgateway.istio-system -o json
...
# 아래는 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: Jun 28 14:11:35 2022 GMT
Not After : Jun 25 14:11:35 2032 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: 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
#
iwest proxy-config listener deploy/istio-ingressgateway.istio-system
iwest proxy-config route deploy/istio-ingressgateway.istio-system
iwest proxy-config cluster deploy/istio-ingressgateway.istio-system
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system
iwest proxy-config secret deploy/istio-ingressgateway.istio-system
iwest proxy-config bootstrap deploy/istio-ingressgateway.istio-system
iwest proxy-config ecds deploy/istio-ingressgateway.istio-system
#
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
두 클러스터 모두에 워크로드 실행하기 Running workloads on both clusters
#
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
kwest get svc,ep catalog -n istioinaction
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/catalog ClusterIP 10.100.2.43 <none> 80/TCP 2m
NAME ENDPOINTS AGE
endpoints/catalog <none> 2m
kwest get gw,vs,dr -A
NAMESPACE NAME AGE
istioinaction gateway.networking.istio.io/coolstore-gateway 16s
NAMESPACE NAME GATEWAYS HOSTS AGE
istioinaction virtualservice.networking.istio.io/webapp-virtualservice ["coolstore-gateway"] ["webapp.istioinaction.io"] 16s
#
iwest proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
istio-ingressgateway-5db74c978c-9j96n.istio-system west-cluster SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-5585445f4c-zcvp4 1.17.8
webapp-5c8b4fff64-tfs8m.istioinaction west-cluster SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-5585445f4c-zcvp4 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
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 서비스 필요성 은 FQDN의 DNS 해결 실패를 방지해 애플리케이션 트래픽이 프록시로 리다이렉션될 수 있게 한다.
#east cluast에 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
keast get svc,ep catalog -n istioinaction
keast get gw,vs,dr -A # 없음
#
ieast proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
catalog-6cf4b97d-ff7lq.istioinaction east-cluster SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-85976468f-9q8ck 1.17.8
istio-ingressgateway-7f6f8f8d99-fjn92.istio-system east-cluster SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-85976468f-9q8ck 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
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.15:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local
지금까지 실습환경 구성이 끝났다.
12.3.5 클러스터 간 워크로드 디스커버리 활성화하기 Enabling cross-cluster workload discovery
istio-reader-service-account 생성 → 원격 클러스터 인증 및 워크로드 정보 조회 가능, 토큰/인증서 공유로 보안 연결 설정.
#
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get sa -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
# east
keast describe sa -n istio-system istio-reader-service-account
...
Mountable secrets: istio-reader-service-account-token-566n2
Tokens: istio-reader-service-account-token-566n2
keast get sa -n istio-system istio-reader-service-account -o yaml
...
name: istio-reader-service-account
namespace: istio-system
resourceVersion: "16805"
uid: 40fa07c3-2e49-4003-a09b-afccdbcec7a2
secrets:
- name: istio-reader-service-account-token-566n2
keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}'
eirsa=$(keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}')
keast get secret -n istio-system $eirsa
keast get secret -n istio-system $eirsa -o json
{
"apiVersion": "v1",
"data": {
"ca.crt": "LS0t,,,==",
"namespace": "aXN0aW8tc3lzdGVt", # istio-system
"token": "ZXl...VN2Zw=="
},
...
kubectl rolesum istio-reader-service-account -n istio-system --kubeconfig=./east-kubeconfig
...
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] [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
...
keast auth can-i --list
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]
...
#
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": "oyrLHJhI-1aEvcPmbnFZEI6avASPC3fJttjoEaXt-iU"
}
Token claims
------------
{
"iss": "kubernetes/serviceaccount",
"kubernetes.io/serviceaccount/namespace": "istio-system",
"kubernetes.io/serviceaccount/secret.name": "istio-reader-service-account-token-566n2",
"kubernetes.io/serviceaccount/service-account.name": "istio-reader-service-account",
"kubernetes.io/serviceaccount/service-account.uid": "40fa07c3-2e49-4003-a09b-afccdbcec7a2",
"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-16T22:45:00.679666Z 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
kwest get secret -n istio-system istio-remote-secret-east-cluster
kwest get secret -n istio-system istio-remote-secret-east-cluster -o yaml
...
# 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
iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
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.15:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local
# west 에서 10.20.0.15(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.15",
"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
ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.15:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local
서로다른 대역(10.10.x 와 10.20.x)이 동시에 불러와지는데, west에서 east 정보가 가져와지는것을 알 수 있다. east 가 west 쿼리 할 수 있게 반대로도 설정하자.
# east 에 시크릿 생성
iwest x create-remote-secret --name="west-cluster" | keast apply -f -
secret/istio-remote-secret-west-cluster created
# istiod 로그 확인 : 시크릿이 생성되면, 바로 istiod가 이 시크릿을 가지고 새로 추가된 원격 클러스터(east)에 워크로드를 쿼리한다.
keast logs deploy/istiod -n istio-system | grep 'Adding cluster'
2025-05-17T00:09:02.438756Z info Adding cluster cluster=west-cluster secret=istio-system/istio-remote-secret-west-cluster
#
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
keast get secret -n istio-system istio-remote-secret-west-cluster
keast get secret -n istio-system istio-remote-secret-west-cluster -o yaml
...
# east 확인 : west 의 모든 CDS/EDS 정보를 east 에서도 확인 가능!
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 webapp
# east 에서 10.10.0.15(10.10.0.0/16)로 라우팅이 가능한 상태인가?
ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep webapp
10.10.0.15:8080 HEALTHY OK outbound|80||webapp.istioinaction.svc.cluster.local
이제 컨트롤 플레인이 상대 클러스터의 워크로드를 퀴리할 수 있다.
12.3.6 클러스터 간 연결 설정하기 Setting up cross-cluster connectivity
north-south 트래픽: 외부→내부 네트워크 진입 트래픽으로, 이스티오 인그레스 게이트웨이를 통해 처리된다.
east-west 트래픽: 내부 네트워크 간 통신으로, VPC 피어링 불가 시 이스티오 east-west 게이트웨이를 사용해 크로스 클라우드/온프레미스 연결을 구성한다.
클러스터 간 트래픽을 암호화된 상호 인증으로 투명하게 라우팅하며, 운영자 추가 설정 없이 정밀한 트래픽 제어가 가능하다.
SNI 클러스터와 SNI 자동 통과 기능이 게이트웨이 동작을 구현해 클러스터 경계 넘어 자동화된 라우팅을 지원한다.
클라이언트는 SNI 헤더에 목적지 클러스터 정보(서비스명·포트·버전)를 인코딩해 전송하며, 게이트웨이는 이를 파싱해 암호화된 트래픽을 정확한 워크로드로 라우팅한다.mTLS 연결 유지 상태에서 프록시가 트래픽을 복호화 없이 전달하므로 보안성을 확보하면서도 클러스터 간 세부 라우팅 제어가 가능하다. 이 구조는 운영자의 추가 설정 없이 자동화된 크로스 클러스터 통신과 상호 인증을 동시에 구현한다.
SNI 클러스터가 있는 east-west 게이트웨이 설치하기 Installing the east-west gateway with SNI clusters
east-west 게이트웨이에 SNI 클러스터를 활성화하려면 IstioOperator에서 ISTIO_META_ROUTER_MODE를 sni-dnat으로 설정하면 된다. 이 설정으로 게이트웨이가 SNI 기반 트래픽 라우팅을 자동 지원하며, 추가 리소스 없이 다중 클러스터 mTLS 통신이 가능해진다.
# 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
# 설치 전 확인
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
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
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
iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
10.20.0.15:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local
# IstioOperator 로 east 클러스터에 east-west 게이트웨이를 설치
cat ch12/gateways/cluster-east-eastwest-gateway.yaml
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
# east 클러스터에 east-west 게이트웨이를 설치 확인
for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
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
...
NAME READY STATUS RESTARTS AGE
istio-eastwestgateway-866794c798-k9xfl 1/1 Running 0 14s
istio-ingressgateway-7f6f8f8d99-4mlp5 1/1 Running 1 (3h38m ago) 17h
istiod-85976468f-vp4zg 1/1 Running 1 (3h38m ago) 17h
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-9c995.istioinaction east-cluster SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-85976468f-vp4zg 1.17.8
istio-eastwestgateway-866794c798-k9xfl.istio-system east-cluster SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-85976468f-vp4zg 1.17.8
istio-ingressgateway-7f6f8f8d99-4mlp5.istio-system east-cluster SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-85976468f-vp4zg 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 에 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
ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system | grep istioinaction
catalog.istioinaction.svc.cluster.local 80 - outbound EDS
webapp.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
# 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
iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep istioinaction
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.15: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.90 172.18.255.202 15021:32281/TCP,15443:30196/TCP,15012:32628/TCP,15017:32346/TCP 27m
NAME ENDPOINTS AGE
endpoints/istio-eastwestgateway 10.20.0.16:15021,10.20.0.16:15017,10.20.0.16:15012 + 1 more... 27m
# 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.15: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.170 <none> 80/TCP 63m
service/webapp ClusterIP 10.100.0.141 <none> 80/TCP 63m
NAME ENDPOINTS AGE
endpoints/catalog <none> 63m
endpoints/webapp 10.10.0.15:8080 63m
kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
* Trying 10.100.0.170:80...
* connect to 10.100.0.170 port 80 failed: Connection refused
...
배포전배포후
east-west 게이트웨이 설치 후 라우터 모드를 sni-dnat으로 설정하면, SNI 자동 통과 모드를 통해 다중 클러스터 mTLS 포트를 노출시켜 암호화된 트래픽을 복호화 없이 목적지 클러스터로 라우팅할 수 있다.
# 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 에 노출한다.
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 85s
# west 에서 호출 시도
kwest get svc,ep -n istioinaction
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/catalog ClusterIP 10.100.0.170 <none> 80/TCP 63m
service/webapp ClusterIP 10.100.0.141 <none> 80/TCP 63m
NAME ENDPOINTS AGE
endpoints/catalog <none> 63m
endpoints/webapp 10.10.0.15:8080 63m
kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
* Trying 10.100.0.170:80...
* connect to 10.100.0.170 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
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
kwest get IstioOperator -n istio-system installed-state-istio-eastwestgateway -o yaml
iwest proxy-status
# west 클러스터에 적용하자. east-cluster의 워크로드를 west-cluster 에 노출한다.
cat ch12/gateways/expose-services.yaml
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.170 <none> 80/TCP 93m
service/webapp ClusterIP 10.100.0.141 <none> 80/TCP 93m
NAME ENDPOINTS AGE
endpoints/catalog <none> 93m
endpoints/webapp 10.10.0.15:8080 93m
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 클러스터
지금까지의 설정으로 SNI 자동 통과로 클러스터 간 라우팅 활성화 → 제어 플레인이 원격 엔드포인트 감지 후 워크로드 설정 업데이트, mTLS 기반 보안 통신 유지한다.
클러스터 간 워크로드 디스커버리 검증하기* VALIDATING CROSS-CLUSTER WORKLOAD DISCOVERY
east-west 게이트웨이 IP 확인으로 webapp 엔보이의 catalog 엔드포인트 노출 검증 → 클러스터 간 트래픽 라우팅 가능 확인한다.
#이를 확인하기 위해 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
# west 에 istio-ingressgateway 인입을 위한 접속 정보 확인
kwest get svc -n istio-system istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istio-ingressgateway LoadBalancer 10.100.0.82 172.18.255.101 15021:30627/TCP,80:30000/TCP,443:31615/TCP,31400:32694/TCP,15443:32016/TCP 119m
EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $EXT_IP
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",
...
# 신규 터미널 : 반복 접속
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
다른 클러스터를 통해서 트래픽이 통신되는것을 알 수 있다.
12.3.7 클러스터 간 로드 밸런싱 Load-balancing across clusters
#
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
kwest get svc,ep -n istioinaction simple-backend
# 트래픽을 허용하기 위해 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 7h15m
gateway.networking.istio.io/simple-backend-gateway 3m10s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/simple-backend-vs-for-gateway ["simple-backend-gateway"] ["simple-backend.istioinaction.io"] 3m10s
virtualservice.networking.istio.io/webapp-virtualservice ["coolstore-gateway"] ["webapp.istioinaction.io"] 7h15m
# 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"
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.17"
],
"start_time": "2025-05-17T14:48:43.973591",
"end_time": "2025-05-17T14:48:44.124935",
"duration": "151.346ms",
"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에도 배포
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
keast get svc,ep -n istioinaction simple-backend
# 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
4 "Hello from EAST"
6 "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.156 <none> 80/TCP 37m
NAME ENDPOINTS AGE
endpoints/simple-backend 10.10.0.17:8080 37m # 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.17:8080 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
...
#
iwest proxy-config listener deploy/istio-ingressgateway.istio-system
iwest proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json
iwest proxy-config route deploy/istio-ingressgateway.istio-system
iwest proxy-config route deploy/istio-ingressgateway.istio-system --name http.8080
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.17:8080 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
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.17",
"portValue": 8080
}
"weight": 1,
"locality": {}
...
{
"address": {
"socketAddress": {
"address": "172.18.255.202",
"portValue": 15443
}
"weight": 1,
"locality": {}
...
클러스터 간 지역 인식 라우팅 검증하기 VERIFYING LOCALITY-AWARE ROUTING ACROSS CLUSTERS
#
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"
}
...
#엔드포인트 상태를 수동적으로 확인하도록, 이상값 감지를 사용하는 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
kwest get gw,vs,dr -n istioinaction
# 확인
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.18:8080 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
#
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
docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
# 동일 클러스터 안에서 라우팅 되는 것을 확인
for i in {1..20}; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; echo ; done | sort | uniq -c
20 "Hello from WEST"
#
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"
}
...
지역별 라우팅으로인해, 한쪽으로만 트래픽이 흐르는것을 알 수 있다.
클러스터 간 장애 극복 확인하기 VERIFYING CROSS-CLUSTER FAILOVER
# 신규 터미널 : 반복 접속 해두기
while true; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
...
"Hello from WEST"
2025-05-18 09:31:21
"Hello from EAST" # failover 시점
2025-05-18 09:31:23
...
#
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.21:8080 HEALTHY FAILED outbound|80||simple-backend.istioinaction.svc.cluster.local
172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
인가 정책을 사용해 클러스터 간 접근 제어 확인하기 VERIFYING CROSS-CLUSTER ACCESS CONTROL USING AUTHORIZATION POLICIES
AuthorizationPolicy 적용으로 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
keast get authorizationpolicy -A
#업데이트가 전파되고 나면, west-cluster 의 워크로드에서 요청을 만들어 정책을 시험해보자. 이를 위해 임시 파드를 실행한다.
kwest run netshoot -n istioinaction --rm -it --image=nicolaka/netshoot -- zsh
-----------------------------------
#
curl -s webapp.istioinaction/api/catalog
# 직접 요청하면 실패!
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
...
# 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
-----------------------------------
접속하는 환경에따라 성공/실패
AuthorizationPolicy로 인그레스 게이트웨이 출처 트래픽만 허용해 외부 접근 차단 가능함을 확인된다. 이는 상호 인증된 mTLS 기반 메타데이터를 활용해 클러스터 간 세밀한 접근 제어 구현 가능하다는것을 알 수 있다. 이를 통해 다중 클러스터 환경에서도 별도 설정 없이 일관된 보안 정책 적용 및 기능 통합 가능하다는것.
이스티오 컨트롤 플레인은 쿠버네티스 이벤트를 실시간 감지해 서비스 메시 설정을 업데이트한다. 설정 동기화가 지연되면 유령 워크로드가 발생해 삭제된 엔드포인트로 트래픽이 라우팅된다. 이로 인해 요청 실패와 시스템 불안정성이 생기지만, 재시도와 이상값 감지로 단기 지연은 완화된다.
이스티오 데이터 플레인은 궁극적 일관성 설계로 인해 설정 동기화에 잠깐의 지연이 발생할 수 있다. 비정상 워크로드 이벤트 발생 시 컨트롤 플레인(istiod)이 새로운 설정을 생성하지만, 업데이트 지연으로 인해 프록시가 낡은 설정을 유지하면 유령 엔드포인트로 트래픽이 라우팅된다. 이스티오는 **재시도(기본 2회)**와 이상값 감지(실패 엔드포인트 자동 제거) 메커니즘을 통해 단기 지연을 완화하지만, 장기간 지연 시 사용자 영향이 불가피하다.
이스티오 컨트롤 플레인(istiod)은 들어오는 이벤트를 처리할 때 디바운싱과 스로틀링 기법을 활용한다. 첫째, 이벤트가 발생하면 DiscoveryServer가 이를 수신하고 일정 시간(PILOT_DEBOUNCE_AFTER) 동안 후속 이벤트를 모아 병합한다. 이 과정에서 중복 이벤트는 제거되고, 설정 업데이트 빈도가 줄어든다. 둘째, 디바운싱이 완료되면 병합된 이벤트를 푸시 대기열에 추가한다. 이때 스로틀링(PILOT_PUSH_THROTTLE)으로 동시 처리 가능한 푸시 요청 수를 제한해 CPU 자원 낭비를 방지한다. 셋째, 처리된 이벤트는 엔보이 설정으로 변환되어 워크로드에 전파된다. 디바운싱 시간 단축과 스로틀링 조정을 통해 컨트롤 플레인의 성능을 최적화할 수 있다.
# 실습 환경 준비
kubectl -n istioinaction apply -f services/catalog/kubernetes/catalog.yaml
kubectl -n istioinaction apply -f ch11/catalog-virtualservice.yaml
kubectl -n istioinaction apply -f ch11/catalog-gateway.yaml
# 확인
kubectl get deploy,gw,vs -n istioinaction
# 반복 설정 해두기
while true; do curl -s http://catalog.istioinaction.io:30000/items ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
# 컨트롤 플레인 메트릭 확인
kubectl exec -it -n istio-system deploy/istiod -- curl localhost:15014/metrics
# HELP citadel_server_csr_count The number of CSRs received by Citadel server.
# TYPE citadel_server_csr_count counter
citadel_server_csr_count 3
...
이스티오 컨트롤 플레인에서 데이터 플레인 업데이트 지연 시간은 pilot_proxy_convergence_time 메트릭으로 측정되며, 이는 이벤트 발생부터 엔보이 프록시에 설정이 동기화되기까지의 전체 시간을 나타낸다. pilot_proxy_queue_time은 이벤트가 처리 대기열에서 대기하는 시간을, pilot_xds_push_time은 xDS 푸시 작업 자체에 소요되는 시간을 각각 측정해 지연 원인을 세부적으로 진단할 수 있다. 이 메트릭들은 컨트롤 플레인의 동기화 효율성을 모니터링하고, 설정 전파 지연으로 인한 유령 워크로드 문제를 예방하는 데 활용된다.
pilot_proxy_convergence_time 은 프록시 푸시 요청이 대기열에 안착한 순간 부터 워크로드에 배포 되기까지 전체 과정의 지속 시간을 측정한다.
- **Proxy Queue Time** : PromQL - `pilot_proxy_queue_time`
histogram_quantile(0.5, sum(rate(**pilot_proxy_queue_time_bucket**[1m])) by (le))
histogram_quantile(0.9, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
histogram_quantile(0.99, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
histogram_quantile(0.999, sum(rate(pilot_proxy_queue_time_bucket[1m])) by (le))
- **XDS Push Time** : PromQL - `pilot_xds_push_time_bucket`
histogram_quantile(0.5, sum(rate(**pilot_xds_push_time_bucket**[1m])) by (le))
histogram_quantile(0.9, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
histogram_quantile(0.99, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
histogram_quantile(0.999, sum(rate(pilot_xds_push_time_bucket[1m])) by (le))
이스티오 컨트롤 플레인(istiod)의 포화도는 주로 CPU 사용률로 측정되며, 90% 이상 도달 시 설정 업데이트 지연으로 인해 데이터 플레인 동기화가 느려져 유령 워크로드 문제가 발생할 수 있다.
핵심 메트릭인 container_cpu_usage_seconds_total(쿠버네티스 컨테이너 CPU)와 process_cpu_seconds_total(istiod 프로세스 CPU)로 모니터링하며, 포화 상태 시 이벤트 처리 최적화(디바운싱/스로틀링) 또는 리소스 스케일업이 필요하다.
이스티오 컨트롤 플레인의 트래픽 부하는 수신 트래픽(설정 변경 이벤트)과 송신 트래픽(데이터 플레인 푸시)으로 구성된다. 주요 메트릭인 pilot_xds_pushes는 xDS 푸시 빈도를, pilot_inbound_updates는 초당 설정 업데이트 수를 측정해 부하 정도를 파악한다. 이 메트릭들을 모니터링해 컨트롤 플레인 병목 현상을 식별하고, 디바운싱/스로틀링 설정을 조정해 성능을 최적화한다.
Pilot Pushes: 클러스터, 엔드포인트, 리스너, 라우트별로 초당 설정 푸시 횟수를 보여준다.
Pilot Errors: 이스티오 컨트롤 플레인에서 발생한 에러 수를 나타낸다.
Proxy Push Time: 프록시로 설정을 푸시하는 데 걸린 시간을 p50, p90, p99, p99.9 백분위수로 보여준다.
Conflicts: 설정 충돌 발생 건수를 나타낸다.
ADS Monitoring: VirtualService, Service, Connected Endpoint 등 ADS(Envoy와의 연결) 상태를 모니터링한다.
Envoy Details: Envoy 관련 세부 이벤트나 동작 횟수를 초당 단위로 보여준다.
XDS Active Connections: 활성화된 XDS(Envoy와의 설정 동기화) 연결 수를 나타낸다.
XDS Requests Size: XDS 요청/응답의 최대 및 평균 바이트 크기를 시간별로 보여준다.
메트픽 설명
pilot_total_xds_rejects
설정 푸시 거부 횟수
pilot_xds_’cds/lds/rds/cds’_reject
pilot_total_xds_rejects 메트릭의 부분집합. 어느 API 푸시가 거부됐는지 수사망을 좁히는 데 유용함
pilot_xds_write_timeout
push를 시작할 때 발생한 오류와 타임아웃의 합계
pilot_xds_push_context_errors
엔보이 설정을 생성하는 동안 발생한 이스티오 파일럿 오류 횟수. 주로 이스티오 파일럿의 버그와 관련
이제 본격적으로 성능 튜닝을 진행해보자.
이스티오 컨트롤 플레인 성능은 클러스터 변경 빈도, 리소스 할당량, 워크로드 수, 설정 크기에 따라 좌우된다. 성능 최적화를 위해 이벤트 배치 처리(디바운싱), Istiod 수평/수직 확장, Sidecar 리소스로 설정 범위 제한 등의 전략을 사용한다. 핵심 메트릭(pilot_proxy_convergence_time, container_cpu_usage_seconds_total)을 모니터링해 병목 현상을 식별하고 조치해야 한다
# 실습 환경 준비 : 11.2.1 에서 이미 설정함
kubectl -n istioinaction apply -f services/catalog/kubernetes/catalog.yaml
kubectl -n istioinaction apply -f ch11/catalog-virtualservice.yaml
kubectl -n istioinaction apply -f ch11/catalog-gateway.yaml
kubectl get deploy,gw,vs -n istioinaction
# 반복 설정 해두기
while true; do curl -s http://catalog.istioinaction.io:30000/items ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
# 모니터링
while true; do kubectl top pod -n istio-system -l app=istiod --containers=true ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
POD NAME CPU(cores) MEMORY(bytes)
istiod-8d74787f-cqhs2 discovery 7m 65Mi
2025-05-11 15:04:34
POD NAME CPU(cores) MEMORY(bytes)
istiod-8d74787f-cqhs2 discovery 27m 82Mi
2025-05-11 15:04:36
...
# 더미 워크로드 10개 생성
cat ch11/sleep-dummy-workloads.yaml
...
apiVersion: v1
kind: Service
...
spec:
ports:
- port: 80
name: http
selector:
app: sleep
---
apiVersion: apps/v1
kind: Deployment
...
spec:
serviceAccountName: sleep
containers:
- name: sleep
image: governmentpaas/curl-ssl
command: ["/bin/sleep", "3650d"]
imagePullPolicy: IfNotPresent
...
kubectl -n istioinaction apply -f ch11/sleep-dummy-workloads.yaml
# 확인
kubectl get deploy,svc,pod -n istioinaction
...
docker exec -it myk8s-control-plane istioctl proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
catalog-6cf4b97d-5jtzt.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-cqhs2 1.17.8
istio-egressgateway-85df6b84b7-m4699.istio-system Kubernetes SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-8d74787f-cqhs2 1.17.8
istio-ingressgateway-6bb8fb6549-k4ln6.istio-system Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-cqhs2 1.17.8
sleep-6f8cfb8c8f-2nfrm.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-8d74787f-cqhs2 1.17.8
...
#
docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction --fqdn sleep.istioinaction.svc.cluster.local
docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction
10.10.0.16:80 HEALTHY OK outbound|80||sleep.istioinaction.svc.cluster.local
10.10.0.17:80 HEALTHY OK outbound|80||sleep.istioinaction.svc.cluster.local
10.10.0.18:80 HEALTHY OK outbound|80||sleep.istioinaction.svc.cluster.local
10.10.0.19:80 HEALTHY OK outbound|80||sleep.istioinaction.svc.cluster.local
...
이제 테스트는 서비스를 반복적으로 만들어 부하를 생성하고, 프록시에 설정을 업데이트하는 데 걸리는 지연 시간과 P99 값과 푸시 개수를 측정한다.
#!/bin/bash
main(){
## Pass input args for initialization
init_args "$@"
SLEEP_POD=$(kubectl -n istioinaction get pod -l app=sleep -o jsonpath={.items..metadata.name} -n istioinaction | cut -d ' ' -f 1)
PRE_PUSHES=$(kubectl exec -n istio-system deploy/istiod -- curl -s localhost:15014/metrics | grep pilot_xds_pushes | awk '{total += $2} END {print total}')
if [[ -z "$PRE_PUSHES" ]]; then
echo "Failed to query Pilot Pushes from prometheus."
echo "Have you installed prometheus as shown in chapter 7?"
exit 1
fi
echo "Pre Pushes: $PRE_PUSHES"
INDEX="0"
while [[ $INDEX -lt $REPS ]]; do
SERVICE_NAME="service-`openssl rand -hex 2`-$INDEX"
create_random_resource $SERVICE_NAME &
sleep $DELAY
INDEX=$[$INDEX+1]
done
## Wait until the last item is distributed
while [[ "$(curl --max-time .5 -s -o /dev/null -H "Host: $SERVICE_NAME.istioinaction.io" -w ''%{http_code}'' $GATEWAY:30000/items)" != "200" ]]; do
# curl --max-time .5 -s -o /dev/null -H "Host: $SERVICE_NAME.istioinaction.io" $GATEWAY/items
sleep .2
done
echo ==============
sleep 10
POST_PUSHES=$(kubectl exec -n istio-system deploy/istiod -- curl -s localhost:15014/metrics | grep pilot_xds_pushes | awk '{total += $2} END {print total}')
echo
LATENCY=$(kubectl -n istioinaction exec -it $SLEEP_POD -c sleep -- curl "$PROM_URL/api/v1/query" --data-urlencode "query=histogram_quantile(0.99, sum(rate(pilot_proxy_convergence_time_bucket[1m])) by (le))" | jq '.. |."value"? | select(. != null) | .[1]' -r)
echo "Push count:" `expr $POST_PUSHES - $PRE_PUSHES`
echo "Latency in the last minute: `printf "%.2f\n" $LATENCY` seconds"
}
create_random_resource() {
SERVICE_NAME=$1
cat <<EOF | kubectl apply -f -
---
kind: Gateway
apiVersion: networking.istio.io/v1alpha3
metadata:
name: $SERVICE_NAME
namespace: $NAMESPACE
spec:
servers:
- hosts:
- "$SERVICE_NAME.istioinaction.io"
port:
name: http
number: 80
protocol: HTTP
selector:
istio: ingressgateway
---
apiVersion: v1
kind: Service
metadata:
labels:
app: catalog
name: $SERVICE_NAME
namespace: $NAMESPACE
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 3000
selector:
app: catalog
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: $SERVICE_NAME
namespace: $NAMESPACE
spec:
hosts:
- "$SERVICE_NAME.istioinaction.io"
gateways:
- "$SERVICE_NAME"
http:
- route:
- destination:
host: $SERVICE_NAME.istioinaction.svc.cluster.local
port:
number: 80
---
EOF
}
help() {
cat <<EOF
Poor Man's Performance Test creates Services, Gateways and VirtualServices and measures Latency and Push Count needed to distribute the updates to the data plane.
--reps The number of services that will be created. E.g. --reps 20 creates services [0..19]. Default '20'
--delay The time to wait prior to proceeding with another repetition. Default '0'
--gateway URL of the ingress gateway. Defaults to 'localhost'
--namespace Namespace in which to create the resources. Default 'istioinaction'
--prom-url Prometheus URL to query metrics. Defaults to 'prom-kube-prometheus-stack-prometheus.prometheus:9090'
EOF
exit 1
}
init_args() {
while [[ $# -gt 0 ]]; do
case ${1} in
--reps)
REPS="$2"
shift
;;
--delay)
DELAY="$2"
shift
;;
--gateway)
GATEWAY="$2"
shift
;;
--namespace)
NAMESPACE="$2"
shift
;;
--prom-url)
PROM_URL="$2"
shift
;;
*)
help
;;
esac
shift
done
[ -z "${REPS}" ] && REPS="20"
[ -z "${DELAY}" ] && DELAY=0
[ -z "${GATEWAY}" ] && GATEWAY=localhost
[ -z "${NAMESPACE}" ] && NAMESPACE=istioinaction
[ -z "${PROM_URL}" ] && PROM_URL="prom-kube-prometheus-stack-prometheus.prometheus.svc.cluster.local:9090"
}
main "$@"
# (참고) 호출
curl -H "Host: catalog.istioinaction.io" localhost:30000/items
# 확인
kubectl get svc -n istioinaction --no-headers=true | wc -l
kubectl get gw -n istioinaction --no-headers=true | wc -l
kubectl get vs -n istioinaction --no-headers=true | wc -l
# :30000 포트 정보 추가해둘것!
cat bin/performance-test.sh
...
Poor Man's Performance Test creates Services, Gateways and VirtualServices and measures Latency and Push Count needed to distribute the updates to the data plane.
--reps The number of services that will be created. E.g. --reps 20 creates services [0..19]. Default '20'
--delay The time to wait prior to proceeding with another repetition. Default '0'
--gateway URL of the ingress gateway. Defaults to 'localhost'
--namespace Namespace in which to create the resources. Default 'istioinaction'
--prom-url Prometheus URL to query metrics. Defaults to 'prom-kube-prometheus-stack-prometheus.prometheus:9090'
...
# 성능 테스트 스크립트 실행!
./bin/performance-test.sh --reps 10 --delay 2.5 --prom-url prometheus.istio-system.svc.cluster.local:9090
Pre Pushes: 335
...
ateway.networking.istio.io/service-00a9-9 created
service/service-00a9-9 created
virtualservice.networking.istio.io/service-00a9-9 created
==============
Push count: 510 # 변경 사항을 적용하기 위한 푸시 함수
Latency in the last minute: 0.45 seconds # 마지막 1분 동안의 지연 시간
# 확인
kubectl get svc -n istioinaction --no-headers=true | wc -l
kubectl get gw -n istioinaction --no-headers=true | wc -l
kubectl get vs -n istioinaction --no-headers=true | wc -l
부하가 발생중이다.
#딜레이없이 실행
# 성능 테스트 스크립트 실행 : 딜레이 없이
./bin/performance-test.sh --reps 10 --prom-url prometheus.istio-system.svc.cluster.local:9090
Push count: 51
Latency in the last minute: 0.47 seconds
# 확인
kubectl get svc -n istioinaction --no-headers=true | wc -l
kubectl get gw -n istioinaction --no-headers=true | wc -l
kubectl get vs -n istioinaction --no-headers=true | wc -l
사이드카를 사용해 푸시 횟수 및 설정 크기 줄이기 REDUCING CONFIGURATION SIZE AND NUMBER OF PUSHES USING SIDECARS
이스티오는 기본적으로 모든 서비스 프록시에 메시 내 모든 워크로드 정보를 제공해, 불필요하게 설정 크기가 커지는 문제가 발생한다.
워크로드가 200개인 중간 클러스터만 돼도 엔보이 설정이 400MB로 늘어나며, 이로 인해 연산 성능, 네트워크 대역폭, 메모리가 더 많이 필요하다.
이 설정이 모든 사이드카 프록시에 저장되기 때문이다.
이스티오 Sidecar 리소스는 특정 워크로드의 트래픽 제어를 세밀하게 설정한다. workloadSelector로 대상 워크로드를 지정하고, egress 필드로 허용할 외부 서비스를 명시적으로 정의한다. outboundTrafficPolicy를 REGISTRY_ONLY로 설정하면 등록된 서비스만 접근 가능해 보안이 강화된다. 예시 YAML에서는 app: foo 워크로드가 bar.istioinaction과 istio-system 서비스만 접근하도록 제한한다. 이를 통해 불필요한 설정 전파를 막아 CPU/메모리 사용량과 네트워크 부하를 줄인다.
메시 범위 사이드카 설정으로 더 나은 기본값 정의하기 DEFINING BETTER DEFAULTS WITH A MESH-WIDE SIDECAR CONFIGURATION
**Sidecar 리소스**를 사용해 트래픽 송신을 istio-system 및 prometheus 네임스페이스의 서비스로만 제한한다. 이를 통해 프록시에 전달되는 엔보이 설정 크기가 현저히 줄어들어 컨트롤 플레인 부하가 감소한다. 설정 최소화로 CPU/메모리 사용량과 네트워크 대역폭이 절약되며, 서비스 간 명시적 의존성 정의가 강제되어 운영 안정성이 향상된다.
# cat ch11/sidecar-mesh-wide.yaml
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: default # istio-system 네임스페이스의 사이드카는 메시 전체에 적용된다.
namespace: istio-system # 위 설명 동일.
spec:
egress:
- hosts:
- "istio-system/*" # istio-system 네임스페이스의 워크로드만 트래픽 송신을 할 수 있게 설정한다.
- "prometheus/*" # 프로메테우스 네임스페이스도 트래픽 송신을 할 수 있게 설정한다.
outboundTrafficPolicy:
mode: REGISTRY_ONLY # 모드는 사이드카에 설정한 서비스로만 트래픽 송신을 허용한다
# 테스트를 위해 샘플 nginx 배포
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF
# catalog 에서 nginx 서비스 접속 확인
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction | grep nginx
docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction | grep nginx
docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction | grep nginx
10.10.0.26:80 HEALTHY OK outbound|80||nginx.default.svc.cluster.local
kubectl exec -it deploy/catalog -n istioinaction -- curl nginx.default | grep title
<title>Welcome to nginx!</title>
# istio-system, prometheus 네임스페이스만 egress 허용 설정
kubectl -n istio-system apply -f ch11/sidecar-mesh-wide.yaml
kubectl get sidecars -A
# catalog 에서 nginx 서비스 접속 확인
docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction | grep nginx
docker exec -it myk8s-control-plane istioctl proxy-config cluster deploy/catalog.istioinaction | grep nginx
docker exec -it myk8s-control-plane istioctl proxy-config endpoint deploy/catalog.istioinaction | grep nginx
kubectl exec -it deploy/catalog -n istioinaction -- curl nginx.default | grep title
# envoy config 크기 다시 확인!
CATALOG_POD=$(kubectl -n istioinaction get pod -l app=catalog -o jsonpath={.items..metadata.name} | cut -d ' ' -f 1)
kubectl -n istioinaction exec -ti $CATALOG_POD -c catalog -- curl -s localhost:15000/config_dump > /tmp/config_dump
du -sh /tmp/config_dump
520K /tmp/config_dump
사이드카 배포 후...
안나온다.
설정파일이 4분의 1로..
즉. 모든 구간에서 최적화가 이뤄졌다.
# 성능 테스트 스크립트 실행!
./bin/performance-test.sh --reps 10 --delay 2.5 --prom-url prometheus.istio-system.svc.cluster.local:9090
...
Push count: 88 # 변경 사항을 적용하기 위한 푸시 함수
Latency in the last minute: 0.10 seconds # 마지막 1분 동안의 지연 시간
# 확인
kubectl get svc -n istioinaction --no-headers=true | wc -l
kubectl get gw -n istioinaction --no-headers=true | wc -l
kubectl get vs -n istioinaction --no-headers=true | wc -l
11.3.3 이벤트 무시하기*: 디스커버리 셀렉터로 디스커버리 범위 줄이기 meshConfig.discoverySelectors
이스티오 컨트롤 플레인은 기본적으로 모든 네임스페이스의 파드/서비스 이벤트를 감시해 대규모 클러스터에서 성능 부담이 커진다. Istio 1.10부터 discovery selector 기능이 도입되어, 라벨 기반으로 감시할 네임스페이스를 선택적으로 지정할 수 있다. MeshConfig에 discoverySelectors를 설정해 불필요한 워크로드 이벤트를 필터링하면 컨트롤 플레인 부하를 크게 줄일 수 있다.
11.3.4 이벤트 배치 처리 및 푸시 스로틀링 속성 Event-batching and push-throttling properties
이스티오 컨트롤 플레인은 이벤트를 일정 시간(PILOT_DEBOUNCE_AFTER) 동안 모아 병합해 디바운싱을 수행한다. 이를 통해 중복 이벤트를 제거하고 단일 설정 업데이트로 변환해 푸시 횟수를 줄인다. PILOT_PUSH_THROTTLE로 동시 처리 가능한 푸시 요청 수를 제한해 CPU 자원 낭비를 방지한다.
그러나 푸시를 너무 미루면 데이터 플레인 설정이 오래돼 최신 상태가 아니게 될 수 있는데, 상술한 것처럼 이런 상황 역시 원하는 바가 아니다.
- 배치 기간과 푸시 스로틀링을 정의하는 환경 변수 ENVIRONMENT VARIABLES THAT DEFINE THE BATCHING PERIOD AND PUSH THROTTLING
이스티오 컨트롤 플레인 성능 최적화를 위해 PILOT_DEBOUNCE_AFTER와 PILOT_DEBOUNCE_MAX로 이벤트 배치 처리 기간을 조정한다.
PILOT_DEBOUNCE_AFTER(기본 100ms): 이벤트 발생 후 푸시 대기열 추가 전 대기 시간. 이 기간 내 새 이벤트 발생 시 병합 후 재대기.
PILOT_DEBOUNCE_MAX(기본 10s): 최대 배치 처리 허용 시간. 이 시간 초과 시 즉시 푸시.
PILOT_ENABLE_EDS_DEBOUNCE(기본 true)로 엔드포인트 업데이트도 배치 처리 여부를 결정한다.
PILOT_PUSH_THROTTLE(기본 100)로 동시 처리 가능한 푸시 요청 수를 제어한다.
컨트롤 플레인 포화 시: 배치 기간 늘리고 스로틀 감소
빠른 업데이트 필요 시: 배치 기간 줄이고 스로틀 증가.
컨트롤 플레인에 리소스 추가 할당하기 ALLOCATING ADDITIONAL RESOURCES TO THE CONTROL PLANE
이스티오 컨트롤 플레인 성능 향상을 위해 스케일 아웃 또는 스케일 업을 선택한다. 송신 트래픽 병목 시 워크로드 분산을 위해 스케일 아웃으로 istiod 인스턴스를 추가한다. 수신 트래픽 병목 시 리소스 처리 능력 강화를 위해 스케일 업으로 기존 인스턴스의 CPU/메모리를 증설한다.
#
kubectl get pod -n istio-system -l app=istiod
kubectl describe pod -n istio-system -l app=istiod
...
Requests:
cpu: 10m
memory: 100Mi
...
kubectl resource-capacity -n istio-system -u -l app=istiod
NODE CPU REQUESTS CPU LIMITS CPU UTIL MEMORY REQUESTS MEMORY LIMITS MEMORY UTIL
myk8s-control-plane 10m (0%) 0m (0%) 8m (0%) 100Mi (0%) 0Mi (0%) 90Mi (0%)
# myk8s-control-plane 진입 후 설치 진행
docker exec -it myk8s-control-plane bash
-----------------------------------
# demo 프로파일 컨트롤 플레인 배포 시 적용
istioctl install --set profile=demo \
--set values.pilot.resources.requests.cpu=1000m \
--set values.pilot.resources.requests.memory=1Gi \
--set values.pilot.replicaCount=2 -y
exit
-----------------------------------
#
kubectl get pod -n istio-system -l app=istiod
NAME READY STATUS RESTARTS AGE
istiod-5485dd8c48-6ngdc 1/1 Running 0 11s
istiod-5485dd8c48-chjsz 1/1 Running 0 11s
kubectl resource-capacity -n istio-system -u -l app=istiod
NODE CPU REQUESTS CPU LIMITS CPU UTIL MEMORY REQUESTS MEMORY LIMITS MEMORY UTIL
myk8s-control-plane 2000m (25%) 0m (0%) 119m (1%) 2048Mi (17%) 0Mi (0%) 107Mi (0%)
kubectl describe pod -n istio-system -l app=istiod
...
Requests:
cpu: 1
memory: 1Gi
...
컨트롤 플레인 성능 최적화의 요점은 다음과 같다.
항상 워크로드에 사이드카 설정을 정의하자. 이것만으로도 대부분의 이점을 얻을 수 있다.
컨트롤 플레인이 포화 상태인데 이미 리소스를 많이 할당한 경우에만 이벤트 배치를 수정하자.
병목이 송신 트래픽일 때 istiod 스케일 아웃하자.
병목이 수신 트래픽일 때 istiod 스케일 업하자.
Istiod 디플로이먼트 오토스케일링Autoscaling istiod deployment
이스티오 컨트롤 플레인(istiod)의 오토스케일링은 **30분 유지되는 gRPC 연결(ADS)**로 인해 효율성이 제한된다. 새로 추가된 istiod 복제본은 기존 연결이 만료될 때까지 트래픽을 받지 못해 HPA가 무의미하게 축소되며, 이로 인해 퍼덕거림(flapping) 현상이 발생한다. MaxServerConnectionAge를 조정해 연결 수명을 단축하거나 ProxyConfig/DestinationRule을 활용하면 연결 재분산을 유도해 오토스케일링 효율을 개선할 수 있다.
현재로서 오토스케일링을 구성하는 가장 좋은 방법은 점진적인 부하 증가에 맞추는 것이다.
며칠, 몇주, 심지어는 몇 달 단위에 걸쳐서 말이다.
이렇게 하면 성능을 지속적으로 모니터링하고 디폴리어먼트 스케일링 결정을 내려야 하는 인적 자원의 부담을 줄일 수 있다.