You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Hub-Spoke架构POC中VM间无法Ping通的问题排查求助

问题:Azure Hub-Spoke架构中跨Spoke虚拟机无法互Ping

我在Azure实验环境部署了Hub-Spoke架构POC,所有资源已部署完成,但部署在spoke1子网的vm1和spoke2子网的vm2之间无法互相Ping通。已在两台VM的NSG入向设置了通配符规则,以下是部署用的Terraform代码:

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "3.115.0"
    }
    tls = {
      source  = "hashicorp/tls"
      version = "~>4.0"
    }
  }
}

provider "azurerm" {
  
  features {}
  skip_provider_registration = true
}

data "azurerm_resource_group" "myrg" {
  name = "1-cace0afe-playground-sandbox"
}

# Hub VNet
resource "azurerm_virtual_network" "hub_vnet" {
  name                = "hub-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = data.azurerm_resource_group.myrg.location
  resource_group_name = data.azurerm_resource_group.myrg.name
}

# Spoke 1 VNet
resource "azurerm_virtual_network" "spoke1_vnet" {
  name                = "spoke1-vnet"
  address_space       = ["10.1.0.0/16"]
  location            = data.azurerm_resource_group.myrg.location
  resource_group_name = data.azurerm_resource_group.myrg.name
}

# Spoke 2 VNet
resource "azurerm_virtual_network" "spoke2_vnet" {
  name                = "spoke2-vnet"
  address_space       = ["10.2.0.0/16"]
  location            = data.azurerm_resource_group.myrg.location
  resource_group_name = data.azurerm_resource_group.myrg.name
}

# Hub Subnet
resource "azurerm_subnet" "hub_subnet" {
  name                 = "hub-subnet"
  resource_group_name  = data.azurerm_resource_group.myrg.name
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  address_prefixes     = ["10.0.1.0/24"]
}

# Spoke 1 Subnet
resource "azurerm_subnet" "spoke1_subnet" {
  name                 = "spoke1-subnet"
  resource_group_name  = data.azurerm_resource_group.myrg.name
  virtual_network_name = azurerm_virtual_network.spoke1_vnet.name
  address_prefixes     = ["10.1.1.0/24"]
}

# Spoke 2 Subnet
resource "azurerm_subnet" "spoke2_subnet" {
  name                 = "spoke2-subnet"
  resource_group_name  = data.azurerm_resource_group.myrg.name
  virtual_network_name = azurerm_virtual_network.spoke2_vnet.name
  address_prefixes     = ["10.2.1.0/24"]
}

resource "azurerm_virtual_network_peering" "hub_to_spoke1" {
  name = "hub-to-spoke1"
  remote_virtual_network_id = azurerm_virtual_network.spoke1_vnet.id
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  resource_group_name = data.azurerm_resource_group.myrg.name
  allow_virtual_network_access =true 
  allow_gateway_transit = true
  allow_forwarded_traffic = true
  use_remote_gateways = false
}

resource "azurerm_virtual_network_peering" "spoke1_to_hub" {
  name = "spoke1-to-hub"
  remote_virtual_network_id = azurerm_virtual_network.hub_vnet.id
  virtual_network_name = azurerm_virtual_network.spoke1_vnet.name
  resource_group_name = data.azurerm_resource_group.myrg.name
  allow_virtual_network_access =true 
  allow_gateway_transit = false
  allow_forwarded_traffic = true
  use_remote_gateways = false
}


resource "azurerm_virtual_network_peering" "hub_to_spoke2" {
  name = "hub-to-spoke2"
  remote_virtual_network_id = azurerm_virtual_network.spoke2_vnet.id
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  resource_group_name = data.azurerm_resource_group.myrg.name
  allow_virtual_network_access =true 
  allow_gateway_transit = true
  allow_forwarded_traffic = true
  use_remote_gateways = false
}

resource "azurerm_virtual_network_peering" "spoke2_to_hub" {
  name = "spoke2-to-hub"
  remote_virtual_network_id = azurerm_virtual_network.hub_vnet.id
  virtual_network_name = azurerm_virtual_network.spoke2_vnet.name
  resource_group_name = data.azurerm_resource_group.myrg.name
  allow_virtual_network_access =true 
  allow_gateway_transit = false
  allow_forwarded_traffic = true
  use_remote_gateways = false
}

resource "azurerm_public_ip" "pipip1" {
  allocation_method = "Static"
  location = data.azurerm_resource_group.myrg.location
  name = "pip1"
  resource_group_name = data.azurerm_resource_group.myrg.name
}

resource "azurerm_public_ip" "pipip2" {
  allocation_method = "Static"
  location = data.azurerm_resource_group.myrg.location
  name = "pip2"
  resource_group_name = data.azurerm_resource_group.myrg.name
}

