14

我正在处理的 VPC 有 3 个逻辑层:Web、App 和 DB。对于每一层,每个可用区内都有一个子网。我正在使用的区域中共有 6 个子网。

我正在尝试使用模块和count参数创建 EC2 实例,但我不知道如何告诉 terraform 使用 App 层的两个子网。我有一个额外的限制是使用静态 IP 地址(或具有确定性私有名称的方法)

我在玩资源

resource "aws_instance" "app_server" {
  ...
  count = "${var.app_servers_count}"

  # Not all at the same time, though!
  availability_zone = ...
  subnet_id = ...
  private_ip = ...
}

到目前为止我尝试过/想到的事情:

  • 使用data "aws_subnet" "all_app_subnets" {...},按名称过滤,获取所有匹配的子网并将它们用作列表。但aws_subnet不能返回列表;
  • 用于data "aws_availability_zones" {...}查找所有区域。但是我仍然有分配正确子网的问题;
  • 使用data "aws_subnet_ids" {...}看起来是最好的选择。但显然它没有过滤选项来匹配网络名称
  • 将子网 ID 作为字符串列表传递给模块。但我不想对 ID 进行硬编码,这不是自动化;
  • 将子网硬编码为data "aws_subnet" "app_subnet_1" {...}data "aws_subnet" "app_subnet_2" {...}但是我必须为每个我不喜欢的子网使用单独的变量集;
  • 像上面一样获取每个子网的信息,然后创建一个map以列表形式访问它。但是在变量定义中使用插值是不可能的;
  • 不使用模块并对每个环境的每个实例进行硬编码。嗯……真的吗?

我真的没有主意了。似乎没有人必须在特定的子网中部署实例并保持良好的抽象程度。我只看到未指定子网或人们只对所有内容使用默认值的示例。这真的很不寻常吗?

在此先感谢大家。

4

4 回答 4

13

可以使用模数将实例均匀分布在多个区域中。

variable "zone" {
  description = "for single zone deployment"
  default = "europe-west4-b"
}

variable "zones" {
  description = "for multi zone deployment"
  default = ["europe-west4-b", "europe-west4-c"]
}

resource "google_compute_instance" "default" {
  count = "${var.role.count}"
  ...
  zone = "${var.zone != "" ? var.zone: var.zones[ count.index % length(var.zones) ]}"
  ...
}

这种分布机制允许跨区域均匀分布节点。
例如 zone = [A,B] - instance-1 将在 A 中,instance-2 将在 B 中,instance-3 将再次在 A 中。
通过将区域 C 添加到区域会将实例 3 转移到 C。

于 2019-08-14T16:26:09.090 回答
6

如果实例多于子网,资源中的计数索引将引发错误。使用 Terraform 的元素插值

element(list, index) - 从给定索引处的列表中返回单个元素。如果索引大于元素的数量,则此函数将使用标准 mod 算法进行换行。此功能仅适用于平面列表。

subnet_id = "${element(data.aws_subnet_ids.app_tier_ids.ids, count.index)}"
于 2018-10-11T14:24:21.890 回答
4

最后我想出了如何做到这一点,使用data "aws_subnet_ids" {...}更重要的是理解 terraform 在使用时会从资源中创建列表count

variable "target_vpc" {}
variable "app_server_count" {}
variable "app_server_ip_start" {}

# Discover VPC
data "aws_vpc" "target_vpc" {
  filter = {
    name = "tag:Name"
    values = ["${var.target_vpc}"]
  }
}

# Discover subnet IDs. This requires the subnetworks to be tagged with Tier = "AppTier"
data "aws_subnet_ids" "app_tier_ids" {
  vpc_id = "${data.aws_vpc.target_vpc.id}"
  tags {
    Tier = "AppTier"
  }
}

# Discover subnets and create a list, one for each found ID
data "aws_subnet" "app_tier" {
  count = "${length(data.aws_subnet_ids.app_tier_ids.ids)}"
  id = "${data.aws_subnet_ids.app_tier_ids.ids[count.index]}"
}

resource "aws_instance" "app_server" {
  ...

  # Create N instances
  count = "${var.app_server_count}"

  # Use the "count.index" subnet
  subnet_id = "${data.aws_subnet_ids.app_tier_ids.ids[count.index]}"

  # Create an IP address using the CIDR of the subnet
  private_ip = "${cidrhost(element(data.aws_subnet.app_tier.*.cidr_block, count.index), var.app_server_ip_start + count.index)}"

  ...
}
于 2017-09-05T09:42:14.037 回答
3

我让 Terraform 通过使用aws_subnet_ids数据源并通过表示层的标记(在我的情况下为公共/私有)过滤来遍历可用区中的子网。

然后看起来像这样:

variable "vpc" {}
variable "ami" {}
variable "subnet_tier" {}
variable "instance_count" {}

data "aws_vpc" "selected" {
  tags {
    Name = "${var.vpc}"
  }
}

data "aws_subnet_ids" "selected" {
  vpc_id = "${data.aws_vpc.selected.id}"

  tags {
    Tier = "${var.subnet_tier}"
  }
}

resource "aws_instance" "instance" {
  count         = "${var.instance_count}"
  ami           = "${var.ami}"
  subnet_id     = "${data.aws_subnet_ids.selected.ids[count.index]}"
  instance_type = "${var.instance_type}"
}

这将返回一致的排序顺序,但不一定以您账户中的 AZ A 开头。我怀疑 AWS API 按 AZ 顺序返回子网,但按它们自己的内部 id 排序,因为 AZ 按帐户打乱(大概是为了阻止 AZ A 被淹没,因为可以预见,人类不擅长将所有东西放在他们可以使用的首位) .

如果出于某种奇怪的原因您特别关心首先将实例放置在 AZ A 中,您将不得不把自己绑在一些可怕的结上,但是这个最小的示例应该至少通过依赖 Terraform 的子网在您拥有子网的 AZ 中轮询实例超过数组长度时循环返回数组。

于 2017-09-05T09:37:50.457 回答