Hub-Spoke는 방화벽, VPN Gateway 같은 공용 네트워크 자원을 Hub VNet 한 곳에 모으고, 워크로드는 Spoke VNet으로 나눠 VNet Peering으로 잇는 구조입니다. Peering은 전이되지 않기 때문에 Spoke끼리의 통신과 인터넷 출구는 UDR로 Hub의 Azure Firewall을 거치게 만드는 것이 핵심입니다.
1. Hub-Spoke란
지난 글에서 VNet 하나에 웹과 DB 서브넷을 두는 기본 구성을 정리했습니다. 서비스가 늘어나면 VNet마다 방화벽과 VPN Gateway를 따로 두게 되는데, 이렇게 하면 비용이 VNet 수만큼 늘고 보안 정책도 여러 곳에서 따로 관리해야 합니다.
Hub-Spoke(허브 앤 스포크)는 이 문제를 공용 자원을 한곳에 모으는 방식으로 풉니다. 가운데 Hub VNet에는 Azure Firewall, VPN Gateway, Azure Bastion처럼 모두가 함께 쓰는 자원을 두고, 서비스별 Spoke VNet에는 워크로드만 둡니다. Microsoft Azure 아키텍처 센터에서도 가장 기본이 되는 네트워크 참조 아키텍처로 소개하고 있습니다.
2. 구성 요소

Hub VNet에 공용 자원을 모으고 Spoke VNet을 Peering으로 연결한 구성
| 구성 요소 | 위치 | 역할 |
| Azure Firewall | Hub AzureFirewallSubnet |
Spoke 간 통신과 인터넷 출구 트래픽을 검사합니다. 서브넷 이름이 고정이고 /26 이상이어야 합니다. |
| VPN Gateway | Hub GatewaySubnet |
온프레미스와 Site-to-Site VPN으로 연결합니다. 모든 Spoke가 이 게이트웨이를 함께 씁니다. |
| Azure Bastion | Hub AzureBastionSubnet |
공용 IP 없이 브라우저로 Spoke의 VM에 RDP, SSH로 접속합니다. /26 이상이 필요합니다. |
| Spoke VNet | 서비스별 | 워크로드만 둡니다. 서비스, 환경, 팀 단위로 나누고 다른 구독에 있어도 됩니다. |
| VNet Peering | Hub ↔ Spoke | 두 VNet을 Microsoft 백본으로 연결합니다. 방향마다 하나씩, 양쪽에 만들어야 합니다. |
| 라우트 테이블(UDR) | Spoke 서브넷 | 0.0.0.0/0 트래픽의 다음 홉을 Azure Firewall 사설 IP로 바꿉니다. |
주소 대역은 처음에 넉넉하게 나눠 두는 것이 좋습니다. Peering으로 연결할 VNet끼리는 주소 대역이 겹치면 안 되고, 온프레미스 대역과도 겹치지 않아야 하기 때문입니다. 이 글에서는 Hub를 10.0.0.0/16, Spoke를 10.1.0.0/16, 10.2.0.0/16으로 나눴습니다.
3. 트래픽 흐름

