使用 terraform 在 AWS 中部署多个实例。编写代码是为了使用特定的专用 IP 范围。代码循环访问一个范围以提供 IP 上的最后两位数字。
IP_AA_MGMT_Windows = [for i in range(1, var.Number_of_AA_Candidates +1 ) : format("%s%02d", "10.10.8.1", i)]
有关信息,此子网所属的子网具有以下 CIDR 分配
cidr_block = "10.10.8.0/22"
这给出了 10.10.8.0 - 10.10.11.255 的 ip 范围
创建实例时没有实际问题。预期的专用 IP 以与网络接口相同的方式分配。
resource "aws_instance" "Windows" {
instance_type = "t2.large"
subnet_id = aws_subnet.windows.id
vpc_security_group_ids = [aws_security_group.AA_Eng_Windows[count.index].id]
key_name = aws_key_pair.ENG-DEV.id
count = var.Number_of_AA_Candidates
private_ip = local.IP_AA_WINLAN_Windows[count.index]
associate_public_ip_address = false
将创建一个额外的网络接口并将其附加到实例。
resource "aws_network_interface" "Windows_Access_Interface" {
subnet_id = aws_subnet.management.id
private_ip = local.IP_AA_MGMT_Windows[count.index]
security_groups = [aws_security_group.Windows.id]
count = var.Number_of_AA_Candidates
attachment {
instance = aws_instance.Windows[count.index].id
device_index = 1
}
所有这些都根据地形正确部署。直到您检查 AWS 中的私有 IP 或通过地形状态显示您意识到网络接口资源已创建,但私有 IP 不正确,而不是代码中预置的私有 IP。注意。地形规划提供的输出表明 IP 分配没有问题。
下面是 terrafrom show 命令的一些输出。
# aws_network_interface.Windows_Access_Interface[0]:
resource "aws_network_interface" "Windows_Access_Interface" {
interface_type = "interface"
private_ip = "10.10.10.72"
private_ip_list = [
"10.10.10.72",
]
private_ip_list_enabled = false
private_ips = [
"10.10.10.72",
]
注意 为了安全起见,节目中的某些细节已被故意删除。
现在的问题是,是什么原因造成的?
这里有一些要点需要考虑。首先是每个 VPC 中有五个保留地址 [1]:
每个子网 CIDR 块中的前四个 IP 地址和最后一个 IP 地址不可供您使用,并且无法分配给资源,例如 EC2 实例。
因此,这意味着您必须开始计算可从10.10.8.4
分配的地址。这进一步意味着在范围函数中,计数必须在 3 之后开始:
IP_AA_MGMT_Windows = [for i in range(4, var.Number_of_AA_Candidates + 1 ) : format("%s%02d", "10.10.8.1", i)]
由于 IP 地址不是真正的字符串,因此format
函数和%02d
只会添加0
,并且根据Number_of_AA_Candidates
字符串末尾添加一定数量的十进制数字。例如,如果Number_of_AA_Candidates
等于2
,这将产生以下 IP 地址:
> local.IP_AA_MGMT_Windows
[
"10.10.8.101",
"10.10.8.102",
]
请注意,这是针对从 1 开始的原始range
。这看起来很好,但请考虑您将添加一个两位数数字(甚至三位数数字以将点带回家)的情况。此外,range
的第二部分很好,除非您将Number_of_AA_Candidates
设置为大于或等于最大 IP 地址数的值。如果您以某种方式计算错误,范围将超出,并且将创建的 IP 地址将不是有效的 IP 地址。为确保不会超出 CIDR 范围内可用 IP 的最大数量,您可以使用以下公式计算该数字:
2^10 - 5
10 是从最大位数 32 中扣除子网位后剩余的位数。5 是不能使用的 IP 地址数。这为您提供了 1019 个可能的主机地址。为了确保这种情况不会发生,您可以为range
函数的第二部分引入三元运算符:
IP_AA_MGMT_Windows = [for i in range(4, (var.Number_of_AA_Candidates > 1019 ? 1019 : var.Number_of_AA_Candidates + 1) ) : format("%s%02d", "10.10.8.1", i)]
现在,这是两个问题已解决。第三个也是最后一个问题是format
函数。为了仅使用可用的IP地址并避免使用format
,我建议尝试cidrhost
内置函数[2]。cidrhost
语法为:
CIDRHOST(前缀,主机编号)
hostnum
部分表示 CIDR 范围内所需的主机 IP 地址。例如,如果您要执行以下操作:
cidrhost("10.10.8.0/22", 1)
这将返回范围中的第一个 IP 地址。对于等于 2hostnum
,它将返回 2nd,依此类推。
要正确使用它,您必须修改局部变量,使其如下所示:
IP_AA_MGMT_Windows = [for i in range(4, (var.Number_of_AA_Candidates > 1019 ? 1019 : var.Number_of_AA_Candidates + 1)) : cidrhost("10.10.8.0/22", i)]
这适用于主机 IP 地址最大数量之前的任何数字。最后,即使我们知道有 5 个 IP 地址不能使用,cidrhost
对此一无所知,并且总是从 CIDR 范围内的第一个数字开始计数到最后一个数字,因此最后一个表达式必须使用 1023 个地址,因为我们不想包含广播地址(覆盖起始 IP 地址,因为我们从 4 开始):
IP_AA_MGMT_Windows = [for i in range(4, (var.Number_of_AA_Candidates > 1023 ? 1023 : var.Number_of_AA_Candidates + 1)) : cidrhost("10.10.8.0/22", i)]
编辑:在聊天中进行讨论后,我们发现aws_network_interface
中的论点存在问题(即使terraform没有抱怨)。问题中的参数是private_ip
,而提供程序将其列为字符串列表 [3]private_ips
。将其更改为:
private_ips = [ local.IP_AA_MGMT_Windows[count.index] ]
应用程序按预期工作。
<小时 />[1] https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html#subnet-sizing
[2] https://www.terraform.io/language/functions/cidrhost
[3] https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_interface#private_ips