resource "azurerm_network_interface" "nic1" {
  name = "nic1"
  resource_group_name = data.azurerm_resource_group.myrg.name
  location = data.azurerm_resource_group.myrg.location
  ip_configuration {
    name = "nic1-ip"
    private_ip_address_allocation = "Dynamic"
    subnet_id = azurerm_subnet.spoke1_subnet.id
    public_ip_address_id = azurerm_public_ip.pipip1.id
  }
}

resource "azurerm_network_interface" "nic2" {
  name = "nic2"
  resource_group_name = data.azurerm_resource_group.myrg.name
  location = data.azurerm_resource_group.myrg.location
  ip_configuration {
    name = "nic2-ip"
    private_ip_address_allocation = "Dynamic"
    subnet_id = azurerm_subnet.spoke2_subnet.id
    public_ip_address_id = azurerm_public_ip.pipip2.id
  }
}

resource "azurerm_linux_virtual_machine" "vm1" {
  name                = "vm1"
  resource_group_name = data.azurerm_resource_group.myrg.name
  location            = data.azurerm_resource_group.myrg.location
  size                = "Standard_B2s"
  admin_username      = "test"
  network_interface_ids = [
    azurerm_network_interface.nic1.id,
  ]

  admin_ssh_key {
    username   = "sharat"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"
  }
}


resource "azurerm_linux_virtual_machine" "vm2" {
  name                = "vm2"
  resource_group_name = data.azurerm_resource_group.myrg.name
  location            = data.azurerm_resource_group.myrg.location
  size                = "Standard_B2s"
  admin_username      = "test"
  network_interface_ids = [
    azurerm_network_interface.nic2.id,
  ]

  admin_ssh_key {
    username   = "sharat"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"
  }
}

排查分析及解决方法

1. 跨Spoke通信的路由缺失

Azure VNet对等连接默认仅建立Hub与单个Spoke的通信路径,不会自动创建Spoke之间的路由。两个Spoke的虚拟机要互相通信,需要手动添加路由:

  • 在spoke1的子网路由表中添加目标为10.2.0.0/16(spoke2地址段)的路由,下一跳类型为VirtualNetworkPeering,指向Hub的VNet;
  • 在spoke2的子网路由表中添加目标为10.1.0.0/16(spoke1地址段)的路由,下一跳类型同样为VirtualNetworkPeering,指向Hub的VNet。

2. 虚拟机内部防火墙限制

Ubuntu 22.04默认启用ufw防火墙,会拦截ICMP请求。可以通过以下命令临时关闭或开放ICMP:

# 关闭防火墙测试
sudo ufw disable
# 或仅开放ICMP
sudo ufw allow icmp

3. 对等连接配置验证

虽然现有对等连接的allow_forwarded_traffic已设为true,但需确保所有双向对等连接状态为已连接,可在Azure门户查看hub-to-spoke1、spoke1-to-hub、hub-to-spoke2、spoke2-to-hub这四个对等连接的状态,确认无配置错误。

修正后的Terraform配置示例

以下是添加路由表及关联的代码片段,可补充到现有Terraform配置中:

# Spoke1子网路由表及路由
resource "azurerm_route_table" "spoke1_rt" {
  name                = "spoke1-rt"
  location            = data.azurerm_resource_group.myrg.location
  resource_group_name = data.azurerm_resource_group.myrg.name
}

resource "azurerm_route" "spoke1_to_spoke2" {
  name                   = "spoke1-to-spoke2"
  resource_group_name    = data.azurerm_resource_group.myrg.name
  route_table_name       = azurerm_route_table.spoke1_rt.name
  address_prefix         = "10.2.0.0/16"
  next_hop_type          = "VirtualNetworkPeering"
  next_hop_in_ip_address = azurerm_virtual_network.hub_vnet.address_space[0]
}

resource "azurerm_subnet_route_table_association" "spoke1_subnet_rt" {
  subnet_id      = azurerm_subnet.spoke1_subnet.id
  route_table_id = azurerm_route_table.spoke1_rt.id
}

# Spoke2子网路由表及路由
resource "azurerm_route_table" "spoke2_rt" {
  name                = "spoke2-rt"
  location            = data.azurerm_resource_group.myrg.location
  resource_group_name = data.azurerm_resource_group.myrg.name
}

resource "azurerm_route" "spoke2_to_spoke1" {
  name                   = "spoke2-to-spoke1"
  resource_group_name    = data.azurerm_resource_group.myrg.name
  route_table_name       = azurerm_route_table.spoke2_rt.name
  address_prefix         = "10.1.0.0/16"
  next_hop_type          = "VirtualNetworkPeering"
  next_hop_in_ip_address = azurerm_virtual_network.hub_vnet.address_space[0]
}

resource "azurerm_subnet_route_table_association" "spoke2_subnet_rt" {
  subnet_id      = azurerm_subnet.spoke2_subnet.id
  route_table_id = azurerm_route_table.spoke2_rt.id
}

内容的提问来源于stack exchange,提问作者Sharat Bhaskar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:05:53