Переменные, проверки и HTML-шаблон
План помогает читать изменения, но до сих пор настройки «Контура» находятся прямо в ресурсе. Теперь выделим входные параметры: префикс имени, внешний порт и заголовок страницы. Один набор инфраструктурных блоков сможет выражать разные локальные варианты. Одновременно добавим HTML-шаблон, чтобы сайт показывал свою принадлежность лаборатории, а не стандартную страницу Nginx.
Переменная Terraform — именованный вход корневого модуля. Она не является изменяемой ячейкой программы и не получает новое значение после создания ресурса. Выбирается одно значение для расчёта конфигурации. Из этого значения строятся выражения, например имя контейнера. Тип и validation описывают допустимую область входа, но не удостоверяют внешние факты: свободен ли порт, существует ли Docker daemon, разрешён ли запуск на выбранном хосте.
Ограничения на значения
В variables.tf задаются три входа. Для prefix разрешены маленькие латинские буквы, цифры и дефис; первый символ должен быть буквой. Ограничение длины удерживает имена в понятных пределах. Такой учебный контракт намеренно уже возможностей Docker. Он позволяет использовать префикс в именах сети и контейнеров без дополнительных правил преобразования и помогает читателю сразу увидеть принадлежность ресурсов.
Порт имеет тип number, но это ещё не целое значение. Поэтому проверка включает диапазон и сравнение с floor. Число 8088.5 нельзя использовать как TCP-порт, хотя оно соответствует общему числовому типу. Диапазон начинается с 1024, потому что лаборатория не нуждается в привилегированном адресе. Сама проверка не обещает доступность порта; она только отсеивает входы, которые нельзя осмысленно передать дальше.
Полный файл variables.tf для этого состояния:
variable "prefix" {
type = string
default = "kontur"
validation {
condition = can(regex("^[a-z][a-z0-9-]{1,24}$", var.prefix))
error_message = "Use a lowercase prefix, 2–25 characters."
}
}
variable "port" {
type = number
default = 8088
validation {
condition = var.port >= 1024 && var.port <= 65535 && floor(var.port) == var.port
error_message = "Port must be an integer from 1024 to 65535."
}
}
variable "title" {
type = string
default = "Kontur local web"
validation {
condition = can(regex("^[A-Za-z0-9 ._-]{1,80}$", var.title))
error_message = "The lab title permits ASCII letters, numbers, spaces, dot, underscore and dash."
}
}
Полный файл site/index.html.tftpl для этого состояния:
<!doctype html>
<html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width">
<title>${title}</title><main><h1>${title}</h1><p>Local Terraform laboratory.</p></main></html>
Полный файл main.tf для этого состояния:
resource "docker_container" "web" {
name = "${var.prefix}-web"
image = docker_image.web.image_id
ports {
internal = 80
external = var.port
ip = "127.0.0.1"
}
upload {
file = "/usr/share/nginx/html/index.html"
content = templatefile("${path.module}/site/index.html.tftpl", { title = var.title })
}
}
Заголовок ограничен набором ASCII-символов, безопасных для прямой вставки в наш простой HTML. templatefile не выполняет HTML-экранирование автоматически. Если разрешить произвольную строку с угловыми скобками, читатель сможет получить разметку вместо текста. Для публичного редактора контента понадобится отдельная модель безопасного вывода; здесь узкий учебный алфавит удерживает пример понятным. Эта граница записана в validation и названа в документации, а не оставлена неявным предположением.
Передача данных в шаблон
Файл site/index.html.tftpl содержит полный короткий документ. В нём одна подстановка ${title}, используемая для заголовка вкладки и h1. Функция templatefile получает путь и объект переменных. Привязка к path.module делает расположение шаблона независимым от того, откуда пользователь вызвал процесс. Если бы путь вычислялся относительно случайной текущей директории, перенос рабочего каталога мог бы привести к ошибке чтения.
В ресурс контейнера добавлен блок upload: он записывает результат шаблона по пути /usr/share/nginx/html/index.html перед запуском. Мы не используем привязку директории хоста и не предполагаем, что контейнер видит дерево проекта. Это полезное различие для читателя, знакомого с Docker volume. В данной версии контейнер получает конкретные байты конфигурационного HTML; изменение шаблона затем является изменением намерения ресурса, а не незаметным редактированием живого файла.
Передавайте входы через файл, показанный как terraform.tfvars.example, после собственной настройки и явного указания -var-file. Суффикс .example выбран специально: Terraform не должен случайно подхватить демонстрационные параметры как реальные локальные настройки. Если переименовать файл в автоматическое имя, меняется способ загрузки. Для первых опытов явный путь в команде делает происходящее легче заметить. Настоящие локальные файлы с чувствительными значениями не попадут в Git.
Изменение одной настройки
Пусть у читателя занят порт 8088. Он может задать 8092, сохранив префикс и заголовок. Новый URL будет использовать 8092 на хосте, однако Nginx продолжит слушать внутренний 80. Этот пример показывает назначение отдельного входа: человек меняет причину, а выражения автоматически переносят значение в нужное место. Если одновременно вручную исправить ресурс и переменную, можно создать противоречивые источники истины.
Другой вариант меняет только title на Kontur documentation. Ожидаемое содержимое HTTP-страницы изменится, а её имя контейнера и порт останутся прежними по конфигурации. Но способ применения такого изменения зависит от поведения провайдера: блок загрузки может привести к замене ресурса. Поэтому «изменили только текст» не означает «процесс точно не будет остановлен». Реальный план остаётся обязательным источником сведений о предполагаемом действии.
Разберём ошибку шаблона. Если переименовать вход в page_title, а в объекте для templatefile оставить ключ title, сам файл продолжит ожидать старый ключ. Если же изменить ключ объекта, забыв подстановку в шаблоне, вычисление не найдёт нужную переменную. Эти имена относятся к небольшому контракту между HCL и шаблоном. Сохраняйте их согласованно; наличие одинакового названия в разных файлах здесь не случайная косметика.
Полезно также различать отсутствие переменной и пустое значение. Значение по умолчанию используется, когда вход не задан; пустая строка является отдельным переданным значением и проходит через validation. Для префикса она будет отвергнута. Такой результат лучше молчаливой попытки создать контейнер с непонятным именем. В больших проектах аналогичный принцип помогает остановить ошибочную конфигурацию раньше внешнего изменения.
Теперь «Контур» имеет три явных входа и собственную страницу. В исходниках достаточно изменить один параметр, чтобы понять новую цель, а HTML строится из того же набора данных. Следующий урок добавит именованную сеть и выходные значения. Они покажут обратную сторону контракта: не только что модуль принимает, но и какие сведения он возвращает человеку или другому модулю.
Почему входной файл содержит пример
Файл с суффиксом .example можно безопасно читать до подготовки среды. Он описывает форму входов и помогает заметить опечатку: external_port не заменяет объявленную port. Если передать неизвестный или неверно названный вход, ожидаемого изменения не получится. Не пытайтесь исправлять это дублированием настроек в ресурсе; сначала сопоставьте имя объявления и имя переданного значения.
Заголовок страницы не предназначен для пользователя из интернета. Мы явно ограничили его учебным алфавитом. Если в будущем он приходит из CMS, правильное решение состоит в изменении контракта вывода и экранировании для конкретного HTML-контекста. Одно выражение Terraform не превращает произвольную строку в безопасную разметку. Так маленький шаблон становится поводом понять границу между параметром инфраструктуры и содержимым настоящего сайта.
Оглавление серии, общая подборка и полный архив исходников. Для этого урока выберите lesson-04.
Первичные источники: Версии Terraform, Учебник Terraform и Docker, Схема Docker container 3.6.2, Схема Docker image 3.6.2, Адреса и moved, Локальный backend, Import, Refresh-only plan.
Редакционный статус: локальный черновик. Учебные команды, приложение и проверки не запускались; описанные результаты ожидаемые. Профиль версий закреплён, разрешённый lockfile не создавался.