🎉
複数の Hub-spoke アーキテクチャで spoke-to-spoke を実現する (VNet Peering + VPN 利用)
複数の Hub-spoke を VNet Peering + VPN で spoke-to-spoke を実現する
今回は hub-spoke の hub 同士を VNet Peering で接続してしまい、そのうえで Private IP を使用した VPN で接続します。
この方法でも spoke-to-spoke の接続を確立することができます。
アーキテクチャの概要図はこのような感じです。
メリットとしては Internet VPN 版と異なり、完全に閉域での接続となっています。
latency については後程別途比較してみようかと思いますが、論理的には最短距離でつなげるはずです。
デメリットとしては他と同じく VPN Gateway の SKU により帯域が制限されます。
具体的にどのような結果となるかは後程 microsoft/ethr を試した結果を追加します。
イメージとしては spoke - hub まではそれなりに帯域が広いですが、spoke - hub - hub まで来るとガクッと落ちます。
Bicep ファイルはこちらです。
main.bicep
main.bicep
param location01 string = 'eastasia'
param location02 string = 'eastasia'
param kvName string
param kvRGName string
param secretName string
param s2svpnSecretName string
param isInitialDeploy bool = false
var useExisting = !isInitialDeploy
resource kv 'Microsoft.KeyVault/vaults@2021-10-01' existing = {
name: kvName
scope: resourceGroup(kvRGName)
}
resource hub00 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-hub00'
location: location01
properties: {
addressSpace: {
addressPrefixes: [
'10.0.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.0.0.0/24'
}
}
{
name: 'AzureBastionSubnet'
properties: {
addressPrefix: '10.0.100.0/24'
}
}
{
name: 'GatewaySubnet'
properties: {
addressPrefix: '10.0.200.0/24'
}
}
]
}
resource defaultsubnet 'subnets' existing = {
name: 'default'
}
}
resource spoke10 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-spoke10'
location: location01
properties: {
addressSpace: {
addressPrefixes: [
'10.10.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.10.0.0/24'
}
}
]
}
resource defaultsubnet 'subnets' existing = {
name: 'default'
}
}
module peering_hub0010 '../lib/vnet-peering.bicep' = {
name: 'peering-hub00-spoke10'
params: {
vnet01Name: hub00.name
vnet02Name: spoke10.name
useRemoteGateways: true
remoteGatewayName: vpngw01.outputs.vpngwName
}
}
resource spoke20 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-spoke20'
location: location01
properties: {
addressSpace: {
addressPrefixes: [
'10.20.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.20.0.0/24'
}
}
]
}
resource defaultsubnet 'subnets' existing = {
name: 'default'
}
}
module peering_hub0020 '../lib/vnet-peering.bicep' = {
name: 'peering-hub00-spoke20'
params: {
vnet01Name: hub00.name
vnet02Name: spoke20.name
useRemoteGateways: true
remoteGatewayName: vpngw01.outputs.vpngwName
}
}
var vpngw01Name = 'vpngw-hub01'
module vpngw01 '../lib/vpngw_single.bicep' = {
name: vpngw01Name
params: {
location: location01
vnetName: hub00.name
gatewayName: vpngw01Name
bgpAsn: 65150
enablePrivateIpAddress: true
useExisting: useExisting
}
}
module conn_vpngw01_vpngw02 '../lib/connection-vpngw.bicep' = {
name: 'conn-${vpngw01Name}-${vpngw02Name}'
params: {
vpnGateway01Name: vpngw01.outputs.vpngwName
vpnGateway02Name: vpngw02.outputs.vpngwName
psk: kv.getSecret(s2svpnSecretName)
enableBgp: true
enablePrivateIpAddress: true
}
}
var bast01Name = 'bast-hub00'
module bast01 '../lib/bastion.bicep' = {
name: bast01Name
params: {
location: location01
bastionName: bast01Name
vnetName: hub00.name
}
}
var vm01Name = 'vm-hub00'
module vm_loc01 '../lib/ws2019.bicep' = {
name: vm01Name
params: {
location: location01
adminPassword: kv.getSecret(secretName)
subnetId: hub00::defaultsubnet.id
vmName: vm01Name
}
}
var vm02Name = 'vm-spoke10'
module vm_spoke01 '../lib/ws2019.bicep' = {
name: vm02Name
params: {
location: location01
adminPassword: kv.getSecret(secretName)
subnetId: spoke10::defaultsubnet.id
vmName: vm02Name
}
}
var vm03Name = 'vm-spoke20'
module vm_spoke02 '../lib/ws2019.bicep' = {
name: vm03Name
params: {
location: location01
adminPassword: kv.getSecret(secretName)
subnetId: spoke20::defaultsubnet.id
vmName: vm03Name
}
}
resource hub100 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-hub100'
location: location02
properties: {
addressSpace: {
addressPrefixes: [
'10.100.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.100.0.0/24'
}
}
{
name: 'AzureBastionSubnet'
properties: {
addressPrefix: '10.100.100.0/24'
}
}
{
name: 'GatewaySubnet'
properties: {
addressPrefix: '10.100.200.0/24'
}
}
]
}
resource defaultsubnet 'subnets' existing = {
name: 'default'
}
}
module peering_hub00100 '../lib/vnet-peering.bicep' = {
name: 'peering-hub00-hub100'
params: {
vnet01Name: hub00.name
vnet02Name: hub100.name
}
}
resource spoke110 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-spoke110'
location: location02
properties: {
addressSpace: {
addressPrefixes: [
'10.110.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.110.0.0/24'
}
}
]
}
resource defaultsubnet 'subnets' existing = {
name: 'default'
}
}
module peering_hub100110 '../lib/vnet-peering.bicep' = {
name: 'peering-hub100-spoke110'
params: {
vnet01Name: hub100.name
vnet02Name: spoke110.name
useRemoteGateways: true
remoteGatewayName: vpngw02.outputs.vpngwName
}
}
resource spoke120 'Microsoft.Network/virtualNetworks@2022-01-01' = {
name: 'vnet-spoke120'
location: location02
properties: {
addressSpace: {
addressPrefixes: [
'10.120.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '10.120.0.0/24'
}
}
]
}
resource defaultsubnet 'subnets' existing = {
name: 'default'
}
}
module peering_hub100120 '../lib/vnet-peering.bicep' = {
name: 'peering-hub100-spoke120'
params: {
vnet01Name: hub100.name
vnet02Name: spoke120.name
useRemoteGateways: true
remoteGatewayName: vpngw02.outputs.vpngwName
}
}
var vpngw02Name = 'vpngw-hub02'
module vpngw02 '../lib/vpngw_single.bicep' = {
name: vpngw02Name
params: {
location: location02
vnetName: hub100.name
gatewayName: vpngw02Name
bgpAsn: 65151
enablePrivateIpAddress: true
useExisting: useExisting
}
}
var bast02Name = 'bast-hub100'
module bast02 '../lib/bastion.bicep' = {
name: bast02Name
params: {
location: location02
bastionName: bast02Name
vnetName: hub100.name
}
}
var vm11Name = 'vm-hub100'
module vm_loc02 '../lib/ws2019.bicep' = {
name: vm11Name
params: {
location: location02
adminPassword: kv.getSecret(secretName)
subnetId: hub100::defaultsubnet.id
vmName: vm11Name
}
}
var vm12Name = 'vm-spoke110'
module vm_spoke11 '../lib/ws2019.bicep' = {
name: vm12Name
params: {
location: location02
adminPassword: kv.getSecret(secretName)
subnetId: spoke110::defaultsubnet.id
vmName: vm12Name
}
}
var vm13Name = 'vm-spoke120'
module vm_spoke12 '../lib/ws2019.bicep' = {
name: vm13Name
params: {
location: location02
adminPassword: kv.getSecret(secretName)
subnetId: spoke120::defaultsubnet.id
vmName: vm13Name
}
}
Internet VPN 版との差分が最小限に抑えられており、個人的に大満足です。
例えばこういったところが変わっています。
module vpngw01 '../lib/vpngw_single.bicep' = {
name: vpngw01Name
params: {
location: location01
vnetName: hub00.name
gatewayName: vpngw01Name
bgpAsn: 65150
enablePrivateIpAddress: true
useExisting: useExisting
}
}
enablePrivateIpAddress
パラメータを利用して VPN Gateway に Private IP アドレスを有効化しています。
useExisting
パラメータについては 2 回目以降、VPN Gateway に関しては内部的に existing
キーワードを使うための工夫です。
デプロイのコマンドをやや変えることで 2 回目以降を時短にします。
-p
パラメータは複数書いてもいいのが面白いです。
- 1 回目:
az deployment group create -g multi-hub-vpn02 -f .\main.bicep -p `@parameter.json -p isInitialDeploy=true
- 2 回目以降:
az deployment group create -g multi-hub-vpn02 -f .\main.bicep -p `@parameter.json
Discussion