简体   繁体   English

AWS 上具有 terraform 的多个可用区

[英]Multiple availability zones with terraform on AWS

The VPC I'm working on has 3 logical tiers: Web, App and DB.我正在处理的 VPC 有 3 个逻辑层:Web、App 和 DB。 For each tier there is one subnet in each availability zone.对于每一层,每个可用区内都有一个子网。 Total of 6 subnets in the region I'm using.我正在使用的区域中共有 6 个子网。

I'm trying to create EC2 instances using a module and the count parameter but I don't know how to tell terraform to use the two subnets of the App tier.我正在尝试使用模块和count参数创建 EC2 实例,但我不知道如何告诉 terraform 使用 App 层的两个子网。 An additional constraint I have is to use static IP addresses (or a way to have a deterministic private name)我有一个额外的限制是使用静态 IP 地址(或具有确定性私有名称的方法)

I'm playing around with the resource我在玩资源

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

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

Things I've tried/thought so far:到目前为止我尝试过/想到的事情:

  • Use data "aws_subnet" "all_app_subnets" {...} , filter by name, get all the subnets that match and use them as a list.使用data "aws_subnet" "all_app_subnets" {...} ,按名称过滤,获取所有匹配的子网并将它们用作列表。 But aws_subnet cannot return a list;但是aws_subnet不能返回列表;
  • Use data "aws_availability_zones" {...} to find all the zones.使用data "aws_availability_zones" {...}查找所有区域。 But I still have the problem of assigning the correct subnet;但是我仍然有分配正确子网的问题;
  • Use data "aws_subnet_ids" {...} which looks like the best option.使用data "aws_subnet_ids" {...}看起来是最好的选择。 But apparently it doesn't have a filter option to match the networks namel但显然它没有过滤选项来匹配网络名称
  • Pass the subnets IDs as list of strings to the module.将子网 ID 作为字符串列表传递给模块。 But I don't want to hard code the IDs, it's not automation;但我不想对 ID 进行硬编码,这不是自动化;
  • Hard code the subnets as data "aws_subnet" "app_subnet_1" {...} , data "aws_subnet" "app_subnet_2" {...} but then I have to use separate sets of variables for each subnet which I don't like;将子网硬编码为data "aws_subnet" "app_subnet_1" {...}data "aws_subnet" "app_subnet_2" {...}但是我必须为每个我不喜欢的子网使用单独的变量集;
  • Get information for each subnet like in the point above but then create a map to access it as a list.像上面一样获取每个子网的信息,然后创建一个map以将其作为列表访问。 But it's not possibile to use interpolation in variables definition;但是在变量定义中使用插值是不可能的;
  • Not using modules and hard-code each instance for each environment.不使用模块并对每个环境的每个实例进行硬编码。 Mmmm... really?嗯……真的吗?

I really ran out of ideas.我真的没有主意了。 It seems that nobody has to deploy instances in specific subnetworks and keep a good degree of abstration.似乎没有人必须在特定的子网中部署实例并保持良好的抽象程度。 I see only examples where subnetworks are not specified or where people just use default values for everything.我只看到未指定子网或人们只对所有内容使用默认值的示例。 Is this really something so unusual?这真的很不寻常吗?

Thanks in advance to everyone.在此先感谢大家。

It is possible to evenly distribute instances across multiple zones using modulo.可以使用模数将实例均匀分布在多个区域中。

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) ]}"
  ...
}

This distribution mechanism allow to distribute nodes evenly across zones.这种分布机制允许跨区域均匀分布节点。
Eg zones = [A,B] - instance-1 will be in A, instance-2 will in B, instance-3 will be in A again.例如 zone = [A,B] - instance-1 将在 A 中,instance-2 将在 B 中,instance-3 将再次在 A 中。
By adding zone C to zones will shift instance-3 to C.通过将区域 C 添加到区域会将实例 3 转移到 C。

The count index in the resource will throw an error if you have more instances than subnets.如果实例多于子网,资源中的计数索引将引发错误。 Use the element interpolation from Terraform使用 Terraform 的元素插值

element(list, index) - Returns a single element from a list at the given index. element(list, index) - 从给定索引处的列表中返回单个元素。 If the index is greater than the number of elements, this function will wrap using a standard mod algorithm.如果索引大于元素的数量,则此函数将使用标准 mod 算法进行换行。 This function only works on flat lists.此功能仅适用于平面列表。

subnet_id = "${element(data.aws_subnet_ids.app_tier_ids.ids, count.index)}"

At the end I figured out how to do it, using data "aws_subnet_ids" {...} and more importantly understanding that terraform creates lists out of resources when using count :最后我想出了如何做到这一点,使用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)

  ...
}

I get Terraform to loop through the subnets in an availability zone by using the aws_subnet_ids data source and filtering by a tag representing the tier (in my case public/private).我让 Terraform 通过使用aws_subnet_ids数据源并通过表示层的标记(在我的情况下为公共/私有)过滤来遍历可用区中的子网。

This then looks something like this:然后看起来像这样:

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}"
}

This returns a consistent sort order but not necessarily starting with AZ A in your account.这将返回一致的排序顺序,但不一定以您账户中的 AZ A 开头。 I suspect that the AWS API returns the subnets in AZ order but ordered by their own internal id as the AZs are shuffled by account (presumably to stop AZ A being flooded as humans are predictably bad at putting everything in the first place they can use).我怀疑 AWS API 按 AZ 顺序返回子网,但按它们自己的内部 id 排序,因为 AZ 按帐户打乱(大概是为了阻止 AZ A 被淹没,因为可以预见,人类不擅长将所有东西放在他们可以使用的首位) .

You would have to tie yourself in some horrible knots if for some odd reason you particularly care about instances being placed in AZ A first but this minimal example should at least get instances being round-robined through the AZs you have subnets in by relying on Terraform's looping back through arrays when exceeding the array length.如果出于某种奇怪的原因您特别关心首先将实例放置在 AZ A 中,您将不得不把自己绑在一些可怕的结上,但是这个最小的示例应该至少通过依赖 Terraform 的子网在您拥有子网的 AZ 中轮询实例超过数组长度时循环返回数组。

对于在 aws_instance 方法中建议 count.index 的每个人来说,这不是最好的,因为如果您的实例多于子网,则会失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM