Terraform: инфраструктура на уровне кода — страница 40 из 65

  # Ждем, пока как минимум столько серверов не пройдут проверку

  # работоспособности, прежде чем считать развертывание ASG завершенным

  min_elb_capacity = var.min_size

  # (...)

}

Четвертую переменную, user_data, нужно передать в скрипт пользовательских данных. Если в модуле webserver-cluster этот скрипт был прописан вручную и мог использоваться лишь для развертывания приложения Hello, World, модуль asg-rolling-deploy позволяет развертывать в ASG любое приложение, так как скрипт теперь передается в виде входной переменной. Итак, передайте переменную user_data ресурсу aws_launch_configuration (заменив ею ссылку на источник данных template_file, которую мы не скопировали в модуль asg-rolling-deploy):

resource "aws_launch_configuration" "example" {

  image_id        = var.ami

  instance_type   = var.instance_type

  security_groups = [aws_security_group.instance.id]

  user_data       = var.user_data

  # Требуется при использовании группы автомасштабирования

  # в конфигурации запуска.

  # https://www.terraform.io/docs/providers/aws/r/launch_configuration.html

  lifecycle {

    create_before_destroy = true

  }

}

Следует также добавить парочку полезных переменных в файл modules/cluster/asgrolling-deploy/outputs.tf:

output "asg_name" {

  value       = aws_autoscaling_group.example.name

  description = "The name of the Auto Scaling Group"

}

output "instance_security_group_id" {

  value       = aws_security_group.instance.id

  description = "The ID of the EC2 Instance Security Group"

}

Вывод этих данных делает модуль asg-rolling-deploy еще более универсальным, поскольку с помощью этих выходных значений его пользователи смогут изменять его поведение, подключая собственные правила к группе безопасности.

По аналогичным причинам несколько выходных переменных следует добавить и в файл modules/networking/alb/outputs.tf:

output "alb_dns_name" {

  value       = aws_lb.example.dns_name

  description = "The domain name of the load balancer"

}

output "alb_http_listener_arn" {

  value       = aws_lb_listener.http.arn

  description = "The ARN of the HTTP listener"

}

output "alb_security_group_id" {

  value       = aws_security_group.alb.id

  description = "The ALB Security Group ID"

}

Вы скоро увидите, как их использовать.

Последним шагом будет преобразование webserver-cluster в модуль hello-world-app, способный развернуть приложение Hello, World с помощью asg-rolling-deploy и alb. Для этого переименуйте module/services/webserver-cluster в module/services/hello-world-app. После всех предыдущих изменений в файле module/services/hello-world-app/main.tf должны остаться только следующие ресурсы:

•template_file (для пользовательских данных);

• aws_lb_target_group;

• aws_lb_listener_rule;

• terraform_remote_state (для БД);

• aws_vpc;

• aws_subnet_ids.

Добавьте следующую переменную в файл modules/services/hello-world-app/variables.tf:

variable "environment" {

  description = "The name of the environment we're deploying to"

  type        = string

}

Теперь добавьте созданный вами ранее модуль asg-rolling-deploy в hello-world-app, чтобы развернуть ASG:

module "asg" {

  source = "../../cluster/asg-rolling-deploy"

  cluster_name  = "hello-world-${var.environment}"

  ami           = var.ami

  user_data     = data.template_file.user_data.rendered

  instance_type = var.instance_type

  min_size           = var.min_size

  max_size           = var.max_size

  enable_autoscaling = var.enable_autoscaling

  subnet_ids        = data.aws_subnet_ids.default.ids

  target_group_arns = [aws_lb_target_group.asg.arn]

  health_check_type = "ELB"

  custom_tags = var.custom_tags

}

Добавьте в hello-world-app еще и модуль alb, который вы тоже создали ранее, чтобы развернуть ALB:

module "alb" {

  source     = "../../networking/alb"

  alb_name   = "hello-world-${var.environment}"

  subnet_ids = data.aws_subnet_ids.default.ids

}

Обратите внимание на то, что входная переменная environment используется для соблюдения соглашения об именовании, чтобы все ваши ресурсы были распределены по пространствам имен в зависимости от среды (например, hello-world-stage, hello-world-prod). Этот код также устанавливает соответствующие значения для переменных subnet_ids, target_group_arns, health_check_type и user_data, которые вы добавили ранее.

Теперь вам нужно настроить целевую группу ALB и правило прослушивателя для этого приложения. Сделайте так, чтобы ресурс aws_lb_target_group в файле modules/services/hello-world-app/main.tf использовал в своем поле name переменную environment:

resource "aws_lb_target_group" "asg" {

  name     = "hello-world-${var.environment}"

  port     = var.server_port

  protocol = "HTTP"

  vpc_id   = data.aws_vpc.default.id

  health_check {

    path                = "/"

    protocol            = "HTTP"

    matcher             = "200"

    interval            = 15

    timeout             = 3

    healthy_threshold   = 2

    unhealthy_threshold = 2

  }

}

