user_data = < #!/bin/bash echo "Hello, World" >> index.html echo "${data.terraform_remote_state.db.outputs.address}" >> index.html echo "${data.terraform_remote_state.db.outputs.port}" >> index.html nohup busybox httpd -f -p ${var.server_port} & EOF Чем длиннее становится скрипт в параметре user_data, тем более неряшливым выглядит ваш код. Встраивание одного языка программирования (bash) в другой (Terraform) усложняет поддержку обоих, поэтому давайте на секунду остановимся и вынесем bash-скрипт в отдельный файл. Для этого можно использовать встроенную функцию file и источник данных template_file. Рассмотрим их по отдельности. Terraform включает в себя ряд function_name (...) Возьмем для примера функцию format: format( Эта функция форматирует аргументы в ARGS в соответствии с синтаксисом sprintf, заданным в строке FMT41. Отличным способом поэкспериментировать со встроенными функциями является использование команды terraformconsole. Она создает интерактивную консоль, в которой вы можете попробовать синтаксис Terraform, запросить состояние вашей инфраструктуры и сразу же получить результаты: $ terraform console > format("%.3f", 3.14159265359) 3.142 Стоит отметить, что консоль Terraform предназначена только для чтения, поэтому не нужно волноваться о случайном изменении инфраструктуры или состояния. Существует целый ряд других встроенных функций для работы со строками, числами, списками и ассоциативными массивами42. К их числу относится функция file: file( Эта функция читает файл с путем PATH и возвращает его содержимое в виде строки. Например, вы могли бы сохранить скрипт пользовательских данных в файл stage/services/webserver-cluster/user-data.sh и загружать его следующим образом: file("user-data.sh") Подвох в том, что скрипту пользовательских данных для кластера веб-серверов необходима определенная динамическая информация из Terraform, включая порт сервера, а также адрес и порт базы данных. Когда скрипт был встроен в код Terraform, он получал эти значения с помощью ссылок и интерполяции. С функцией file это не сработает. Однако вместо этого вы можете воспользоваться источником данных template_file. Источник данных template_file принимает два аргумента: template (строка, которую нужно обработать) и vars (ассоциативный массив с переменными, которые должные быть доступны во время обработки). Он содержит одну выходную переменную, rendered, которая является результатом обработки template. Чтобы увидеть это в деле, добавьте следующий код источника template_file в файл stage/services/webserver-cluster/main.tf: data "template_file" "user_data" { template = file("user-data.sh") vars = { server_port = var.server_port db_address = data.terraform_remote_state.db.outputs.address db_port = data.terraform_remote_state.db.outputs.port } } Как видите, этот код присваивает параметрам template и vars содержимое скрипта user-data.sh и, соответственно, три переменные, которые нужны этому скрипту: порт сервера, адрес и порт базы данных. Для использования этих переменных нужно соответствующим образом обновить скрипт stage/services/webserver-cluster/user-data.sh: #!/bin/bash cat > index.html < DB address: ${db_address}Hello, World
DB port: ${db_port}
EOF
nohup busybox httpd -f -p ${server_port} &
Обратите внимание на несколько изменений в этом bash-скрипте по сравнению с оригиналом.
• Он ищет переменные с помощью стандартного синтаксиса интерполяции Terraform. Единственными переменными в данном случае являются те, что находятся в ассоциативном массиве vars источника template_file. Стоит отметить, что для доступа к ним не нужен никакой префикс: например, вместо var.server_port следует писать server_port.
• Теперь в скрипте можно заметить синтаксис HTML (скажем,
Замечание о внешних файлах
Одним из преимуществ выноса скрипта пользовательских данных в отдельный файл является возможность написания для него модульных тестов. Код теста может даже заполнить интерполированные значения с помощью переменных среды, поскольку для поиска последних bash использует тот же синтаксис, который в Terraform применяется для интерполяции. Вы можете написать автоматический тест для user-data.sh примерно такого вида:
export db_address=12.34.56.78
export db_port=5555
export server_port=8888
./user-data.sh
output=$(curl "http://localhost:$server_port")
if [[ $output == *"Hello, World"* ]]; then
echo "Success! Got expected text from server."
else
echo "Error. Did not get back expected text 'Hello, World'."
fi
Заключительным шагом будет обновление параметра user_data в ресурсе aws_launch_configuration. Присвойте ему обработанный выходной атрибут источника данных template_file:
resource "aws_launch_configuration" "example" {
image_id = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
security_groups = [aws_security_group.instance.id]
user_data = data.template_file.user_data.rendered
# Требуется при использовании группы автомасштабирования
# в конфигурации запуска.
# https://www.terraform.io/docs/providers/aws/r/launch_configuration.html
lifecycle {
create_before_destroy = true
}
}
Выглядит намного аккуратней, чем встраивание bash-скриптов!
Если развернуть этот кластер с помощью команды terraformapply, подождать, пока серверы не зарегистрируются в ALB, и открыть URL-адрес ALB в браузере, можно увидеть нечто похожее на рис. 3.12.
Ура! Ваш кластер веб-серверов теперь может программно обращаться к адресу и порту базы данных через Terraform. Если вы используете настоящий веб-фреймворк (вроде Ruby on Rails), можете задать адрес и порт в виде переменных среды или записать их в конфигурационный файл, чтобы их могла использовать ваша библиотека для работы с БД (как ActiveRecord).
Рис. 3.12. Кластер веб-серверов может программно обращаться к адресу и порту базы данных
Резюме
Причина, по которой вам следует уделять столько внимания изоляции, блокированию и состоянию, заключается в том, что IaC отличается от обычного программирования. При написании кода для типичного приложения большинство ошибок оказываются относительно несущественными и портят только небольшую его часть. Но когда вы пишете код для управления инфраструктурой, программные ошибки имеют более серьезные последствия, поскольку могут затронуть все ваши приложения вместе со всеми источниками данных, топологией сети и практически всем остальным. Поэтому при работе над IaC советую использовать больше «защитных механизмов», чем при написании обычного кода43.
Применение рекомендуемой структуры файлов и каталогов часто приводит к дублированию кода. Если вы хотите запускать кластер веб-серверов как в тестовой, так и в промышленной среде, как избежать копирования и вставки большого количества фрагментов между stage/services/webserver-cluster и prod/services/webserver-cluster? Ответ: использовать модули Terraform, которым посвящена глава 4.
34 Узнайте больше о гарантиях, которые дает S3, по адресу amzn.to/31ihjAg.
35 Ознакомьтесь с тарифами для S3 по адресу amzn.to/2yTtnw1.
36 Подробнее об именах бакетов S3 можно почитать по адресу bit.ly/2b1s7eh.
37 Ознакомьтесь с тарифами для DynamoDB по адресу amzn.to/2OJiyHp.
38 По адресу bit.ly/2lTsewM представлен яркий пример того, что может случиться, если не изолировать состояние Terraform.
39 Подробнее об этом читайте в документации Terragrunt по адресу bit.ly/2M48S8e.
40 Большинство командных оболочек в Linux/Unix/OS X сохраняют каждую вводимую вами команду в файл истории какого-то рода (например, ~/.bash_history). Но если начать команду с пробела, почти все оболочки не станут ее туда записывать. Имейте в виду, что эта возможность может быть отключена в вашей командной оболочке. Чтобы ее включить, придется присвоить переменной среды HISTCONTROL значение ignoreboth.
41 На странице golang.org/pkg/fmt/ можно найти документацию для синтаксиса sprintf.
42 На странице bit.ly/2GNCxOM представлен полный список встроенных функций.
43 Подробнее о защитных механизмах в ПО можно почитать по адресу bit.ly/2YJuqJb.
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии