AKS 네트워킹 이해하기: Underlay Network
Source: Dev.to
VM에서 Kubernetes Service IP에 curl을 시도했는데 그냥… 멈춰버린 적이 있다면, 이 가이드는 바로 당신을 위한 것입니다.
다음 내용을 차근차근 살펴보겠습니다:
- AKS 네트워크 설계
- CIDR 레이아웃 (VNet, 서브넷, Service CIDR, Pod CIDR)
- VM에서
ClusterIP가 실패하는 이유 NodePort가 동작하는 이유- 단계별 패킷 흐름
- 전체 Azure CLI 설정
모두 Azure Kubernetes Service (AKS) 를 Microsoft Azure 환경에서 테스트한 결과입니다.
🧱 1️⃣ 네트워크 설계 개요
랩 토폴로지
| 구성 요소 | CIDR |
|---|---|
| VNet | 10.0.0.0/16 |
| AKS 서브넷 | 10.0.1.0/24 |
| VM 서브넷 | 10.0.2.0/24 |
| Service CIDR | 10.240.0.0/16 |
| Overlay Pods (선택 사항) | 192.168.0.0/16 |
언더레이 모드 (Azure CNI)
🗺️ 아키텍처 다이어그램 (PlantUML)
🧠 CIDR 이해
| CIDR | 용도 |
|---|---|
10.0.0.0/16 | Azure VNet |
10.0.1.0/24 | AKS 노드 |
10.0.2.0/24 | 테스트 VM |
10.240.0.0/16 | 쿠버네티스 서비스(가상) |
192.168.0.0/16 | Overlay Pods(활성화된 경우) |
핵심 개념: Service CIDR은 Azure VNet 라우팅에 포함되지 않으므로, VM에서 ClusterIP로 향하는 트래픽은 Azure 라우터에 의해 차단됩니다.
⚙️ 2️⃣ 전체 Azure CLI 설정
변수
LOCATION=eastus2
RG=aks-networking-lab
VNET_NAME=aks-vnet
UNDERLAY_SUBNET=aks-underlay-subnet
VM_SUBNET=vm-subnet
AKS_NAME=aks-underlay
리소스 그룹 만들기
az group create \
--name $RG \
--location $LOCATION
VNet 및 AKS 서브넷 만들기
az network vnet create \
--resource-group $RG \
--name $VNET_NAME \
--address-prefix 10.0.0.0/16 \
--subnet-name $UNDERLAY_SUBNET \
--subnet-prefix 10.0.1.0/24
VM 서브넷 만들기
az network vnet subnet create \
--resource-group $RG \
--vnet-name $VNET_NAME \
--name $VM_SUBNET \
--address-prefix 10.0.2.0/24
서브넷 ID 가져오기 (AKS용)
SUBNET_ID=$(az network vnet subnet show \
--resource-group $RG \
--vnet-name $VNET_NAME \
--name $UNDERLAY_SUBNET \
--query id -o tsv)
AKS 클러스터 만들기
az aks create \
--resource-group $RG \
--name $AKS_NAME \
--network-plugin azure \
--vnet-subnet-id $SUBNET_ID \
--service-cidr 10.240.0.0/16 \
--dns-service-ip 10.240.0.10 \
--node-count 2 \
--generate-ssh-keys
클러스터에 연결하기
az aks get-credentials \
--resource-group $RG \
--name $AKS_NAME
🚀 3️⃣ 테스트 애플리케이션 배포
kubectl create deployment nginx --image=nginx
kubectl scale deployment nginx --replicas=2
ClusterIP 로 노출
kubectl expose deployment nginx \
--name nginx-svc \
--port 80 \
--type ClusterIP
서비스 확인
kubectl get svc
예시 출력
nginx-svc ClusterIP 10.240.225.54 80/TCP
IP (10.240.225.54)는 Service CIDR에서 할당됩니다.
🔥 4️⃣ Packet Flow: ClusterIP (VM 접근이 실패하는 이유)
VM에서 시도해 보세요:
curl 10.240.225.54
Azure 라우팅이 다음을 확인하기 때문에 요청이 멈춥니다:
Is 10.240.0.0/16 in the VNet? → No
→ Drop packet
패킷이 어떤 AKS 노드에도 도달하지 못합니다.
🧭 패킷 흐름 다이어그램
🧪 5️⃣ NodePort 로 변환
kubectl patch svc nginx-svc \
-p '{"spec":{"type":"NodePort"}}'
확인:
kubectl get svc nginx-svc
예시 출력
nginx-svc NodePort 10.240.225.54 80:31598/TCP
이제 서비스는 할당된 노드 포트(예시에서는 31598)를 통해 모든 노드의 IP 주소로 접근할 수 있습니다.
✅ 6️⃣ VM에서 테스트하는 올바른 방법
-
노드의 내부 IP를 확인합니다:
kubectl get nodes -o wide샘플 출력
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME aks-nodepool1-42091994-vmss000000 Ready <none> 34m v1.33.6 10.0.1.33 <none> Ubuntu 22.04.5 LTS 5.15.0-1103-azure containerd://1.7.30-2 aks-nodepool1-42091994-vmss000001 Ready <none> 34m v1.33.6 10.0.1.4 <none> Ubuntu 22.04.5 LTS 5.15.0-1103-azure containerd://1.7.30-2 -
노드 IP와 노드‑포트를 이용해 서비스를 curl 합니다:
curl http://10.0.1.4:31598
기본 Nginx 응답이 반환되면, NodePort 로 노출된 서비스가 VM에서 접근 가능함을 확인한 것입니다.
가이드 종료.
전체 화면 모드 종료
예시
azureuser@test-vm:~$ curl -s 10.0.1.33:31598 | grep -i "welcome"
Welcome to nginx!
nginx에 오신 것을 환영합니다!
## Flow
1. VM → Node IP
2. Node가 트래픽을 수신
3. `kube-proxy`가 NodePort 규칙과 일치
4. Pod IP로 DNAT
5. 응답 반환
## 🧠 깊은 기술 분석
패킷이 노드에 도달하면 `kube-proxy`는 다음과 같은 iptables 규칙을 설치합니다:
```text
KUBE-NODEPORTS
KUBE-SERVICES
KUBE-SEP-XXXX
DNAT 예시
10.0.1.4:31598 → 10.0.1.10:80
왜 ClusterIP가 Pod 내부에서 작동하는가
- 패킷이 먼저 노드에 도달합니다.
kube-proxy가 목적지를 Pod IP로 재작성합니다.
왜 VM에서는 실패하는가
- 패킷이 노드에 도달하지 않습니다.
- Azure 라우팅이 이를 차단합니다.
🎯 주요 요점
- ClusterIP = 가상 내부 Kubernetes IP.
- NodePort = 노드가 실제 VNet IP에서 수신 대기합니다.
- Service CIDR은 VNet CIDR과 겹치면 안 됩니다.
- Azure는 VNet CIDR만 라우팅합니다.
kube-proxy가 Service IP 변환을 처리합니다.
🏁 Final Mental Model
Azure가 담당:
10.0.0.0/16
Kubernetes가 담당:
10.240.0.0/16
다른 라우팅 도메인.

