如何使用 cidrsubnet Function 在 Terraform 中细分 CIDR 子网?

How to subdivide CIDR subnet in Terraform with cidrsubnet Function?

我想为 AWS 区域内的子网提供这样的地址:

10.0.0.0/21
10.0.8.0/21
10.0.16.0/21
10.0.24.0/21
10.0.32.0/21
...

基本上,将第二个数字递增 8。

我还将为每个区域设置一个 VPN,它们应该共享同一个网络。所以我希望他们像这样:

10.1.0.0/16
10.2.0.0/16
10.3.0.0/16
10.4.0.0/16
10.5.0.0/16
...

基本上每次将第 3 个数字递增 1。如果不可能每步走 1,那么也走 8。

我有read in depth about CIDR blocks,但还是不太清楚如何申请,具体在下面的上下文中。

我想要一套以 1 个 CIDR 块开头的嵌套 Terraform 模块,并将其子网划分为越来越小的块。首先,它将按区域(第三个数字)细分,然后按可用性区域(第二个数字)细分。一切都将是动态的,因此您只需按字面意思提供初始 CIDR 块。

看来我应该使用 cidrsubnet:

cidrsubnet(prefix, newbits, netnum)

我得到了这样的结果:

cidrsubnet(var.cidr_block, 13, netnum?)

我知道 13 将从 /8 变为 /21。但这直接进入可用性区域(我不知道如何处理 netnum)。我想做这样的事情:

# main.tf
variable "cidr_block" {
  default = "10.0.0.0/8"
}

module "region1" {
  source = "./region" # ./region/main.tf
  cidr_block = cidrsubnet(var.cidr_block, 8?, count.index?) # 10.0.0.0/16?
}

module "region2" {
  source = "./region" # ./region/main.tf
  cidr_block = cidrsubnet(var.cidr_block, 8?, count.index?) # 10.1.0.0/16?
}

// ...

然后在区域子模块中:

# region/main.tf
module "availability_zone1" {
  source = "./availability_zone" # ./availability_zone/main.tf
  cidr_block = cidrsubnet(var.cidr_block, 5?, count.index?) # 10.1.0.0/21
}

module "availability_zone2" {
  source = "./availability_zone" # ./availability_zone/main.tf
  cidr_block = cidrsubnet(var.cidr_block, 5?, count.index?) # 10.1.8.0/21
}

module "availability_zone3" {
  source = "./availability_zone" # ./availability_zone/main.tf
  cidr_block = cidrsubnet(var.cidr_block, 5?, count.index?) # 10.1.16.0/21
}

// ...

所以基本上,从 /8 -> /16 细分一次,然后再从 /16 -> /21 细分一次。 count.index 我还没有用过,但不知何故我想让它知道它在调用序列中的什么位置。如果不可能,那很好,我可以随模块一起传递一个索引。

如何使用 cidrsubnet 函数编写此代码?这个问题最重要的部分是我应该在函数的每个槽中放什么:cidrsubnet(prefix, newbits, netnum),我不知道具体 netnum 应该是什么样的。

你已经拥有的是正确的。 count.index 只会增加 1。

您可以按如下方式计算和验证块 (terraform 0.12):

provider "aws" {
  region = "us-east-1"
}


variable "cidr_block" {
    default = "10.0.0.0/8"
}

output "cidr1" {
    value = [for index in range(8):
             cidrsubnet(var.cidr_block, 8, index)]
}

output "cidr2" {
    value = [for index in range(8):
             cidrsubnet(var.cidr_block, 13, index)]
}

这些将输出:

cidr1 = [
  "10.0.0.0/16",
  "10.1.0.0/16",
  "10.2.0.0/16",
  "10.3.0.0/16",
  "10.4.0.0/16",
  "10.5.0.0/16",
  "10.6.0.0/16",
  "10.7.0.0/16",
]
cidr2 = [
  "10.0.0.0/21",
  "10.0.8.0/21",
  "10.0.16.0/21",
  "10.0.24.0/21",
  "10.0.32.0/21",
  "10.0.40.0/21",
  "10.0.48.0/21",
  "10.0.56.0/21",
]

要查看所有组合,您可以这样做:

provider "aws" {
  region = "us-east-1"
}


variable "cidr_block" {
    default = "10.0.0.0/8"
}


output "all_cidrs" {
    value = [for index1 in range(8):
             {
                 cidrsubnet(var.cidr_block, 8, index1) = [
                     for index2 in range(8): cidrsubnet(cidrsubnet(var.cidr_block, 8, index1), 5, index2)
                 ]
             }
    ]
}

给出:

