์ฟ ๋ฒ๋คํฐ์ค์์ ๋คํธ์ํฌ ํธ๋ํฝ ์ต์ ํ์ ํต์ฌ ๊ธฐ๋ฅ์ธ Topology Aware Routing์ ๋ํด ์ค๋ฌด ๊ด์ ์์ ์์ ์ ๋ณตํ์
Topology Aware Routing(์ด์ ๋ช : Topology Aware Hints)๋ ์ฟ ๋ฒ๋คํฐ์ค 1.23์์ ๋ฒ ํ ๊ธฐ๋ฅ์ผ๋ก ๋์ ๋์์ผ๋ฉฐ, 1.27๋ถํฐ ํ์ฌ์ ์ด๋ฆ์ผ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค. Amazon EKS์์๋ 1.24๋ถํฐ ์ง์ํ๋ ๊ธฐ๋ฅ์ผ๋ก, ๋์ผํ ๊ฐ์ฉ ์์ญ(Availability Zone) ๋ด์์ ํธ๋ํฝ์ ์ ์งํ์ฌ ๋คํธ์ํฌ ์ง์ฐ ์๊ฐ๊ณผ ๋น์ฉ์ ์ค์ด๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค.
ํ์ง๋ง ๋ชจ๋ ํ๊ฒฝ์์ ์๋ํ๋ ๊ฒ์ ์๋๋ฉฐ, ํน์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ํจ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ๊ฐ์ด๋์์๋ ์ด๋ก ์ ์ค๋ช ๋ฟ๋ง ์๋๋ผ ์ค๋ฌด์์ ๋ง์ฃผ์น๋ ๋ค์ํ ์ํฉ๋ค๊ณผ ํด๊ฒฐ์ฑ ์ ์ ์ํฉ๋๋ค.
Topology Aware Routing์ kube-proxy๊ฐ Service์ ์๋ํฌ์ธํธ๋ฅผ ์ ํํ ๋ ํ ํด๋ก์ง ์ ๋ณด๋ฅผ ํ์ฉํ๋๋ก ํฉ๋๋ค.
# Service ์์
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
service.kubernetes.io/topology-mode: "auto"
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
EndpointSlice Controller๋ ๋ค์๊ณผ ๊ฐ์ ๊ณผ์ ์ผ๋ก ํํธ๋ฅผ ํ ๋นํฉ๋๋ค:
hints.forZones ํ๋ ์ถ๊ฐ# EndpointSlice ์์
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: my-service-abc123
endpoints:
- addresses:
- "10.1.2.3"
hints:
forZones:
- name: "zone-a"
kube-proxy๋ ํํธ๊ฐ ์๋ ์๋ํฌ์ธํธ๋ฅผ ์ฐ์ ์ ์ผ๋ก ์ฌ์ฉ:
# iptables ๊ท์น ์์ (์ค์ ๋ก๋ ๋ ๋ณต์ก)
-A KUBE-SVC-XYZ -m comment --comment "zone-a endpoints only" \
-j KUBE-SEP-LOCAL-ZONE
# ๋ถ๊ท ๋ฑ ๋ถ์ฐ ์์ (๋์ํ์ง ์์)
# Zone A: 10 pods
# Zone B: 2 pods
# Zone C: 1 pod
# ๊ฐ๋ฐํ๊ฒฝ์์ ํํ ์ค์
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 1 # โ ํ๋ 1๊ฐ๋ง ์์ผ๋ฉด TAR ๋นํ์ฑํ!
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.kubernetes.io/topology-mode: "auto" # โ ์ค์ ํด๋ ๋ฌด์๋จ
# ๋น์ฉ ์ ์ฝ์ ์ํ 2๋ ์ด์คํ - TAR ์ ํ์
spec:
replicas: 2 # 3๊ฐ zone์์๋ ๋ถ๊ท ๋ฑ ๋ถ์ฐ์ผ๋ก TAR ๋นํ์ฑํ
# ์ค์ ๋ฐฐ์น ์์:
# Zone A: 1๊ฐ ํ๋
# Zone B: 1๊ฐ ํ๋
# Zone C: 0๊ฐ ํ๋ โ ๋น zone์ผ๋ก ์ธํด TAR ๋นํ์ฑํ
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
# ์๋ ๋ชจ๋ (๊ถ์ฅ)
service.kubernetes.io/topology-mode: "auto"
spec:
selector:
app: my-app
ports:
- port: 80
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 6 # zone๋น 2๊ฐ์ฉ ๊ท ๋ฑ ๋ฐฐํฌ
template:
spec:
# Zone ๊ฐ ๊ท ๋ฑ ๋ถ์ฐ์ ์ํ ์ค์
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: my-app
| ์๋๋ฆฌ์ค | ๊ธฐ๋ณธ ์ค์ | Topology Aware Hints | ๊ฐ์ ์จ |
|---|---|---|---|
| ๋์ผ AZ ํต์ | ~1ms | ~0.5ms | 50% |
| ํฌ๋ก์ค AZ ํต์ | ~3-5ms | ~0.5ms | 80-90% |
# AWS ๋ฐ์ดํฐ ์ ์ก ๋น์ฉ ์์
# ํฌ๋ก์ค-AZ: $0.01/GB
# ๋์ผ-AZ: $0.00/GB
# ์ 1TB ๋ฐ์ดํฐ ์ ์ก ์
# ๊ธฐ์กด: $10.24 (ํฌ๋ก์ค-AZ)
# ์ ์ฉ ํ: $0 (๋์ผ-AZ)
# ์ ์ฝ: 100%
# Zone ์ ํ์ธ (๊ฐ์ฅ ์ค์!)
kubectl get nodes -o jsonpath='{.items[*].metadata.labels.topology\.kubernetes\.io/zone}' | tr ' ' '\n' | sort -u | wc -l
# Zone ๋ชฉ๋ก ํ์ธ
kubectl get nodes -o custom-columns="NAME:.metadata.name,ZONE:.metadata.labels['topology\.kubernetes\.io/zone']"
# Zone๋ณ ๋
ธ๋ ๋ถํฌ ํ์ธ
kubectl get nodes -o json | jq -r '.items[] | .metadata.labels["topology.kubernetes.io/zone"]' | sort | uniq -c
์ถ๋ ฅ ์์:
2 ap-northeast-2a
2 ap-northeast-2b
2 ap-northeast-2c
# โ 3๊ฐ zone, TAR ์ ์ฉ ๊ฐ๋ฅ โ
#!/bin/bash
# zone-check.sh - TAR ์ ์ฉ ๊ฐ๋ฅ์ฑ ์๋ ํ๋จ
ZONE_COUNT=$(kubectl get nodes -o jsonpath='{.items[*].metadata.labels.topology\.kubernetes\.io/zone}' | tr ' ' '\n' | sort -u | wc -l)
echo "=== Topology Aware Routing ์ ์ฉ ๊ฐ๋ฅ์ฑ ๋ถ์ ==="
echo "๐ ์ด Zone ์: $ZONE_COUNT"
case $ZONE_COUNT in
1)
echo "โ TAR ์ ์ฉ ๋ถ๊ฐ (Single AZ)"
echo "๐ก ๊ถ์ฅ: ๊ธฐ๋ณธ ๋ก๋๋ฐธ๋ฐ์ฑ ์ฌ์ฉ"
;;
2)
echo "โ ๏ธ TAR ์ ํ์ ์ ์ฉ ๊ฐ๋ฅ"
echo "๐ก ๊ถ์ฅ: ํ๋ 2๊ฐ ์ด์ + ๊ท ๋ฑ ๋ถ์ฐ ํ์"
;;
3)
echo "โ
TAR ์ต์ ํ ๊ฐ๋ฅ"
echo "๐ก ๊ถ์ฅ: ํ๋ ์ต์ 3๊ฐ (zone๋น 1๊ฐ์ฉ)"
echo "๐ ์ด์์ : ํ๋ 9๊ฐ ์ด์ (zone๋น 3๊ฐ์ฉ)"
;;
*)
echo "โ
TAR ์ต์ ํ ๊ฐ๋ฅ"
echo "๐ก ๊ถ์ฅ: ํ๋ ์ต์ ${ZONE_COUNT}๊ฐ (zone๋น 1๊ฐ์ฉ)"
;;
esac
# EndpointSlice ํํธ ํ์ธ
kubectl get endpointslices -o yaml | grep -A5 hints
# ํน์ ์๋น์ค์ EndpointSlice ์์ธ ํ์ธ
kubectl describe endpointslices -l kubernetes.io/service-name=my-service
# Service ์ด๋ฒคํธ ํ์ธ
kubectl describe service my-service
# ํํธ ๊ด๋ จ ๋ก๊ทธ ํ์ธ
kubectl logs -n kube-system -l component=kube-proxy | grep topology
# Pod ๋ถ์ฐ ์ํ ํ์ธ
kubectl get pods -o wide --show-labels | grep my-app
# Zone๋ณ Pod ์ ํ์ธ
kubectl get nodes --show-labels | grep topology.kubernetes.io/zone
# PodDisruptionBudget์ ํตํ ์์ ์ ๋ฐฐํฌ
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
minAvailable: 1
selector:
matchLabels:
app: my-app
# ๋น์ฉ ์ฐ์ โ TAR ๋นํ์ฑํ ๊ถ์ฅ
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 1-2 # ์ต์ ๋ฆฌ์์ค
---
apiVersion: v1
kind: Service
metadata:
annotations:
# TAR ๋ช
์์ ๋นํ์ฑํ (๋ถํ์ํ ์ค๋ฒํค๋ ๋ฐฉ์ง)
service.kubernetes.io/topology-mode: "disabled"
# ์ฑ๋ฅ + ๋น์ฉ ์ ์ฝ โ TAR ์ ๊ทน ํ์ฉ
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 6 # zone ์(3) ร 2 = ๊ท ๋ฑ ๋ถ์ฐ
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
---
apiVersion: v1
kind: Service
metadata:
annotations:
service.kubernetes.io/topology-mode: "auto"
| Zone ์ | ์ต์ ํ๋ ์ | ๊ถ์ฅ ํ๋ ์ | TAR ํจ๊ณผ |
|---|---|---|---|
| 1๊ฐ | N/A | N/A | โ ๋ถ๊ฐ๋ฅ |
| 2๊ฐ | 2๊ฐ | 4๊ฐ ์ด์ | โ ๏ธ ์ ํ์ |
| 3๊ฐ | 3๊ฐ | 9๊ฐ ์ด์ | โ ์ต์ |
| 4๊ฐ+ | zone ์ | zone ร 3 | โ ์ต์ |
# ์๋๋ฆฌ์ค 1: TAR ๋ฏธ์ ์ฉ (2๋ ์ด์คํ)
์ ์ธ์คํด์ค ๋น์ฉ: $200 ร 2 = $400
ํฌ๋ก์ค-AZ ๋ฐ์ดํฐ ์ ์ก: $10-50/์ (ํธ๋ํฝ์ ๋ฐ๋ผ)
์ด๋น์ฉ: $410-450/์
# ์๋๋ฆฌ์ค 2: TAR ์ ์ฉ (3๋ ๋ถ์ฐ)
์ ์ธ์คํด์ค ๋น์ฉ: $200 ร 3 = $600
ํฌ๋ก์ค-AZ ๋ฐ์ดํฐ ์ ์ก: $0-5/์ (๊ฑฐ์ ์ ๊ฑฐ)
์ด๋น์ฉ: $600-605/์
# ROI ๊ณ์ฐ: ๋์ ํธ๋ํฝ์ผ์๋ก TAR์ด ์ ๋ฆฌ!
# ๋์ ๊ฐ์ฉ์ฑ์ด ํ์ํ ๊ฒฝ์ฐ ๋นํ์ฑํ ๊ณ ๋ ค
metadata:
annotations:
service.kubernetes.io/topology-mode: "disabled"
# HPA ์ฌ์ฉ ์ zone ๊ท ํ ์ ์ง ํ์!
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 3 # zone ์์ ๋์ผํ๊ฒ ์ค์
maxReplicas: 12 # zone ์์ ๋ฐฐ์๋ก ์ค์
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web-app
spec:
replicas: 6
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
# ๊ท ๋ฑ ๋ถ์ฐ ๋ณด์ฅ
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web-app
---
apiVersion: v1
kind: Service
metadata:
name: web-service
annotations:
service.kubernetes.io/topology-mode: "auto"
spec:
selector:
app: web-app
ports:
- name: http
port: 80
targetPort: 80
type: ClusterIP
#!/bin/bash
# topology-check.sh
echo "=== Topology Aware Hints ๊ฒ์ฆ ==="
# 1. Service ํํธ ๋ชจ๋ ํ์ธ
echo "1. Service ์ด๋
ธํ
์ด์
ํ์ธ:"
kubectl get service web-service -o jsonpath='{.metadata.annotations}' | jq
# 2. EndpointSlice ํํธ ํ์ธ
echo "2. EndpointSlice ํํธ ํ์ธ:"
kubectl get endpointslices -l kubernetes.io/service-name=web-service -o yaml | grep -A10 hints
# 3. Pod ๋ถ์ฐ ์ํ ํ์ธ
echo "3. Pod zone๋ณ ๋ถ์ฐ ์ํ:"
kubectl get pods -l app=web-app -o wide | awk '{print $7}' | sort | uniq -c
# 4. kube-proxy ๋ก๊ทธ ํ์ธ (์ ํ์ฌํญ)
echo "4. kube-proxy ํ ํด๋ก์ง ๊ด๋ จ ๋ก๊ทธ:"
kubectl logs -n kube-system -l component=kube-proxy --tail=50 | grep -i topology || echo "๋ก๊ทธ ์์"
# ์ฑ๋ฅ ์ธก์ ์ฉ Pod ๋ฐฐํฌ
kubectl run perf-test --image=busybox --rm -it --restart=Never -- sh
# ์๋น์ค ์๋ต ์๊ฐ ์ธก์
for i in {1..100}; do
time wget -qO- http://web-service/health
done | grep real | awk '{sum+=$2} END {print "ํ๊ท :", sum/NR "์ด"}'
Topology Aware Routing๋ ์ฟ ๋ฒ๋คํฐ์ค ํด๋ฌ์คํฐ์ ๋คํธ์ํฌ ์ฑ๋ฅ๊ณผ ๋น์ฉ ํจ์จ์ฑ์ ํฌ๊ฒ ๊ฐ์ ํ ์ ์๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ๋๋ค. ํ์ง๋ง ๋ชจ๋ ํ๊ฒฝ์ ์ ํฉํ ๊ฒ์ ์๋๋ฉฐ, ์ ์คํ ๊ฒํ ๊ฐ ํ์ํฉ๋๋ค.
1. ํด๋ฌ์คํฐ Zone ์๋?
โโ 1๊ฐ Zone โ โ TAR ๋ถ๊ฐ, ๋
โโ 2๊ฐ Zone โ ๋ค์ ๋จ๊ณ
โโ 3๊ฐ+ Zone โ ๋ค์ ๋จ๊ณ
2. ํ๊ฒฝ ์ฑ๊ฒฉ์?
โโ ๊ฐ๋ฐ/ํ
์คํธ โ โ TAR ๋ถํ์ (๋น์ฉ ์ฐ์ )
โโ ํ๋ก๋์
โ ๋ค์ ๋จ๊ณ
3. ํธ๋ํฝ ํน์ฑ์?
โโ ์ ํธ๋ํฝ โ โณ TAR ํจ๊ณผ ๋ฏธ๋ฏธ
โโ ๊ณ ํธ๋ํฝ โ ๋ค์ ๋จ๊ณ
4. ํ๋ ํ์ฅ ๊ฐ๋ฅ์ฑ?
โโ 2๋ ๊ณ ์ โ โ TAR ์ ํ์
โโ 3๋+ ๊ฐ๋ฅ โ โ
TAR ๋์
!
# ๊ฒฐ๋ก : TAR ์ ์ฉ ๋ถํ์
replicas: 1-2
service.kubernetes.io/topology-mode: "disabled"
# ์ด์ : ๋น์ฉ ์ ์ฝ > ์ฑ๋ฅ ์ต์ ํ
# ๊ฒฐ๋ก : TAR ํ
์คํธ ๋ชฉ์ ์ผ๋ก๋ง ์ ์ฉ
replicas: 3-6
service.kubernetes.io/topology-mode: "auto"
# ์ด์ : ํ๋ก๋์
ํ๊ฒฝ ์ฌ์ ๊ฒ์ฆ
# ๊ฒฐ๋ก : ์ ๊ทน์ TAR ๋์
replicas: zone_count ร 3 # ์ต์ ํ
service.kubernetes.io/topology-mode: "auto"
# ์ด์ : ์ฑ๋ฅ + ๋น์ฉ ์ ์ฝ ํจ๊ณผ ๊ทน๋ํ
โ ๋์ ํ๋ฉด ์ข์ ๊ฒฝ์ฐ
โ ๋์ ํ์ง ๋ง์์ผ ํ๋ ๊ฒฝ์ฐ
โ ๏ธ ์ ์คํ ๊ฒํ ํด์ผ ํ๋ ๊ฒฝ์ฐ
๋์ ์ ํ์ธ์ฌํญ:
kubectl get nodes Zone ์ ํ์ธ (3๊ฐ+ ๊ถ์ฅ)๋์ ํ ๊ฒ์ฆ์ฌํญ:
๐ฏ ํต์ฌ ๋ฉ์์ง: "๋ชจ๋ ํ๊ฒฝ์ ์ ์ฉํ๋ ๋ง๋ฅ ํด๊ฒฐ์ฑ ์ด ์๋๋ผ, ์ ์ ํ ์กฐ๊ฑด์์ ํฐ ํจ๊ณผ๋ฅผ ๋ฐํํ๋ ์ ๋ฌธ ๋๊ตฌ"
์ด ๊ธ์ด Topology Aware Hints ์ดํด์ ๋์์ด ๋์๋ค๋ฉด ์ข์์์ ๊ณต์ ๋ถํ๋๋ ค์! ๐