web-dev-qa-db-fra.com

Plusieurs zones de disponibilité avec terraform sur AWS

Le VPC sur lequel je travaille a 3 niveaux logiques: Web, App et DB. Pour chaque niveau, il existe un sous-réseau dans chaque zone de disponibilité. Total de 6 sous-réseaux dans la région que j'utilise.

J'essaie de créer des instances EC2 à l'aide d'un module et du paramètre count, mais je ne sais pas comment dire à terraform d'utiliser les deux sous-réseaux de la couche App. Une contrainte supplémentaire que j'ai est d'utiliser des adresses IP statiques (ou un moyen d'avoir un nom privé déterministe)

Je joue avec la ressource

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

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

Choses que j'ai essayé/pensé jusqu'ici:

  • Utilisez data "aws_subnet" "all_app_subnets" {...}, filtrez par nom, obtenez tous les sous-réseaux correspondants et utilisez-les sous forme de liste. Mais aws_subnet ne peut pas renvoyer de liste;
  • Utilisez data "aws_availability_zones" {...} pour trouver toutes les zones. Mais j'ai toujours le problème d'attribuer le bon sous-réseau;
  • Utilisez data "aws_subnet_ids" {...} qui ressemble à la meilleure option. Mais apparemment, il n’a pas d’option de filtrage pour correspondre au réseau
  • Transmettez les ID de sous-réseaux sous forme de liste de chaînes au module. Mais je ne veux pas coder en dur les identifiants, ce n'est pas de l'automatisation;
  • Codez les sous-réseaux en tant que data "aws_subnet" "app_subnet_1" {...}, data "aws_subnet" "app_subnet_2" {...}, mais je dois ensuite utiliser des ensembles de variables distincts pour chaque sous-réseau, ce que je n’aime pas;
  • Obtenez des informations pour chaque sous-réseau comme dans le point ci-dessus, puis créez une map pour y accéder sous forme de liste. Mais il n'est pas possible d'utiliser l'interpolation dans la définition des variables;
  • N'utilisez pas de modules et codez en dur chaque instance pour chaque environnement. Mmmm ... vraiment?

J'ai vraiment manqué d'idées. Il semble que personne ne doive déployer des instances dans des sous-réseaux spécifiques et conserver un bon degré d'abstraction. Je ne vois que des exemples où les sous-réseaux ne sont pas spécifiés ou où les gens utilisent simplement les valeurs par défaut pour tout. Est-ce vraiment quelque chose d'aussi inhabituel?

Merci d'avance à tous.

7
ColOfAbRiX

À la fin, j'ai compris comment faire, en utilisant data "aws_subnet_ids" {...} et, ce qui est plus important, en sachant que terraform crée des listes à partir de ressources en utilisant 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)}"

  ...
}
2
ColOfAbRiX

Je force Terraform à parcourir les sous-réseaux d'une zone de disponibilité en utilisant la source de données aws_subnet_ids et en filtrant à l'aide d'une balise représentant le niveau (public/privé dans mon cas).

Cela ressemble alors à quelque chose comme ça:

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

Cela retourne un ordre de tri cohérent mais ne commence pas nécessairement par AZ A dans votre compte. Je soupçonne que l'API AWS renvoie les sous-réseaux dans l'ordre AZ, mais en fonction de leur propre identifiant interne, car les AZ sont mélangées par compte (sans doute pour empêcher l'inondation de A à Z car les humains sont prévenus, de manière prévisible, à tout mettre en avant .

Vous devez vous attacher à des nœuds horribles si, pour une raison quelconque, vous vous souciez particulièrement de placer des instances dans AZ Un premier exemple, mais cet exemple minimal devrait au moins permettre aux instances d'être arrondies dans les AZ où se trouvent des sous-réseaux en s'appuyant sur les boucle en arrière dans les tableaux lorsque la longueur du tableau est dépassée.

3
ydaetskcoR

L'index de comptage dans la ressource générera une erreur si vous avez plus d'instances que de sous-réseaux. Utilisez l'interpolation d'élément de Terraform

element (list, index) - Retourne un seul élément d'une liste à l'index donné. Si l'index est supérieur au nombre d'éléments, cette fonction sera bouclée à l'aide d'un algorithme mod standard. Cette fonction ne fonctionne que sur les listes à plat. 

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