Несколько зон доступности с терраформом на AWS

VPC, над которым я работаю, имеет 3 логических уровня: веб, приложения и базы данных. Для каждого уровня есть одна подсеть в каждой зоне доступности. Всего в регионе 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 для доступа к ней в виде списка. Но нельзя использовать интерполяцию в определении переменных;
  • Не использовать модули и жестко запрограммировать каждый экземпляр для каждой среды. Мммм ... правда?

У меня действительно закончились идеи. Кажется, что никому не нужно развертывать экземпляры в определенных подсетях и сохранять высокую степень абстрагирования. Я вижу только примеры, когда подсети не указаны или где люди просто используют значения по умолчанию для всего. Неужели это что-то такое необычное?

Заранее всем спасибо.


person ColOfAbRiX    schedule 04.09.2017    source источник
comment
Кто-нибудь придумал, как отсортировать подсети по количеству доступных IP? Это просто в таких инструментах, как Ansible с ec2_vpc_subnet_facts available_ip_address_count. Я пробовал использовать поставщиков данных aws_subnet_ids и aws_subnet, подобных вашему ответу ниже, но похоже, что в возвращенных подсетях с доступным количеством IP-адресов вообще нет атрибутов!   -  person TrinitronX    schedule 26.02.2018
comment
Вам нужно сначала получить доступное количество IP-адресов, а затем выполнить сортировку. Но учитывая, насколько ограничен синтаксис интерполяции (посмотрите документацию по сортировке), я думаю, что нет способа сделать это с помощью простого и чистого решения.   -  person ColOfAbRiX    schedule 27.02.2018


Ответы (5)


Можно равномерно распределить экземпляры по нескольким зонам, используя модуль по модулю.

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.

person Tom Lime    schedule 14.08.2019

В конце концов, я понял, как это сделать, используя 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)}"

  ...
}
person ColOfAbRiX    schedule 05.09.2017
comment
Не перезаписывается ли переменная счетчика? - person Ranvijay Jamwal; 15.01.2018

Индекс счетчика в ресурсе вызовет ошибку, если у вас больше экземпляров, чем подсетей. Используйте интерполяцию элементов из Terraform

element (list, index) - возвращает единственный элемент из списка по заданному индексу. Если индекс больше, чем количество элементов, эта функция будет завершена с использованием стандартного алгоритма модификации. Эта функция работает только с плоскими списками.

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

Я получаю 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 в вашем аккаунте. Я подозреваю, что API AWS возвращает подсети в порядке AZ, но упорядочивает их по собственному внутреннему идентификатору, поскольку AZ перетасовываются по учетной записи (предположительно, чтобы остановить затопление AZ A, поскольку люди предсказуемо плохо помещают все в первую очередь, что они могут использовать) .

Вам придется связать себя ужасными узлами, если по какой-то странной причине вы особенно заботитесь о том, чтобы инстансы были размещены в первую очередь в AZ A, но этот минимальный пример должен, по крайней мере, получить экземпляры, проходящие циклический перебор через AZ, в которых у вас есть подсети, полагаясь на Terraform's обратный цикл по массивам при превышении длины массива.

person ydaetskcoR    schedule 05.09.2017
comment
Спасибо за ответ. Нет, к счастью, порядок меня не волнует. В любом случае я нашел аналогичное решение, касающееся также проблемы статического IP-адреса. - person ColOfAbRiX; 05.09.2017
comment
Также хотел добавить, что из-за того, что мы плохо выбираем первый вариант, AWS на самом деле случайным образом связывает букву AZ в фоновом режиме. Это означает, что us-east1a моей учетной записи не будет такой же, как ваша зона us-east-1a. Если у вас несколько учетных записей, вы можете связаться с AWS, чтобы синхронизировать зоны доступности для всех ваших учетных записей. - person Stephen Lester; 26.02.2019
comment
@SteohenLester Ага, я упомянул об этом в ответе. - person ydaetskcoR; 26.02.2019

Для всех, кто предлагает count.index в подходе aws_instance, это не лучший вариант, потому что он не работает, если у вас больше экземпляров, чем подсетей.

person Cristiano Casella    schedule 20.07.2021
comment
Привет, я не думаю, что это следует публиковать как ответ, потому что это не отвечает на вопрос. Как только вы заработаете достаточно репутации, вы сможете вместо этого комментировать - person Anonymouse; 22.07.2021