# Ждем, пока как минимум столько серверов не пройдут проверку
# работоспособности, прежде чем считать развертывание 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 в отдельных кластерах, а в тестовой — в одном и том же) и т. д.