현대 클라우드 네이티브 애플리케이션은 수십, 수백 개의 마이크로서비스로 구성됩니다. 각 서비스는 독립적으로 배포되고 확장되지만, 이로 인해 새로운 문제들이 발생합니다:
전통적인 모놀리식 아키텍처:
┌─────────────────────────────┐
│ Single Application │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ UI │ │Logic│ │ DB │ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────┘
문제: 단일 장애점, 확장성 제한
마이크로서비스 아키텍처:
┌─────┐ ┌─────┐ ┌─────┐
│User │───▶│Order│───▶│ Pay │
│ UI │ │ Svc │ │ Svc │
└─────┘ └──┬──┘ └─────┘
│
├─────▶┌─────┐
│ │Notif│
│ └─────┘
│
└─────▶┌─────┐
│ Log │
└─────┘
문제: 서비스 간 통신 복잡도 폭발!
1. 서비스 간 통신 관리의 복잡성
# 각 서비스 코드에 반복적으로 작성해야 하는 코드들
# 재시도 로직
for retry in range(3):
try:
response = requests.get('http://payment-service/pay')
break
except TimeoutError:
if retry == 2:
raise
time.sleep(2 ** retry)
# 타임아웃 설정
response = requests.get('http://service', timeout=3.0)
# 서킷 브레이커
if circuit_breaker.is_open('payment-service'):
return fallback_response()
# 로깅
logger.info(f"Calling {service_name} at {timestamp}")
# 메트릭 수집
metrics.increment('http_requests_total')
문제점:
2. 관측성(Observability) 부재
3. 보안 문제
핵심 아이디어: 네트워크 기능을 애플리케이션 코드에서 분리
Service Mesh 없이:
┌─────────────────────────────┐
│ Application Code │
│ ┌──────────────────────┐ │
│ │ Business Logic │ │
│ ├──────────────────────┤ │
│ │ ❌ Retry Logic │ │ ← 중복 코드!
│ │ ❌ Timeout │ │
│ │ ❌ Metrics │ │
│ │ ❌ Tracing │ │
│ │ ❌ mTLS │ │
│ └──────────────────────┘ │
└─────────────────────────────┘
Service Mesh와 함께:
┌─────────────────────────────┐
│ Application Code │
│ ┌──────────────────────┐ │
│ │ Business Logic │ │ ← 비즈니스 로직만!
│ │ (순수 코드) │ │
│ └──────────────────────┘ │
└─────────────────────────────┘
↕
┌─────────────────────────────┐
│ Sidecar Proxy │
│ ┌──────────────────────┐ │
│ │ ✅ Retry Logic │ │ ← 인프라 레벨에서 처리
│ │ ✅ Timeout │ │
│ │ ✅ Metrics │ │
│ │ ✅ Tracing │ │
│ │ ✅ mTLS │ │
│ └──────────────────────┘ │
└─────────────────────────────┘
Istio는 오픈소스 Service Mesh 플랫폼으로, 마이크로서비스 간 통신을 관리하고 보안, 관측성, 트래픽 제어를 제공하는 인프라 레이어입니다.
주요 특징:
┌──────────────────────────────────────────────────────┐
│ Istio Service Mesh │
├──────────────────────────────────────────────────────┤
│ │
│ 📊 Traffic Management (트래픽 관리) │
│ ├─ Canary Deployment (점진적 배포) │
│ ├─ A/B Testing (버전 분기) │
│ ├─ Circuit Breaker (장애 격리) │
│ └─ Retry & Timeout (자동 재시도) │
│ │
│ 🔒 Security (보안) │
│ ├─ Mutual TLS (서비스 간 암호화) │
│ ├─ Authentication (인증) │
│ └─ Authorization (권한 관리) │
│ │
│ 👁 Observability (관측성) │
│ ├─ Metrics (메트릭 자동 수집) │
│ ├─ Distributed Tracing (분산 추적) │
│ ├─ Access Logs (접근 로그) │
│ └─ Topology Visualization (토폴로지 시각화) │
│ │
└──────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Control Plane │
│ ┌────────────────────────────────────────────────────┐ │
│ │ istiod │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Pilot │ │ Citadel │ │ Galley │ │ │
│ │ │(트래픽) │ │ (보안) │ │ (설정 검증) │ │ │
│ │ └──────────┘ └──────────┘ └──────────────┘ │ │
│ └─────────────────────┬──────────────────────────────┘ │
│ │ xDS API (설정 푸시) │
└────────────────────────┼────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Data Plane │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Pod A │ │ Pod B │ │ Pod C │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ App │ │ │ │ App │ │ │ │ App │ │ │
│ │ └────┬────┘ │ │ └────┬────┘ │ │ └────┬────┘ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ ┌────▼────┐ │ │ ┌────▼────┐ │ │ ┌────▼────┐ │ │
│ │ │ Envoy │◀┼───┼▶│ Envoy │◀┼───┼▶│ Envoy │ │ │
│ │ │ Proxy │ │ │ │ Proxy │ │ │ │ Proxy │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ Sidecar Sidecar Sidecar │
└─────────────────────────────────────────────────────────────┘
istiod는 3가지 핵심 컴포넌트를 통합한 단일 바이너리입니다 (Istio 1.5 이후):
1. Pilot (트래픽 관리)
역할: VirtualService, DestinationRule 등을 Envoy 설정으로 변환
예시:
VirtualService (사람이 작성) → Pilot → Envoy Config (기계가 이해)
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews
http:
- match:
- headers:
user:
exact: "jason"
route:
- destination:
host: reviews
subset: v2 # jason만 v2로 라우팅
- route:
- destination:
host: reviews
subset: v1 # 나머지는 v1로
2. Citadel (보안 관리)
역할: 인증서 자동 발급 및 갱신
동작 과정:
1. Pod 생성 시 Service Account 확인
2. X.509 인증서 자동 발급 (90일 만료)
3. Envoy에 인증서 주입
4. 서비스 간 mTLS 자동 활성화
5. 인증서 만료 전 자동 갱신
결과: 개발자가 인증서 관리 불필요!
3. Galley (설정 검증)
역할: Istio 설정 YAML 유효성 검증
검증 항목:
- YAML 문법 오류
- 필수 필드 누락
- 중복된 리소스
- 참조 무결성 (존재하지 않는 서비스 참조 등)
kubectl apply 전에 미리 검증:
istioctl analyze
Envoy는 고성능 L7 프록시로 각 Pod에 Sidecar로 주입됩니다.
Sidecar 패턴 동작 원리:
Pod 내부 구조:
┌─────────────────────────────────────────────┐
│ Pod (productpage-v1) │
│ │
│ ┌────────────────────────────────────┐ │
│ │ productpage Container │ │
│ │ (Python Flask App) │ │
│ │ Port: 9080 │ │
│ │ localhost:9080으로 요청 보냄 │ │
│ └──────────────┬─────────────────────┘ │
│ │ │
│ │ 127.0.0.1:9080 │
│ ▼ │
│ ┌────────────────────────────────────┐ │
│ │ istio-proxy Container │ │
│ │ (Envoy Sidecar) │ │
│ │ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Inbound Listener │ │ │
│ │ │ 15006: 모든 inbound 트래픽 │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Outbound Listener │ │ │
│ │ │ 15001: 모든 outbound 트래픽│ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────┘
Envoy가 수행하는 작업:
트래픽 인터셉트
# iptables 규칙으로 모든 트래픽을 Envoy로 리다이렉트
Outbound 트래픽:
App → localhost:9080
↓ (iptables 리다이렉트)
Envoy:15001 → 목적지 서비스
Inbound 트래픽:
외부 → Envoy:15006 → App:9080
로드 밸런싱
reviews 서비스 호출 시:
Envoy가 3개 엔드포인트 중 선택:
- reviews-v1-pod-1 (10.1.2.3:9080)
- reviews-v2-pod-1 (10.1.2.4:9080)
- reviews-v3-pod-1 (10.1.2.5:9080)
알고리즘: Round Robin, Random, Least Request
재시도 & 타임아웃
# VirtualService에 정의
http:
- route:
- destination:
host: reviews
retries:
attempts: 3 # 3번 재시도
perTryTimeout: 2s # 시도당 2초
메트릭 자동 수집
모든 요청에 대해 자동 수집:
- 요청 수 (istio_requests_total)
- 응답 시간 (istio_request_duration_milliseconds)
- 에러율 (istio_request_errors_total)
- 트래픽 크기 (istio_request_bytes)
# 클러스터 정보
kubectl get nodes -o wide
NAME ROLES VERSION INTERNAL-IP
cpu1 control-plane v1.31.13 172.30.1.43
cpu2 worker v1.31.13 172.30.1.80
gpu1 worker v1.31.13 172.30.1.38
cd ~/istio-demo
curl -L https://istio.io/downloadIstio | sh -
# 환경변수 설정
export PATH="/root/istio-demo/istio-1.28.0/bin:$PATH"
# 확인
istioctl version
다운로드된 디렉토리 구조:
istio-1.28.0/
├── bin/
│ └── istioctl # Istio CLI 도구
├── manifests/
│ ├── profiles/ # 설치 프로파일
│ │ ├── default.yaml # 프로덕션용 (우리가 사용)
│ │ ├── demo.yaml # 데모/학습용
│ │ └── minimal.yaml # 최소 설치
│ └── charts/ # Helm Charts
├── samples/
│ ├── bookinfo/ # 샘플 애플리케이션
│ │ ├── platform/kube/ # Pod/Service 정의
│ │ └── networking/ # Gateway/VirtualService
│ └── addons/ # 관측 도구
│ ├── kiali.yaml
│ ├── prometheus.yaml
│ └── grafana.yaml
└── tools/ # 유틸리티
프로파일 비교:
| 프로파일 | 용도 | istiod | Ingress GW | Egress GW |
|---|---|---|---|---|
default | 프로덕션 | ✅ | ✅ | ❌ |
demo | 학습/데모 | ✅ | ✅ | ✅ |
minimal | 최소 구성 | ✅ | ❌ | ❌ |
설치 명령:
istioctl install --set profile=default -y
설치 결과 확인:
kubectl get pods -n istio-system -o wide
NAME READY STATUS NODE
istiod-57b4d7f8b8-875cs 1/1 Running cpu2
istio-ingressgateway-76cc55cb99-9nvnt 1/1 Running cpu1
컴포넌트 설명:
istiod (Control Plane)
istio-ingressgateway (Ingress Gateway)
왜 Egress Gateway가 없나?
Sidecar 자동 주입 원리:
Kubernetes Mutating Admission Webhook 활용
1. kubectl apply로 Pod 생성 요청
↓
2. API Server가 Webhook 호출
↓
3. Istio가 Pod spec 수정 (istio-proxy 컨테이너 추가)
↓
4. 수정된 spec으로 Pod 생성
default namespace에 자동 주입 활성화:
kubectl label namespace default istio-injection=enabled
# 확인
kubectl get namespace default --show-labels
NAME LABELS
default istio-injection=enabled,kubernetes.io/metadata.name=default
동작 확인:
default namespace에 생성되는 모든 Pod에 자동으로 istio-proxy 컨테이너가 주입됩니다.Bookinfo 아키텍처:
┌──────────────────────────────────────────────────────┐
│ Bookinfo App │
│ │
│ ┌─────────────┐ │
│ │ productpage│ (Python) │
│ │ v1 │ │
│ └──────┬──────┘ │
│ │ │
│ ├────┼─────┬─────────────┐ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
│ │det│ │rev│ │rev│ │rat│ │
│ │ v1│ │ v1│ │ v2│ ★ │ v1│ │
│ └───┘ └───┘ └─┬─┘ └───┘ │
│ Ruby Java │ │ Node.js │
│ │ │ │
│ ┌─▼─▼┐ │
│ │rev │ ★★ │
│ │ v3 │ │
│ └────┘ │
│ Java │
│ │
│ ★ = reviews v2/v3는 ratings 호출 │
│ v1/v2/v3 = 3가지 버전으로 Canary 테스트 가능 │
└──────────────────────────────────────────────────────┘
배포:
kubectl apply -f ~/istio-demo/istio-1.28.0/samples/bookinfo/platform/kube/bookinfo.yaml
결과 확인:
kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-77b775f46-m68lr 2/2 Running 0 5m
productpage-v1-78dfd4688c-k7m2h 2/2 Running 0 5m
ratings-v1-7c4c8d6794-bgd99 2/2 Running 0 5m
reviews-v1-849f9bc5d6-glqll 2/2 Running 0 5m
reviews-v2-5c757d5846-rxn2k 2/2 Running 0 5m
reviews-v3-6d5d98f5c4-n4gj8 2/2 Running 0 5m
Sidecar 주입 확인:
kubectl get pod productpage-v1-78dfd4688c-k7m2h -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}'
productpage ← 애플리케이션 컨테이너
istio-proxy ← Envoy Sidecar (자동 주입됨!)
Pod 내부 구조 확인:
kubectl describe pod productpage-v1-78dfd4688c-k7m2h
Init Containers:
istio-init: # iptables 규칙 설정
Image: docker.io/istio/proxyv2:1.28.0
Containers:
productpage: # 애플리케이션
Image: docker.io/istio/examples-bookinfo-productpage-v1:1.20.3
Port: 9080/TCP
istio-proxy: # Envoy Sidecar
Image: docker.io/istio/proxyv2:1.28.0
Ports: 15090/TCP (메트릭), 15021/TCP (헬스체크)
Gateway는 Istio의 외부 트래픽 진입점 설정입니다.
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
# 어떤 Ingress Gateway Pod에 이 설정을 적용할지 선택
selector:
istio: ingressgateway # istio=ingressgateway 라벨을 가진 Pod 선택
# 어떤 포트와 프로토콜을 열지 정의
servers:
- port:
number: 8080 # Envoy 내부 리스닝 포트
name: http
protocol: HTTP
hosts:
- "*" # 모든 호스트명 허용
항목별 설명:
| 항목 | 설명 | 예시 |
|---|---|---|
selector | 어떤 Ingress Gateway Pod에 적용할지 | istio: ingressgateway |
port.number | Envoy가 리스닝할 포트 | 8080 (Service에서 80→8080 매핑) |
port.protocol | 프로토콜 | HTTP, HTTPS, TCP |
hosts | 허용할 호스트명 | * (모두), bookinfo.com |
Selector 매칭 원리:
# Gateway 리소스의 selector
selector:
istio: ingressgateway
# istio-ingressgateway Pod의 라벨 확인
kubectl get pod -n istio-system -l istio=ingressgateway --show-labels
NAME LABELS
istio-ingressgateway-76cc55cb99-9nvnt istio=ingressgateway,...
↑
매칭됨! 이 Pod에 설정 적용
VirtualService는 트래픽 라우팅 규칙을 정의합니다.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: bookinfo
spec:
# 어떤 호스트 요청을 처리할지
hosts:
- "*" # 모든 호스트 요청 받음
# 어느 Gateway를 통해 들어온 트래픽에 적용할지
gateways:
- bookinfo-gateway # 위에서 만든 Gateway와 연결
# HTTP 라우팅 규칙
http:
- match: # 다음 URL 중 하나라도 매칭되면
- uri:
exact: /productpage # 정확히 /productpage
- uri:
prefix: /static # /static으로 시작
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route: # 위 조건에 매칭되면 여기로 라우팅
- destination:
host: productpage # productpage 서비스로 전달
port:
number: 9080 # 9080 포트로
항목별 설명:
| 항목 | 설명 | 예시 |
|---|---|---|
hosts | 처리할 호스트명 | *, reviews.default.svc.cluster.local |
gateways | 적용할 Gateway | bookinfo-gateway, mesh (내부 트래픽) |
match.uri.exact | 정확한 경로 매칭 | /productpage |
match.uri.prefix | 접두사 매칭 | /static → /static/css/style.css 포함 |
destination.host | 목적지 서비스 | productpage (Kubernetes Service 이름) |
Gateway + VirtualService 연결 원리:
1. Gateway 리소스 생성
name: bookinfo-gateway
2. VirtualService에서 참조
gateways:
- bookinfo-gateway ← 이름으로 연결!
3. istiod가 매칭을 감지하고 Envoy 설정 생성
kubectl apply -f ~/istio-demo/istio-1.28.0/samples/bookinfo/networking/bookinfo-gateway.yaml
# 확인
kubectl get gateway,virtualservice
NAME AGE
gateway.networking.istio.io/bookinfo-gateway 1m
NAME GATEWAYS HOSTS
virtualservice.networking.istio.io/bookinfo ["bookinfo-gateway"] ["*"]
istio-ingressgateway Service 확인:
kubectl get svc -n istio-system istio-ingressgateway -o yaml | grep -A 20 "ports:"
ports:
- name: status-port
nodePort: 30670
port: 15021
targetPort: 15021
- name: http2
nodePort: 30192 # ← NodePort (외부 접근)
port: 80 # ← Service Port
targetPort: 8080 # ← Pod 내부 Envoy 포트
- name: https
nodePort: 31797
port: 443
targetPort: 8443
포트 매핑:
외부 요청: http://172.30.1.43:30192/productpage
NodePort 30192
↓
Service Port 80
↓
Pod targetPort 8080
↓
Envoy Listener (0.0.0.0:8080)
↓
Gateway 리소스 (port: 8080에서 매칭)
↓
VirtualService 라우팅 (/productpage → productpage:9080)
↓
productpage Service (ClusterIP 10.105.75.162:9080)
↓
productpage Pod
├─ istio-proxy (15006 → 9080 포트포워딩)
└─ productpage Container (9080)
┌─────────────────────────────────────────────────────────────────────┐
│ 외부 사용자 (브라우저) │
│ curl http://172.30.1.43:30192/productpage │
└──────────────────────┬──────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Kubernetes Node (cpu1) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ NodePort 30192 (iptables NAT) │ │
│ │ DNAT: 외부IP:30192 → Service:80 │ │
│ └──────────────────────┬──────────────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ istio-ingressgateway Service │
│ Type: LoadBalancer │
│ ClusterIP: 10.104.64.238:80 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ PORT MAPPING: │ │
│ │ port: 80 → targetPort: 8080 │ │
│ └──────────────────────┬─────────────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ istio-ingressgateway Pod (istio-system namespace) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Envoy Proxy (Container) │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Listener: 0.0.0.0:8080 (HTTP) │ │ │
│ │ │ "8080 포트에서 HTTP 요청 대기 중..." │ │ │
│ │ └─────────────────┬────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Gateway 리소스 매칭 (bookinfo-gateway) │ │ │
│ │ │ - selector: istio=ingressgateway ✓ │ │ │
│ │ │ - port: 8080 ✓ │ │ │
│ │ │ - hosts: "*" ✓ │ │ │
│ │ │ → "이 Gateway 설정 적용!" │ │ │
│ │ └─────────────────┬────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ VirtualService 라우팅 규칙 (bookinfo) │ │ │
│ │ │ - gateways: [bookinfo-gateway] ✓ │ │ │
│ │ │ - uri.exact: "/productpage" ✓ │ │ │
│ │ │ - destination.host: "productpage" │ │ │
│ │ │ - destination.port: 9080 │ │ │
│ │ │ → "productpage:9080으로 라우팅!" │ │ │
│ │ └─────────────────┬────────────────────────────────┘ │ │
│ └────────────────────┼───────────────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────────────┘
│ HTTP Request
│ Host: productpage:9080
│ Path: /productpage
▼
┌─────────────────────────────────────────────────────────────────────┐
│ productpage Service (ClusterIP) │
│ ClusterIP: 10.105.75.162:9080 │
│ Selector: app=productpage │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Endpoint: productpage-v1-pod (10.244.2.15:9080) │ │
│ └──────────────────────┬─────────────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ productpage-v1 Pod (default namespace) │
│ ┌───────────────────┐ ┌────────────────────────────────────┐ │
│ │ istio-proxy │ │ │ │
│ │ (Envoy Sidecar) │ │ │ │
│ │ │ │ │ │
│ │ Inbound: │ │ │ │
│ │ 15006 포트 │───▶│ productpage Container │ │
│ │ ↓ │ │ (Python Flask App) │ │
│ │ iptables로 │ │ Port: 9080 │ │
│ │ 9080으로 전달 │ │ "HTML 응답 생성" │ │
│ │ │ │ │ │
│ └───────────────────┘ └────────────────────────────────────┘ │
│ │ │
│ │ 메트릭 수집, 로그 기록, 트레이스 생성 │
│ ↓ │
│ [Prometheus, Jaeger로 전송] │
└─────────────────────────────────────────────────────────────────────┘
# 클러스터 내부에서 테스트
curl -s http://172.30.1.43:30192/productpage | head -20
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Bookstore App</title>
성공! Gateway → VirtualService → productpage 흐름이 정상 작동합니다.
기존 방식 vs Istio:
기존 Kubernetes:
- Deployment replicas 조정으로 비율 제어
- 정밀한 비율 제어 어려움 (예: 95:5 불가능)
- 코드 변경 필요
Istio:
- VirtualService로 정확한 비율 제어
- 코드 변경 없이 YAML만 수정
- 헤더 기반 라우팅 가능
예시: reviews v3로 10% 트래픽 전환
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90 # 90% → v1
- destination:
host: reviews
subset: v3
weight: 10 # 10% → v3 (신규 버전)
사용 시나리오:
1. 새 버전 배포 시 10%만 테스트
2. 에러율 모니터링
3. 문제 없으면 50% → 100% 점진적 증가
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
user:
exact: "jason" # jason 사용자만
route:
- destination:
host: reviews
subset: v2 # v2로 라우팅 (별점 검은색)
- route:
- destination:
host: reviews
subset: v1 # 나머지는 v1 (별점 없음)
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100 # 최대 연결 수
http:
http1MaxPendingRequests: 10
maxRequestsPerConnection: 2
outlierDetection: # 비정상 인스턴스 자동 제외
consecutiveErrors: 5 # 5회 연속 실패 시
interval: 30s # 30초마다 체크
baseEjectionTime: 30s # 30초간 제외
동작:
1. reviews-v2 Pod가 5회 연속 에러 응답
2. Envoy가 해당 Pod를 30초간 로드밸런싱에서 제외
3. 정상 Pod로만 트래픽 전달
4. 30초 후 재시도
기존 방식 vs Istio:
기존 방식:
1. 인증서 생성 (openssl)
2. 각 서비스에 인증서 배포
3. 애플리케이션 코드에서 TLS 설정
4. 인증서 만료 전 수동 갱신
Istio:
1. PeerAuthentication 리소스 생성
2. 끝! (자동 활성화)
Istio mTLS 자동화:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: default
spec:
mtls:
mode: STRICT # 모든 서비스 간 통신 암호화 강제
Citadel (istiod)이 자동 수행:
효과:
productpage → reviews 호출 시
기존:
productpage ──HTTP(평문)──▶ reviews
↑ 스니핑 가능!
Istio mTLS:
productpage ──TLS(암호화)──▶ reviews
istio-proxy ────────────────▶ istio-proxy
↑ 암호화된 통신
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-productpage-to-reviews
spec:
selector:
matchLabels:
app: reviews # reviews 서비스에 대한 접근 제어
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/productpage"]
to:
- operation:
methods: ["GET"] # GET만 허용
효과: productpage만 reviews를 호출 가능, 다른 서비스는 차단
Envoy가 모든 요청에 대해 자동 수집:
Prometheus 메트릭:
- istio_requests_total{destination_service="reviews"}
→ reviews 서비스로의 요청 수
- istio_request_duration_milliseconds{destination_service="reviews"}
→ reviews 응답 시간
- istio_request_bytes_sum
→ 요청 크기
코드 변경 없이 자동 수집!
사용자 요청 하나의 전체 경로 추적:
Request ID: abc123
┌────────────────────────────────────────┐
│ productpage (50ms) │
│ ├─ details (10ms) │
│ ├─ reviews-v2 (30ms) │
│ │ └─ ratings (15ms) ← 병목 발견! │
│ └─ ... │
└────────────────────────────────────────┘
Jaeger UI에서 시각화:
Kiali 대시보드:
┌─────────────┐
│ productpage │
└──────┬──────┘
│
┌───────┼───────┬─────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌───┐ ┌───┐ ┌───┐ ┌───┐
│det│ │rev│ │rev│ │rev│
│ v1│ │ v1│ │ v2│ │ v3│
└───┘ └───┘ └─┬─┘ └─┬─┘
│ │
└────┬────┘
│
▼
┌─────┐
│ rat │
│ v1 │
└─────┘
- 초록색 선: 정상 트래픽
- 빨간색 선: 에러 발생
- 선 굵기: 트래픽 양
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
timeout: 10s # 10초 타임아웃
retries:
attempts: 3 # 3번 재시도
perTryTimeout: 2s # 시도당 2초
retryOn: 5xx # 5xx 에러 시 재시도
효과:
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percentage:
value: 10 # 10% 요청에
fixedDelay: 5s # 5초 지연 주입
route:
- destination:
host: ratings
사용 시나리오:
학습 내용:
1. Kiali 설치 및 활용
Prometheus & Grafana
Jaeger (분산 추적)
실습: 트래픽 시나리오 테스트
개발자는 비즈니스 로직에만 집중
언어 독립적
강력한 관측성
프로덕션급 보안
1. Service Mesh = 마이크로서비스 간 통신 관리 인프라 레이어
2. Istio 아키텍처:
- Control Plane (istiod): 설정 관리
- Data Plane (Envoy): 실제 트래픽 처리
3. Sidecar 패턴:
- 각 Pod에 Envoy Proxy 자동 주입
- 애플리케이션 코드 변경 없음
4. Gateway + VirtualService:
- Gateway: 외부 트래픽 진입점
- VirtualService: 라우팅 규칙
- Selector로 Ingress Gateway Pod 선택
- 이름으로 Gateway와 VirtualService 연결
5. 트래픽 흐름:
NodePort → Service → Envoy (Gateway)
→ VirtualService 라우팅 → 목적지 서비스
→ Envoy (Sidecar) → App
언제 Istio를 도입해야 하나?
✅ 도입 권장:
❌ 도입 불필요:
Istio 도입 단계:
1. Pilot 프로젝트 (1-2개 서비스)
2. Sidecar 주입 검증
3. Gateway 설정 및 트래픽 테스트
4. 관측 도구 설치 (Kiali, Prometheus)
5. 점진적 확대 (서비스별 순차 적용)
다음 포스트에서는 Kiali, Prometheus, Grafana를 설치하고 Istio의 강력한 관측성을 직접 체험해보겠습니다!