Несколько зон доступности с терраформой на AWS
VPC, над которым я работаю, имеет 3 логических уровня: Web, App и DB. Для каждого уровня есть одна подсеть в каждой зоне доступности. Всего 6 подсетей в регионе я использую.
Я пытаюсь создать экземпляры EC2, используя модуль и count
параметр, но я не знаю, как сказать 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 = ...
}
То, что я пробовал / думал до сих пор:
- использование
data "aws_subnet" "all_app_subnets" {...}
, фильтр по имени, получить все подсети, которые соответствуют и использовать их в качестве списка. Ноaws_subnet
не может вернуть список; - использование
data "aws_availability_zones" {...}
найти все зоны. Но у меня все еще есть проблема назначения правильной подсети; - использование
data "aws_subnet_ids" {...}
который выглядит как лучший вариант. Но, по-видимому, у него нет опции фильтра для соответствия именам сетей - Передайте идентификаторы подсетей в виде списка строк в модуль. Но я не хочу жестко кодировать идентификаторы, это не автоматизация;
- Жесткий код подсетей как
data "aws_subnet" "app_subnet_1" {...}
,data "aws_subnet" "app_subnet_2" {...}
но тогда я должен использовать отдельные наборы переменных для каждой подсети, которые мне не нравятся; - Получите информацию для каждой подсети, как описано выше, но затем создайте
map
чтобы получить доступ к нему в виде списка. Но невозможно использовать интерполяцию в определении переменных; - Не использовать модули и жестко программировать каждый экземпляр для каждой среды. Мммм... правда?
У меня действительно кончились идеи. Кажется, что никто не должен развертывать экземпляры в определенных подсетях и поддерживать хорошую степень абстракции. Я вижу только примеры, где не указаны подсети или где люди просто используют значения по умолчанию для всего. Это действительно что-то необычное?
Спасибо всем заранее.
5 ответов
В конце я разобрался, как это сделать, используя 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)}"
...
}
Можно равномерно распределить экземпляры по нескольким зонам с использованием модуля.
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] - экземпляр-1 будет в A, экземпляр-2 будет в B, экземпляр-3 снова будет в A.
При добавлении зоны C к зонам экземпляр-3 сместится в C.
Индекс подсчета в ресурсе выдает ошибку, если у вас больше экземпляров, чем подсетей. Используйте элемент интерполяции от Terraform
элемент (список, индекс) - возвращает один элемент из списка по указанному индексу. Если индекс больше, чем количество элементов, эта функция будет переноситься с использованием стандартного алгоритма мода. Эта функция работает только для плоских списков.
subnet_id = "${element(data.aws_subnet_ids.app_tier_ids.ids, count.index)}"
Я получаю Terraform, чтобы пройти через подсети в зоне доступности с помощью aws_subnet_ids
источник данных и фильтрация по тегу, представляющему уровень (в моем случае 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}"
}
Это возвращает согласованный порядок сортировки, но не обязательно начиная с AZ A в вашем аккаунте. Я подозреваю, что API-интерфейс AWS возвращает подсети в порядке AZ, но упорядочены по собственному внутреннему идентификатору, так как AZ перетасовываются из-за учетной записи (предположительно, чтобы остановить затопление AZ A, поскольку люди, как и следовало ожидать, плохо помещают все в первое место, которое они могут использовать),
Вы должны были бы связать себя ужасными узлами, если по какой-то странной причине вы особенно заботитесь о том, чтобы экземпляры были помещены в AZ. Первый, но этот минимальный пример должен по крайней мере получить циклическую обработку экземпляров через AZ, в которых есть подсети, полагаясь на Terraform. Возврат через массив при превышении длины массива.
Для всех, кто предлагает count.index в подходе aws_instance, это не лучший вариант, потому что он не работает, если у вас больше экземпляров, чем подсетей.