East-West 트래픽 최적화: 성능과 비용의 균형
개요
Amazon EKS 기반의 내부 서비스 간 통신(East-West 트래픽)을 지연(latency) 최소화와 비용 효율화 관점에서 최적화하는 방 안을 정리합니다. 단일 클러스터에서 시작하여 멀티 AZ(Availability Zone) 구성, 나아가 멀티 클러스터/멀티 계정 환경으로 확장되는 시나리오를 단계적으로 다룹니다.
East-West(서비스↔서비스)의 홉 수가 1 → 2로 늘어나면 p99 지연이 밀리초 단위로 증가하고, AZ를 가로지르면 AWS 대역폭 요금(GB 단가 $0.01)이 발생합니다. 이 가이드는 Kubernetes 네이티브 기능(Topology Aware Routing·InternalTrafficPolicy)부터 Cilium ClusterMesh, AWS VPC Lattice, Istio 서비스 메쉬까지 레이어별 옵션을 분석하고, 지연·오버헤드·비용을 정량 비교합니다.
배경 및 문제점
기본 Kubernetes 네트워킹에서 East-West 트래픽이 직면하는 문제점은 다음과 같습니다:
- AZ 인식 부재: 기본 ClusterIP 서비스는 클러스터 전체 Pod에 트래픽을 랜덤(iptables) 또는 라운드로빈(IPVS) 분산시키며 AZ를 고려하지 않습니다
- 불필요한 Cross-AZ 트래픽: Pod가 여러 AZ에 분산되면 트래픽이 무작위로 타 AZ로 전달되어 지연 증가 및 비용 발생
- Cross-AZ 데이터 전송 비용: 동일 리전 내 AZ 간 GB당 약 $0.01이 양방향으로 부과
- DNS 조회 지연: 중앙화된 CoreDNS로의 교차 AZ DNS 조회 및 QPS 한도 초과 이슈
- LB 경유 시 추가 홉: Internal ALB/NLB를 East-West에 사용하면 불필요한 네트워크 홉과 고정비용 발생
핵심 이점
이 가이드의 최적화 전략을 적용하면 다음과 같은 개선을 기대할 수 있습니다:
| 항목 | 개선 효과 |
|---|---|
| 네트워크 지연 | Topology Aware Routing으로 동일 AZ 라우팅, p99 sub-ms 달성 |
| 비용 절감 | Cross-AZ 트래픽 제거 시 10 TB/월 기준 약 $100 절감 |
| 운영 단순화 | ClusterIP 기반으로 LB 없이 서비스 간 통신 최적화 |
| DNS 성능 | NodeLocal DNSCache로 DNS 조회 지연 수ms → sub-ms |
| 확장성 | 멀티 클러스터/계정 환경으로의 일관된 확장 경로 제공 |
L4 vs L7 트래픽별 최적화 전략
East-West 트래픽 최적화는 전송 계층(L4)과 애플리케이션 계층(L7)에서 다르게 접근합니다:
-
L4 트래픽(TCP/UDP): 추가적인 프로토콜 처리 없이 직접적인 연결 경로를 확보하는 것이 핵심입니다. 불필요한 프록시나 로드밸런서를 경유하지 않고 Pod 간 1-hop 통신이 이루어지도록 설계하면 지연을 최소화할 수 있습니다. 데이터베이스와 같은 StatefulSet 서비스에는 Headless Service를 통해 클라이언트가 DNS 라운드로빈으로 직접 대상 Pod에 연결하는 패턴이 적합합니다.
-
L7 트래픽(HTTP/gRPC): 내용 기반 라우팅, 리트라이 등의 고급 트래픽 제어가 필요하면 애플리케이션 계층 프록시를 활용합니다. ALB나 Istio 사이드카를 이용하면 경로 기반 라우팅, gRPC 메서드별 라우팅, 서킷 브레이커 등 L7 기능을 적용할 수 있습니다. 다만 L7 프록시는 패킷 검사와 처리로 부하와 지연이 증가하므로, 단순 트래픽에는 과도한 요소가 될 수 있습니다.
사전 요구사항
필수 지식
- Kubernetes 네트워킹 기본 개념 (Service, Endpoint, kube-proxy)
- AWS VPC 네트워킹 (Subnet, AZ, ENI)
- DNS 해석 메커니즘 (CoreDNS, /etc/resolv.conf)
필요한 도구
| 도구 | 버전 | 용도 |
|---|---|---|
| kubectl | 1.27+ | 클러스터 리소스 관리 |
| eksctl | 0.170+ | EKS 클러스터 생성 및 관리 |
| AWS CLI | 2.x | AWS 리소스 확인 |
| Helm | 3.12+ | 차트 배포 (NodeLocal DNSCache 등) |
| AWS Load Balancer Controller | 2.6+ | ALB/NLB 연동 (필요 시) |
환경 요구사항
| 항목 | 요구사항 |
|---|---|
| EKS 버전 | 1.27+ (Topology Aware Routing 지원) |
| VPC CNI | v1.12+ 또는 Cilium (ClusterMesh 시나리오) |
| AZ 구성 | 동일 리전 내 최소 2개 AZ |
| IAM 권한 | EKS 클러스터 관리자, ELB 생성/관리 권한 |
아키텍처
아키텍처 개요: 단일 클러스터 트래픽 경로 비교
아래 다이어그램은 ClusterIP와 Internal ALB 경로의 차이를 보여줍니다:
- ClusterIP 경로: Pod → kube-proxy (iptables/IPVS NAT) → target Pod (1 hop)
- Internal ALB 경로: Pod → AZ-local ALB ENI → target Pod (2 hops)
- Topology Aware Routing 적용 시 ClusterIP 경로는 동일 AZ 내에서 완결됩니다
멀티 클러스터 연결 옵션 비교
Kubernetes 서비스 유형별 비교
서비스 간 통신을 어떻게 연결하느냐에 따라 성능과 비용에 차이가 있습니다:
- 기본 선택: ClusterIP + Topology Aware Routing
- StatefulSet: Headless 서비스
- L7 기능 필요 시: Internal ALB (IP 모드)
- L4 외부 노출 필요 시: Internal NLB (IP 모드)
Instance 모드 vs IP 모드
Internal LB 사용 시 Instance 모드와 IP 모드의 차이를 이해하는 것이 중요합니다:
- Instance 모드: LB → NodePort → kube-proxy → Pod. NodePort를 받은 노드의 kube-proxy가 대상 Pod이 위치한 다른 AZ의 노드로 패킷을 전달하면서 교차 AZ 통신이 발생합니다
- IP 모드: LB → Pod IP 직접 연결. 각 AZ에서 Pod IP로 직접 트래픽을 전달하기 때문에 중간 Node를 거 치지 않고 동일 AZ의 Pod으로 연결됩니다
Instance 모드에서는 NodePort 경유로 cross-AZ 트래픽이 증가합니다. AWS 모범사례는 내부 LB 사용 시 가능하면 IP 모드로 설정하여 불필요한 AZ 간 트래픽을 줄일 것을 권장합니다. IP 모드를 사용하려면 AWS Load Balancer Controller가 필요합니다.
아키텍처 의사결정
왜 ClusterIP를 기본으로 선택하는가?
- 네이티브 Kubernetes 기능으로 추가 비용 없음
- 1-hop 통신으로 최저 지연
- Topology Aware Routing과 결합하여 AZ 인식 가능
- 서비스 메쉬, Gateway API와의 통합 용이
왜 Internal ALB는 선택적으로 사용하는가?
- 시간당 비용($0.0225/h) + LCU 과금이 지속 발생
- 추가 네트워크 홉으로 2-3ms RTT 오버헤드
- EC2→EKS 마이그레이션 등 과도기적 사용에 적합
구현
단계 1: Topology Aware Routing 활성화
멀티 AZ 환경에서 지연과 비용을 줄이는 핵심은 트래픽이 가능한 한 동일 AZ 내에서 처리되도록 하는 것입니다. Kubernetes 1.27+ 버전에서 Topology Aware Routing을 활성화하면, EndpointSlice에 각 엔드포인트의 AZ 정보(hints)가 기록되고 kube-proxy가 클라이언트와 같은 Zone의 Pod으로만 트래픽을 라우팅합니다.
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: production
annotations:
# Topology Aware Routing 활성화
service.kubernetes.io/topology-mode: Auto
spec:
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
검증:
# EndpointSlice에 topology hints가 설정되었는지 확인
kubectl get endpointslices -l kubernetes.io/service-name=my-service -o yaml
# 출력에서 hints 필드 확인
# hints:
# forZones:
# - name: ap-northeast-2a
- 각 AZ에 충분한 엔드포인트가 존재해야 합니다
- Pod가 특정 AZ에만 치우쳐 있으면 해당 서비스는 힌트를 비활성화하고 전체로 라우팅합니다
- EndpointSlice 컨트롤러가 AZ별 Pod 비율이 균등하지 않다고 판단하면 hints가 생성되지 않습니다
단계 2: InternalTrafficPolicy Local 설정
Topology Aware Routing보다 범위를 더 좁힌 기능으로, 동일 노드(Local Node)에 구동 중인 엔드포인트에만 트래픽을 전달합니다. 노드 간(당연히 AZ 간) 네트워크 홉이 완전히 제거되어 지 연이 최소화되고 Cross-AZ 비용도 0에 수렴합니다.
apiVersion: v1
kind: Service
metadata:
name: my-local-service
namespace: production
spec:
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
type: ClusterIP
# 동일 노드의 엔드포인트로만 트래픽 전달
internalTrafficPolicy: Local
로컬 노드에 대상 Pod이 하나도 없는 경우 트래픽이 드롭됩니다. 이 정책을 사용하는 서비스는 모든 노드(혹은 최소 해당 서비스 호출이 발생하는 노드)에 적어도 하나 이상의 Pod가 배치되어야 합니다. Pod Topology Spread 또는 PodAffinity를 반드시 함께 사용하세요.
두 기능은 동시에 사용할 수 없으며 선택적으로 적용해야 합니다:
- 멀티 AZ 환경: 우선 AZ 단위 분산을 보장하는 Topology Aware Routing 고려
- 같은 노드 내 빈번한 호출: 짝을 이루는 파드들 간 강한 결합 통신에 InternalTrafficPolicy(Local) + Pod 공배치 활용
단계 3: Pod Topology Spread Constraints
토폴로지 기반 최적화의 효과를 얻으려면 애플리케이션 복제본의 배치 전략이 중요합니다. Topology Aware Routing이 제대로 동작하려면 각 AZ에 충분한 엔드포인트가 존재해야 합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 6
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
# AZ별 균등 분산
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: my-app
# 노드별 분산 (선택사항)
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: my-app
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
Pod Affinity를 이용한 공동 배치(co-location):
자주 통신하는 서비스 A와 B를 동일 노드 또는 동일 AZ에 배치하도록 PodAffinity 규칙을 적용할 수 있습니다:
spec:
affinity:
podAffinity:
# 서비스 B가 있는 노드에 우선 배치
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: service-b
topologyKey: topology.kubernetes.io/zone
HPA로 스케일 아웃할 때는 Spread Constraints에 따라 새 파드를 퍼뜨릴 수 있지만, 스케일 인 시에는 컨트롤러가 AZ 균형을 고려하지 않고 임의의 파드를 제거하기 때문에 균형이 무너질 수 있습니다. Descheduler를 사용해 불균형 발생 시 재조정하는 것을 권장합니다.
단계 4: NodeLocal DNSCache 배포
DNS 조회 지연과 실패는 마이크로서비스 환경에서 예상 외로 지연을 증가시키는 요소가 될 수 있습니다. NodeLocal DNSCache는 각 노드에 DNS 캐시 에이전트를 DaemonSet으로 구동하여 DNS 응답시간을 크게 단축합니다.
# NodeLocal DNSCache 매니페스트 다운로드 및 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml
또는 Helm 차트를 사용합니다:
helm repo add deliveryhero https://charts.deliveryhero.io/
helm install node-local-dns deliveryhero/node-local-dns \
--namespace kube-system \
--set config.localDnsIp=169.254.20.10
NodeLocal DNSCache 동작 원리:
# 각 Pod의 /etc/resolv.conf가 로컬 캐시로 향하게 설정
# nameserver 169.254.20.10 (NodeLocal DNS IP)
# 자주 조회되는 DNS 질의를 노드 내부에서 캐싱
효과:
- p99 DNS lookup 지연: 수ms → sub-ms
- CoreDNS QPS 부하 완화
- 1만 개 이상 Pod 환경에서 DNS 대기시간 수십ms 절약
- 교차 AZ DNS 요금 감소
AWS 공식 블로그에서는 노드 수가 많은 클러스터에서 NodeLocal DNSCache 사용을 권장하며 CoreDNS 스케일아웃과 함께 활용하라고 조언합니다. 워크로드 규모에 따라 노드당 추가 데몬의 리소스 소모(CPU/메모리)를 고려하여 적용하세요.
단계 5: Internal LB IP 모드 구성 (필요 시)
L7 기능이 필요하거나 EC2→EKS 마이그레이션 과도기에는 Internal ALB를 IP 모드로 구성합니다:
Internal NLB (IP 모드):
apiVersion: v1
kind: Service
metadata:
name: my-service-nlb
namespace: production
annotations:
# AWS Load Balancer Controller 사용
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internal
# Cross-Zone LB 비활성화 (AZ 로컬 트래픽 유지)
service.beta.kubernetes.io/aws-load-balancer-attributes: load_balancing.cross_zone.enabled=false
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
Internal ALB (Ingress 리소스):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-service-alb
namespace: production
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internal
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/healthcheck-path: /health
spec:
rules:
- host: my-service.internal
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
단계 6: Istio 서비스 메쉬 (선택적)
보안 요구사항(mTLS, Zero-Trust)이 있거나 고급 트래픽 관리가 필요한 경우 Istio를 선택적으로 도입 합니다.
어떤 서비스 메시를 선택할지(Istio·Cilium·Linkerd·VPC Lattice)는 서비스 메시 비교 가이드에서 다룹니다. 본 문서는 도입 후 지연·비용 최적화 관점에 집중합니다.
Istio의 주요 이점:
- Locality 기반 라우팅: Envoy 사이드카 간 로컬리티 정보를 활용하여 동일 AZ 또는 동일 지역의 인스턴스로 라우팅
- 투명한 mTLS: 애플리케이션 코드 수정 없이 Mutual TLS 암호화
- 고급 트래픽 관리: 리트라이, 타임아웃, 서킷브레이커, 카나리 배포
성능 오버헤드 (Istio 1.30 기준):
| 메트릭 | 수치 |
|---|---|
| 사이드카당 CPU | ~0.2 vCPU (1000 rps 기준) |
| 사이드카당 메모리 | ~60 MB (1000 rps 기준) |
| 추가 지연 (p99) | ~5ms (클라이언트+서버 2회 프록시 경유) |
| 성능 영향 | 평균 5~10% 처리량 감소 |
- 사이드카 리소스 소모로 EC2 비용 상승 가능
- mTLS 활성화 시 CPU 사용량 추가 증가
- 컨트롤 플레인(Istiod) 관리, CRD(VirtualService, DestinationRule) 학습 필요
- 디버깅 난이도 상승 (사이드카, 컨트롤 플레인까지 추적)
- 지연 민감도가 매우 높은 서비스에는 메쉬 적용을 신중히 결정
# Istio Locality Load Balancing 설정 예시
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-service
spec:
host: my-service.production.svc.cluster.local
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
connectionPool:
tcp:
maxConnections: 100
http:
h2UpgradePolicy: DEFAULT
maxRequestsPerConnection: 10
멀티 클러스터 연결 전략
서비스가 여러 클러스터 또는 여러 AWS 계정에 분산될 경우, 클러스터 간 연결 전략이 필요합니다.
Cilium ClusterMesh
Cilium ClusterMesh는 CNI인 Cilium이 제공하는 멀티 클러스터 네트워킹 기능으로, 여러 클러스터를 하나의 네트워크처럼 묶어줍니다. 별도의 게이트웨이나 프록시를 경유하지 않고 eBPF 기반으로 Pod-to-Pod 직접 통신이 가능합니다.
# ClusterMesh 활성화 (Cilium CLI)
cilium clustermesh enable --context cluster1
cilium clustermesh enable --context cluster2
# 클러스터 연결
cilium clustermesh connect --context cluster1 --destination-context cluster2
# 상태 확인
cilium clustermesh status --context cluster1
장점: 가장 낮은 지연, 추가 요청당 비용 없음, 투명한 서비스 발견 단점: 모든 클러스터가 Cilium CNI 필수, Cilium 운영 지식 필요
AWS VPC Lattice
Amazon VPC Lattice는 완전관리형 애플리케이션 네트워킹 서비스로, 여러 VPC와 계정에 걸쳐 일관된 서비스 연결, IAM 기반 인증, 모니터링을 제공합니다.
# Kubernetes Gateway API를 통한 Lattice 연동
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: my-lattice-gateway
annotations:
application-networking.k8s.aws/lattice-vpc-association: "true"
spec:
gatewayClassName: amazon-vpc-lattice
listeners:
- name: http
protocol: HTTP
port: 80
비용 구조: 서비스당 $0.025/시간 + $0.025/GB + 100만 요청당 $0.10 적합한 경우: 수십 개 이상의 마이크로서비스가 여러 계정에 분산, 중앙 보안 통제 필요
Istio 멀티클러스터 메쉬
이미 Istio를 사용하고 있다면 멀티클러스터 서비스 메쉬로 확장할 수 있습니다. Flat network 환경에서는 Envoy-to-Envoy 직통 통신이 가능하고, 분리된 네트워크에서는 East-West Gateway를 경유합니다.
장점: 서비스 메쉬 전 기능을 클러스터 경계 넘어 활용, 글로벌 mTLS, 클러스터 간 페일오버 단점: 4가지 옵션 중 운영 복잡도 최고, 인증서 관리/사이드카 동기화 등 과제
Route53 + ExternalDNS
가장 단순한 멀티클러스터 연결 방법으로, 각 클러스터의 서비스를 Route53 Private Hosted Zone에 등록하고 DNS로 접근합니다.
# ExternalDNS 설정 예시
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
external-dns.alpha.kubernetes.io/hostname: my-service.internal.example.com
spec:
type: LoadBalancer
...
적합한 경우: 클러스터 2-3개, 서비스 호출이 빈번하지 않은 경우, DR 구성