AWS 上带有 Terraform 的多个可用区
Multiple availability zones with terraform on AWS
我正在处理的 VPC 有 3 个逻辑层:Web、App 和 DB。对于每一层,每个可用区中都有一个子网。我正在使用的区域中共有 6 个子网。
我正在尝试使用模块和 count
参数创建 EC2 实例,但我不知道如何告诉 Terraform 使用应用层的两个子网。我还有一个额外的限制是使用静态 IP 地址(或一种具有确定性私有名称的方法)
我正在玩转资源
resource "aws_instance" "app_server" {
...
count = "${var.app_servers_count}"
# Not all at the same time, though!
availability_zone = ...
subnet_id = ...
private_ip = ...
}
到目前为止我 tried/thought 的事情:
- 使用
data "aws_subnet" "all_app_subnets" {...}
,按名称过滤,获取所有匹配的子网并将它们作为列表使用。但是 aws_subnet
不能 return 列表;
- 使用
data "aws_availability_zones" {...}
找到所有区域。但是我仍然有分配正确子网的问题;
- 使用
data "aws_subnet_ids" {...}
,这看起来是最佳选择。但显然它没有过滤器选项来匹配网络 namel
- 将子网 ID 作为字符串列表传递给模块。但我不想对 ID 进行硬编码,这不是自动化;
- 将子网硬编码为
data "aws_subnet" "app_subnet_1" {...}
、data "aws_subnet" "app_subnet_2" {...}
但是我必须为每个我不喜欢的子网使用单独的变量集;
- 像上面那样获取每个子网的信息,然后创建一个
map
以将其作为列表访问。但是不能在变量定义中使用插值;
- 不使用模块并为每个环境对每个实例进行硬编码。嗯……真的吗?
我真的运行没主意了。似乎没有人必须在特定子网中部署实例并保持良好的抽象度。我只看到未指定子网或人们只对所有内容使用默认值的示例。这真的很不寻常吗?
在此先感谢大家。
我让 Terraform 通过使用 aws_subnet_ids
data source 并按代表层的标记过滤(在我的例子中是 public/private)来遍历可用区中的子网。
这看起来像这样:
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}"
}
这 returns 一致的排序顺序,但不一定在您的帐户中以 AZ A 开头。我怀疑 AWS API returns 子网按 AZ 顺序排列,但按其内部 ID 排序,因为 AZ 按帐户洗牌(大概是为了阻止 AZ A 被淹没,因为人类可预见地不擅长放置所有内容首先他们可以使用)。
如果出于某种奇怪的原因您特别关心首先放置在可用区 A 中的实例,那么您将不得不陷入一些可怕的结在超过数组长度时依靠 Terraform 循环返回数组。
最后我想出了如何去做,使用 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)}"
...
}
如果实例数多于子网数,资源中的计数索引将引发错误。使用来自 Terraform
的元素插值
element(list, index) - Returns a single element from a list at the given index. If the index is greater than the number of elements, this function will wrap using a standard mod algorithm. This function only works on flat lists.
subnet_id = "${element(data.aws_subnet_ids.app_tier_ids.ids, count.index)}"
可以使用模数在多个区域中均匀分布实例。
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) ]}"
...
}
这种分配机制允许跨区域平均分配节点。
例如。 zones = [A,B] - 实例 1 将在 A 中,实例 2 将在 B 中,实例 3 将再次在 A 中。
通过将区域 C 添加到区域会将 instance-3 转移到 C.
我正在处理的 VPC 有 3 个逻辑层:Web、App 和 DB。对于每一层,每个可用区中都有一个子网。我正在使用的区域中共有 6 个子网。
我正在尝试使用模块和 count
参数创建 EC2 实例,但我不知道如何告诉 Terraform 使用应用层的两个子网。我还有一个额外的限制是使用静态 IP 地址(或一种具有确定性私有名称的方法)
我正在玩转资源
resource "aws_instance" "app_server" {
...
count = "${var.app_servers_count}"
# Not all at the same time, though!
availability_zone = ...
subnet_id = ...
private_ip = ...
}
到目前为止我 tried/thought 的事情:
- 使用
data "aws_subnet" "all_app_subnets" {...}
,按名称过滤,获取所有匹配的子网并将它们作为列表使用。但是aws_subnet
不能 return 列表; - 使用
data "aws_availability_zones" {...}
找到所有区域。但是我仍然有分配正确子网的问题; - 使用
data "aws_subnet_ids" {...}
,这看起来是最佳选择。但显然它没有过滤器选项来匹配网络 namel - 将子网 ID 作为字符串列表传递给模块。但我不想对 ID 进行硬编码,这不是自动化;
- 将子网硬编码为
data "aws_subnet" "app_subnet_1" {...}
、data "aws_subnet" "app_subnet_2" {...}
但是我必须为每个我不喜欢的子网使用单独的变量集; - 像上面那样获取每个子网的信息,然后创建一个
map
以将其作为列表访问。但是不能在变量定义中使用插值; - 不使用模块并为每个环境对每个实例进行硬编码。嗯……真的吗?
我真的运行没主意了。似乎没有人必须在特定子网中部署实例并保持良好的抽象度。我只看到未指定子网或人们只对所有内容使用默认值的示例。这真的很不寻常吗?
在此先感谢大家。
我让 Terraform 通过使用 aws_subnet_ids
data source 并按代表层的标记过滤(在我的例子中是 public/private)来遍历可用区中的子网。
这看起来像这样:
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}"
}
这 returns 一致的排序顺序,但不一定在您的帐户中以 AZ A 开头。我怀疑 AWS API returns 子网按 AZ 顺序排列,但按其内部 ID 排序,因为 AZ 按帐户洗牌(大概是为了阻止 AZ A 被淹没,因为人类可预见地不擅长放置所有内容首先他们可以使用)。
如果出于某种奇怪的原因您特别关心首先放置在可用区 A 中的实例,那么您将不得不陷入一些可怕的结在超过数组长度时依靠 Terraform 循环返回数组。
最后我想出了如何去做,使用 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)}"
...
}
如果实例数多于子网数,资源中的计数索引将引发错误。使用来自 Terraform
的元素插值element(list, index) - Returns a single element from a list at the given index. If the index is greater than the number of elements, this function will wrap using a standard mod algorithm. This function only works on flat lists.
subnet_id = "${element(data.aws_subnet_ids.app_tier_ids.ids, count.index)}"
可以使用模数在多个区域中均匀分布实例。
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) ]}"
...
}
这种分配机制允许跨区域平均分配节点。
例如。 zones = [A,B] - 实例 1 将在 A 中,实例 2 将在 B 中,实例 3 将再次在 A 中。
通过将区域 C 添加到区域会将 instance-3 转移到 C.