all_cidrs = [
  {
    "10.0.0.0/16" = [
      "10.0.0.0/21",
      "10.0.8.0/21",
      "10.0.16.0/21",
      "10.0.24.0/21",
      "10.0.32.0/21",
      "10.0.40.0/21",
      "10.0.48.0/21",
      "10.0.56.0/21",
    ]
  },
  {
    "10.1.0.0/16" = [
      "10.1.0.0/21",
      "10.1.8.0/21",
      "10.1.16.0/21",
      "10.1.24.0/21",
      "10.1.32.0/21",
      "10.1.40.0/21",
      "10.1.48.0/21",
      "10.1.56.0/21",
    ]
  },
  {
    "10.2.0.0/16" = [
      "10.2.0.0/21",
      "10.2.8.0/21",
      "10.2.16.0/21",
      "10.2.24.0/21",
      "10.2.32.0/21",
      "10.2.40.0/21",
      "10.2.48.0/21",
      "10.2.56.0/21",
    ]
  },
  {
    "10.3.0.0/16" = [
      "10.3.0.0/21",
      "10.3.8.0/21",
      "10.3.16.0/21",
      "10.3.24.0/21",
      "10.3.32.0/21",
      "10.3.40.0/21",
      "10.3.48.0/21",
      "10.3.56.0/21",
    ]
  },
  {
    "10.4.0.0/16" = [
      "10.4.0.0/21",
      "10.4.8.0/21",
      "10.4.16.0/21",
      "10.4.24.0/21",
      "10.4.32.0/21",
      "10.4.40.0/21",
      "10.4.48.0/21",
      "10.4.56.0/21",
    ]
  },
  {
    "10.5.0.0/16" = [
      "10.5.0.0/21",
      "10.5.8.0/21",
      "10.5.16.0/21",
      "10.5.24.0/21",
      "10.5.32.0/21",
      "10.5.40.0/21",
      "10.5.48.0/21",
      "10.5.56.0/21",
    ]
  },
  {
    "10.6.0.0/16" = [
      "10.6.0.0/21",
      "10.6.8.0/21",
      "10.6.16.0/21",
      "10.6.24.0/21",
      "10.6.32.0/21",
      "10.6.40.0/21",
      "10.6.48.0/21",
      "10.6.56.0/21",
    ]
  },
  {
    "10.7.0.0/16" = [
      "10.7.0.0/21",
      "10.7.8.0/21",
      "10.7.16.0/21",
      "10.7.24.0/21",
      "10.7.32.0/21",
      "10.7.40.0/21",
      "10.7.48.0/21",
      "10.7.56.0/21",
    ]
  },
]

在我撰写此答案时,Terraform 0.13.0 处于候选发布版本 1,并将在几周内发布。 Terraform 0.13.0 包括对模块使用 for_eachcount 的能力,这使得使用 the hashicorp/subnets/cidr module 像这样的多级寻址计划更加直接。与直接进行单独的 cidrsubnet 调用相比,该模块的一个优点是它允许您为每个网络分配符号名称,并且它有一些约定来帮助弃用和稍后重用您的部分地址 space 无需重新分配所有相邻网络。

您在示例中提到了 AWS 区域和子网,因此为了展示这一点,我将使用 AWS 区域名称和可用性区域作为网络的符号名称,但您可以使用任何有意义的名称只要每个网络都以唯一标识符结尾,您的架构就可以了。

locals {
  aws_region_zones = tolist([
    {
      region = "us-west-2"
      zones  = ["us-west-2a", "us-west-2b"]
    },
    {
      region = "us-east-1"
      zones  = ["us-east-1a", "us-east-2f"]
    },
    # When allocating new regions or zones,
    # always add them at the end of their
    # respective list to avoid renumbering
    # the existing address allocations.
  ])
}

module "regional" {
  source = "hashicorp/subnets/cidr"

  base_cidr_block = "10.0.0.0/8"
  networks = [
    for regional in local.aws_region_zones : {
      name     = regional.region
      new_bits = 8
    }
  ]
}

module "zonal" {
  source   = "hashicorp/subnets/cidr"
  for_each = {
    for net in module.regional.networks : net.name => net
  }

  base_cidr_block = each.value.cidr_block
  networks = [
    for zone_name in local.aws_region_zones[each.key].zones : {
      name     = regional.region
      new_bits = 5
    }
  ]
}

output "region_cidr_blocks" {
  value = tomap({
    for net in module.regional.networks : net.name => {
      cidr_block = net.cidr_block
      zones = tomap({
        for subnet in module.zonal[net.name].networks : subnet.name => {
          cidr_block = net.cidr_block
        }
      })
    }
  })
}

region_cidr_blocks 输出值将是一个对象映射,其中每个对象代表一个区域,然后是每个可用区的嵌套映射。