Теперь сделайте так, чтобы параметр listener_arn ресурса aws_lb_listener_rule указывал на вывод alb_http_listener_arn модуля alb:

resource "aws_lb_listener_rule" "asg" {

  listener_arn = module.alb.alb_http_listener_arn

  priority     = 100

  condition {

    field  = "path-pattern"

    values = ["*"]

  }

  action {

    type             = "forward"

    target_group_arn = aws_lb_target_group.asg.arn

  }

}

И передайте важные выходные параметры из asg-rolling-deploy и alb в качестве вывода модуля hello-world-app:

output "alb_dns_name" {

  value       = module.alb.alb_dns_name

  description = "The domain name of the load balancer"

}

output "asg_name" {

  value       = module.asg.asg_name

  description = "The name of the Auto Scaling Group"

}

output "instance_security_group_id" {

  value       = module.asg.instance_security_group_id

  description = "The ID of the EC2 Instance Security Group"

}

Это композиция функций в действии: вы выстраиваете сложное поведение (приложение Hello, World) из более простых частей (модули ASG и ALB). В Terraform часто можно встретить как минимум два типа модулей.

Обобщенные модули. Такие модули, как asg-rolling-deploy и alb, являются базовыми составными блоками вашего кода, которые можно использовать во множестве разных сценариев. Вы видели, как с их помощью развертывается приложение Hello, World, но те же модули можно применить, к примеру, чтобы развернуть ASG для запуска кластера Kafka или полностью автономного экземпляра ALB, способного распределять нагрузку между разнообразными приложениями. Использовать один балансировщик для всех приложений сразу будет дешевле, чем выделять для каждого из них по одному ALB.

Узкоспециализированные модули. Модули вроде hello-world-app сочетают в себе несколько обобщенных модулей с расчетом на один конкретный сценарий использования, скажем, развертывание приложения Hello, World.

В реальной работе вам, возможно, придется дробить свои модули еще сильнее, чтобы улучшить композицию и повторное применение. Например, вы можете применять коллекцию открытых, универсальных модулей из репозитория terraform-aws-consul (http://bit.ly/33hhOwr) для работы с HashiCorp Consul. Consul — это открытое, распределенное хранилище типа «ключ — значение», которому иногда нужно прослушивать сетевые запросы на множестве разных портов (серверные RPC, CLI RPC, Serf WAN, HTTP API, DNS и т. д.), поэтому оно обычно требует около 20 групп безопасности. В репозитории terraform-aws-consul правила этих групп описываются в отдельном модуле consul-security-group-rules (http://bit.ly/31oo5oc).

Чтобы понять, зачем это нужно, можно сначала посмотреть на то, как развертывается система Consul. Ее часто развертывают в качестве хранилища данных для HashiCorp Vault, открытого распределенного хранилища конфиденциальной информации, в котором можно безопасно размещать пароли, API-ключи, сертификаты TLS и т. д. В репозитории terraform-aws-vault (http://bit.ly/2MM6AKn) можно найти коллекцию открытых универсальных модулей для работы с Vault. Там же представлена диаграмма, на которой изображена типичная промышленная архитектура для этой системы (рис. 6.3).

В промышленных условиях Vault обычно запускается в одной группе ASG, состоящей из 3–5 серверов (развернутых с помощью модуля vault-cluster (http://bit.ly/2TngRON), а Consul — в другой, тоже с 3–5 серверами (развернутыми с модулем consul-cluster (http://bit.ly/2YQXIWe), поэтому каждую из этих систем можно масштабировать и защищать по отдельности. Однако в ­тестовом окружении ­запуск такого количества серверов будет излишним, поэтому, чтобы сэкономить, Vault и Consul лучше запускать в одной группе ASG, состоящей, скажем, из одного сервера и развернутой с использованием модуля vault-cluster. Если бы все правила группы безопасности Consul были описаны в модуле consul-cluster, вы бы не смогли применять их повторно (разве что вы вручную скопируете 20 с лишним правил) при развертывании Consul с модулем vault-cluster. Но, поскольку правила описаны в отдельном модуле, consul-security-group-rules, вы можете подключить их к vault-cluster или к кластеру почти любого другого типа.

Рис. 6.3. Архитектура HashiCorp Vault и Consul

Для простого приложения вроде Hello, World такого рода разбиение будет чрезмерным, но, если вы имеете дело с настоящей сложной инфраструктурой, выделение правил группы безопасности, политик IAM и других сквозных элементов в отдельные автономные модули — часто необходимый шаг для поддержки разных методик развертывания. Мы их использовали не только для Consul и Vault, но и для стека ELK (в промышленной среде мы запускали Elasticsearch, Logstash и Kibana в отдельных кластерах, а в тестовой — в одном и том же), Confluent Platform (в промышленной среде мы запускали Kafka, ZooKeeper, REST Proxy и Schema Registry в отдельных кластерах, а в тестовой — в одном и том же), стека TICK (в промышленной среде мы запускали Telegraf, InfluxDB, Chronograf и Kapacitor в отдельных кластерах, а в тестовой — в одном и том же) и т. д.