$ terraform plan -var "server_port=8080"
То же самое можно сделать, добавив переменную среды вида TF_VAR_
$ export TF_VAR_server_port=8080
$ terraform plan
Если же вы не хотите держать в голове дополнительные аргументы командной строки при каждом выполнении команды plan или apply, можете указать значение по умолчанию:
variable "server_port" {
description = "The port the server will use for HTTP requests"
type = number
default = 8080
}
Чтобы использовать значение входной переменной в коде Terraform, следует воспользоваться выражением типа «ссылка на переменную», которое имеет следующий синтаксис:
var.
Например, так можно присвоить параметрам группы безопасности from_port и to_port значение переменной server_port:
resource "aws_security_group" "instance" {
name = "terraform-example-instance"
ingress {
from_port = var.server_port
to_port = var.server_port
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
Хорошей идеей будет использование одной и той же переменной при задании порта в скрипте user_data. Указать ссылку внутри строкового литерала можно с помощью строковой интерполяции, которая имеет следующий синтаксис:
"${...}"
Внутри фигурных скобок можно разместить любую корректную ссылку, и Terraform преобразует ее в строку. Например, вот как можно воспользоваться значением var.server_port внутри строки user_data:
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p ${var.server_port} &
EOF
Помимо входных переменных, Terraform позволяет определять и выходные. Для этого предусмотрен такой синтаксис:
output "
value =
[CONFIG ...]
}
NAME — это имя выходной переменной, а в качестве VALUE можно указать любое выражение Terraform, которое вы хотите вывести. CONFIG может иметь два дополнительных (и необязательных) параметра.
•description. Этот параметр всегда желательно применять для документирования того, как используется выходная переменная.
•sensitive. Если присвоить данному параметру true, Terraform не станет сохранять этот вывод в журнал после выполнения команды terraformapply. Это полезно, когда переменная содержит деликатный материал или конфиденциальные данные, такие как пароли или секретные ключи.
Например, вместо того, чтобы вручную бродить по консоли EC2 в поисках IP-адреса своего сервера, вы можете вывести его в виде выходной переменной:
output "public_ip" {
value = aws_instance.example.public_ip
description = "The public IP address of the web server"
}
Здесь мы опять ссылаемся на атрибуты, на этот раз на атрибут public_ip ресурса aws_instance. Если снова выполнить команду apply, Terraform не внесет никаких изменений (поскольку вы не меняли никакие ресурсы), но покажет вам в самом конце новый вывод:
$ terraform apply
(...)
aws_security_group.instance: Refreshing state... [id=sg-078ccb4f9533d2c1a]
aws_instance.example: Refreshing state... [id=i-028cad2d4e6bddec6]
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
public_ip = 54.174.13.5
Как видите, после выполнения terraformapply выходная переменная выводится в консоли, что может пригодиться пользователям Terraform (например, вы будете знать, какой IP-адрес нужно проверить после развертывания веб-сервера). Вы также можете ввести команду terraformoutput, чтобы вывести список всех выходных значений без применения каких-либо изменений:
$ terraform output
public_ip = 54.174.13.5
Чтобы посмотреть значение определенной выходной переменной, можно воспользоваться командой terraformoutput<ИМЯ_ПЕРЕМЕННОЙ>:
$ terraform output public_ip
54.174.13.5
Это будет особенно полезно при написании скриптов. Например, вы можете создать скрипт развертывания, который развертывает веб-сервер с помощью команды terraformapply, берет его публичный IP-адрес из terraformoutputpublic_ip и обращается к этому адресу, используя curl. В итоге получится проверка по горячим следам, которая подтвердит, что развертывание работает.
Входные и выходные переменные также являются неотъемлемыми составляющими при создании конфигурируемого инфраструктурного кода, пригодного к повторному применению. Подробнее об этом — в главе 4.
Развертывание кластера веб-серверов
Запуск одного сервера — хорошее начало. Однако в реальном мире это означает наличие единой точки отказа. Если этот сервер выйдет из строя или перестанет справляться с нагрузкой из-за слишком большого объема трафика, пользователи не смогут открыть ваш сайт. В качестве решения можно запустить кластер серверов: если один из них откажет, запросы допускается перенаправить к другому серверу, а размер самого кластера можно увеличивать и уменьшать в зависимости от трафика32.
Ручное управление таким кластером потребует много усилий. К счастью, как показано на рис. 2.9, AWS может позаботиться об этом за вас, используя группу автомасштабирования (англ. auto scaling group, или ASG). ASG автоматически выполняет множество задач, включая запуск кластера серверов EC2, мониторинг работоспособности каждого сервера, замену неисправных серверов и изменение размера кластера в зависимости от нагрузки.
Рис. 2.9. Вместо одного веб-сервера группа автомасштабирования запускает кластер веб-серверов
Первое, что нужно сделать при создании ASG, — это написать конфигурацию запуска, которая определяет, как нужно настроить каждый сервер EC2 в вашей группе. Ресурс aws_launch_configuration использует почти все те же параметры, что и aws_instance (только у двух из них отличаются имена: image_id вместо ami и security_groups вместо vpc_security_group_ids), поэтому вы можете их легко заменить:
resource "aws_launch_configuration" "example" {
image_id = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
security_groups = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p ${var.server_port} &
EOF
}
Теперь вы можете создать саму группу ASG, используя ресурс aws_autoscaling_group:
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.name
min_size = 2
max_size = 10
tag {
key = "Name"
value = "terraform-asg-example"
propagate_at_launch = true
}
}
Эта группа ASG включает в себя от двух до десяти серверов EC2 (сначала запускается только два), каждый из которых имеет тег terraform-asg-example. Обратите внимание, что ASG использует ссылку в качестве имени конфигурации запуска. Это приводит к проблеме: конфигурация запуска неизменяема, поэтому, если вы измените любой ее параметр, Terraform попытается заменить ее целиком. Обычно при замене ресурса Terraform сначала удаляет его старую версию и затем создает новую, но, поскольку ASG теперь ссылается на старый ресурс, Terraform не сможет его удалить.
Чтобы решить эту проблему, можно воспользоваться параметром жизненного цикла. Он поддерживается всеми ресурсами Terraform и определяет их создание, обновление и/или удаление. Особенно полезным параметром жизненного цикла является create_before_destroy. Если присвоить ему true, Terraform поменяет порядок замены ресурсов на противоположный. В итоге сначала будет создана замена (с обновлением всех ссылок таким образом, чтобы они указывали на нее, а не на старый ресурс) и только потом произойдет удаление старого ресурса. Добавьте раздел lifecycle в aws_launch_configuration, как показано ниже:
resource "aws_launch_configuration" "example" {
image_id = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
security_groups = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p ${var.server_port} &
EOF
# Требуется при использовании группы автомасштабирования
# в конфигурации запуска.
# https://www.terraform.io/docs/providers/aws/r/launch_configuration.html
lifecycle {
create_before_destroy = true
}
}
Для работы группы ASG требуется еще один параметр: subnet_ids. Он определяет подсети VPC, в которых должны быть развернуты серверы EC2 (справочная информация о подсетях дается во врезке «Сетевая безопасность» на с. 79). Каждая подсеть находится в изолированной зоне доступности AWS (то есть в отдельном вычислительном центре), поэтому, развертывая свои серверы по разным подсетям, вы гарантируете, что ваш сервис продолжит работать, даже если некоторые из вычислительных центров выйдут из строя. Список подсетей можно прописать прямо в коде, но такое решение будет сложно поддерживать и переносить. Вместо этого лучше использовать соответствующий источник данных для получения списка подсетей, принадлежащих к вашей учетной записи AWS.
Источник данных представляет собой фрагмент информации, доступной сугубо для чтения, который извлекается из провайдера (в нашем случае из AWS) при каждом запуске Terraform. Добавляя источник данных в конфигурацию Terraform, вы не создаете ничего нового. Это просто возможность запросить информацию из API провайдера, чтобы сделать ее доступной для остального кода Terraform. Каждый провайдер предоставляет целый ряд источников. Например, провайдер AWS позволяет запрашивать данные о VPC и подсетях, идентификаторы AMI, диапазоны IP-адресов, идентификатор текущего пользователя и многое другое.