Spoke1의 VM이 Hub의 Azure Firewall을 거쳐 Spoke2로 가는 경로
Hub-Spoke에서 가장 헷갈리는 부분은 Peering이 전이되지 않는다는 점입니다. Spoke1과 Hub, Hub와 Spoke2가 각각 Peering되어 있어도 Spoke1에서 Spoke2로 바로 갈 수는 없습니다. Peering은 연결된 두 VNet 사이에서만 동작하기 때문입니다.
그래서 Spoke 서브넷에 라우트 테이블을 붙여 0.0.0.0/0의 다음 홉을 Azure Firewall(10.0.1.4)로 지정합니다. 이렇게 하면 Spoke1에서 나간 패킷이 먼저 방화벽으로 가고, 방화벽 규칙이 허용하면 방화벽이 Spoke2로 다시 보내 줍니다. 자기 VNet 안의 통신은 VNet 기본 경로가 더 구체적이라 그대로 VNet 안에서 처리됩니다.
| Spoke 간 통신 방식 | 장점 | 단점 |
| Hub 방화벽 경유 UDR |
모든 트래픽을 한곳에서 검사하고 로그를 남깁니다. | 방화벽을 한 번 더 거치고 처리 요금이 붙습니다. |
| Spoke끼리 직접 Peering | 지연이 적고 방화벽 처리 요금이 없습니다. | 검사를 건너뛰고, Spoke가 늘면 Peering 수가 빠르게 늘어납니다. |
| Virtual Network Manager 연결 그룹 |
Peering을 일일이 만들지 않고 정책으로 관리합니다. | 서비스를 하나 더 익혀야 하고, 별도 제한 사항을 확인해야 합니다. |
저는 기본값은 방화벽 경유로 두고, DB 복제처럼 지연에 민감하고 위험이 낮은 트래픽만 직접 Peering을 추가하는 쪽을 권합니다. Microsoft 아키텍처 센터도 같은 방향을 안내합니다.
4. Peering 설정
Peering은 방향마다 옵션이 다릅니다. Hub 쪽에서는 게이트웨이를 빌려 주고, Spoke 쪽에서는 그 게이트웨이를 빌려 쓰는 설정을 켭니다.
| 설정 | Hub → Spoke | Spoke → Hub |
| 원격 VNet 접근 허용 allowVirtualNetworkAccess |
허용 | 허용 |
| 전달된 트래픽 허용 allowForwardedTraffic |
허용 | 허용 |
| 게이트웨이 전송 허용 allowGatewayTransit |
허용 | - |
| 원격 게이트웨이 사용 useRemoteGateways |
- | 사용 |
원격 게이트웨이 사용은 Hub에 VPN Gateway가 실제로 배포된 뒤에만 켤 수 있습니다. 게이트웨이 없이 켜면 Peering 생성이 실패하므로, 게이트웨이를 나중에 만든다면 그때 Peering을 수정합니다.
5. Azure CLI로 만들어 보기
Hub, Spoke 2개, Azure Firewall, Peering, 라우트 테이블까지 CLI로 만드는 순서입니다. VPN Gateway와 Bastion은 배포 시간과 비용이 커서 서브넷만 만들어 둡니다.
5.1 VNet과 서브넷
RG=rg-hub-spoke
LOC=koreacentral
az group create --name $RG --location $LOC
# Hub VNet: 서브넷 이름은 Azure가 정한 고정 이름을 써야 합니다
az network vnet create --resource-group $RG --name vnet-hub \
--address-prefixes 10.0.0.0/16 \
--subnet-name AzureFirewallSubnet --subnet-prefixes 10.0.1.0/26
az network vnet subnet create --resource-group $RG --vnet-name vnet-hub \
--name GatewaySubnet --address-prefixes 10.0.0.0/27
az network vnet subnet create --resource-group $RG --vnet-name vnet-hub \
--name AzureBastionSubnet --address-prefixes 10.0.2.0/26
# Spoke VNet 2개
for i in 1 2; do
az network vnet create --resource-group $RG --name vnet-spoke$i \
--address-prefixes 10.$i.0.0/16 \
--subnet-name snet-app --subnet-prefixes 10.$i.1.0/24
done
5.2 Azure Firewall과 정책
az extension add --name azure-firewall
# 방화벽 정책과 Spoke 간 통신 허용 규칙
az network firewall policy create --resource-group $RG --name fwp-hub
az network firewall policy rule-collection-group create \
--resource-group $RG --policy-name fwp-hub \
--name rcg-network --priority 200
az network firewall policy rule-collection-group collection add-filter-collection \
--resource-group $RG --policy-name fwp-hub --rcg-name rcg-network \
--name allow-spoke-to-spoke --collection-priority 100 --action Allow \
--rule-type NetworkRule --rule-name spoke1-spoke2 \
--source-addresses 10.1.0.0/16 10.2.0.0/16 \
--destination-addresses 10.1.0.0/16 10.2.0.0/16 \
--ip-protocols Any --destination-ports '*'
# 방화벽 배포
az network public-ip create --resource-group $RG --name pip-fw --sku Standard
az network firewall create --resource-group $RG --name fw-hub --firewall-policy fwp-hub
az network firewall ip-config create --resource-group $RG --firewall-name fw-hub \
--name fw-config --public-ip-address pip-fw --vnet-name vnet-hub
FW_IP=$(az network firewall show --resource-group $RG --name fw-hub \
--query "ipConfigurations[0].privateIPAddress" -o tsv)
echo $FW_IP # AzureFirewallSubnet의 첫 사용 가능 주소, 이 구성에서는 10.0.1.4
5.3 VNet Peering
HUB_ID=$(az network vnet show --resource-group $RG --name vnet-hub --query id -o tsv)
for i in 1 2; do
SPOKE_ID=$(az network vnet show --resource-group $RG \
--name vnet-spoke$i --query id -o tsv)
# Hub → Spoke (VPN Gateway가 있으면 --allow-gateway-transit 추가)
az network vnet peering create --resource-group $RG --vnet-name vnet-hub \
--name hub-to-spoke$i --remote-vnet $SPOKE_ID \
--allow-vnet-access --allow-forwarded-traffic
# Spoke → Hub (VPN Gateway가 있으면 --use-remote-gateways 추가)
az network vnet peering create --resource-group $RG --vnet-name vnet-spoke$i \
--name spoke$i-to-hub --remote-vnet $HUB_ID \
--allow-vnet-access --allow-forwarded-traffic
done
5.4 라우트 테이블
# 게이트웨이 경로 전파를 끄고 0.0.0.0/0을 방화벽으로 보냅니다
az network route-table create --resource-group $RG --name rt-spoke \
--disable-bgp-route-propagation true
az network route-table route create --resource-group $RG --route-table-name rt-spoke \
--name to-firewall --address-prefix 0.0.0.0/0 \
--next-hop-type VirtualAppliance --next-hop-ip-address $FW_IP
for i in 1 2; do
az network vnet subnet update --resource-group $RG \
--vnet-name vnet-spoke$i --name snet-app --route-table rt-spoke
done
게이트웨이 경로 전파를 끄는 이유는 VPN으로 들어온 온프레미스 경로가 Spoke 서브넷에 전파되어 방화벽을 우회하지 않게 하기 위해서입니다. 온프레미스에서 Spoke로 들어오는 방향도 검사하려면 GatewaySubnet에 Spoke 대역을 방화벽으로 보내는 라우트 테이블을 따로 붙입니다.
5.5 확인
Spoke에 VM을 하나씩 만든 뒤, NIC의 유효 경로를 보면 UDR이 적용됐는지 확인할 수 있습니다.
# 0.0.0.0/0 → VirtualAppliance 10.0.1.4 (Source: User)가 보이면 정상
az network nic show-effective-route-table --resource-group $RG \
--name <VM NIC 이름> -o table
# 실습이 끝나면 리소스 그룹째 삭제 (Azure Firewall은 켜 둔 시간만큼 요금이 나갑니다)
az group delete --name $RG --yes --no-wait
Spoke1의 VM에서 Spoke2의 VM으로 ping이나 SSH를 보내고, Azure Firewall 로그에서 허용 기록이 남는지 확인하면 경로가 방화벽을 거친다는 것까지 확인할 수 있습니다.
6. 운영 시 주의점
| 항목 | 주의할 점 |
| 인터넷 출구 | 0.0.0.0/0을 방화벽으로 보내면 인터넷으로 나가는 트래픽도 방화벽을 거칩니다. 패키지 업데이트 같은 외부 통신은 애플리케이션 규칙으로 따로 허용해야 합니다. |
| Peering 개수 | VNet 하나에 만들 수 있는 Peering은 기본 500개입니다. Spoke가 많아지면 Virtual Network Manager를 검토합니다. |
| 리전 | 리전마다 Hub를 하나씩 두고, 같은 리전의 Spoke만 연결하는 것이 기본 권장입니다. |
| 비용 | Azure Firewall은 배포 시간과 처리한 데이터 양에 요금이 붙고, Peering은 양쪽에서 GB당 요금이 붙습니다. 가격은 Azure 가격 계산기로 확인합니다. |
| DNS | 방화벽 FQDN 규칙을 쓸 때는 Spoke와 방화벽이 같은 DNS를 쓰게 맞춥니다. 다르면 정상 트래픽이 막힐 수 있습니다. |
| NSG | AzureFirewallSubnet에는 NSG를 붙일 수 없습니다. Spoke 서브넷의 NSG는 방화벽과 별개로 그대로 동작합니다. |
7. 정리
- Hub에는 Azure Firewall, VPN Gateway, Bastion 같은 공용 자원을 모으고, Spoke에는 워크로드만 둡니다.
- VNet Peering은 전이되지 않으므로 Spoke 간 통신은 UDR로 Hub 방화벽을 거치게 합니다.
- Peering 옵션은 방향마다 다르게 켜고, 주소 대역은 처음부터 겹치지 않게 나눕니다.
참고
'Cloud > Azure' 카테고리의 다른 글
| [Azure] Azure 기본 구조 정리하기 (0) | 2026.09.24 |
|---|