Terraform: декларативное описание локальной инфраструктуры
В учебном проекте «Контур» мы опишем локальный веб-сервер так, чтобы его устройство можно было читать, обсуждать и изменять вместе с исходниками. Начнём с модели Terraform: пока без создания контейнеров. Для чтения нужны основы HTTP, представление о портах и умение работать с файлами. Docker уже должен быть установлен отдельно; установка среды не является скрытым шагом этого примера.
Обычная последовательность команд рассказывает, что оператор сделал вчера. Декларативная конфигурация описывает, что должно существовать сейчас. Это различие особенно заметно после перерыва: фраза «создать ещё один сервер» оставляет вопрос, сколько их уже есть, а объявление одного ресурса задаёт точное намерение. Terraform использует это намерение вместе со сведениями о прежнем управлении и данными провайдера.
Три разных представления инфраструктуры
Представим страницу ProfessorWeb на локальном Nginx. В конфигурации будет указан контейнер с определённым именем и портом. Docker хранит настоящий контейнер: его идентификатор, сетевые настройки и процесс. Состояние Terraform связывает адрес объявленного ресурса с идентификатором этого объекта. Ни один из этих трёх слоёв не заменяет остальные. Файл конфигурации не доказывает, что сервер запущен; существующий сервер не доказывает, что Terraform умеет им управлять.
Состояние иногда ошибочно считают кешем, который можно удалить при непонятной ошибке. Но после удаления связи инструмент уже не знает, какой контейнер был создан для конкретного объявления. Настоящий контейнер продолжит существовать, а следующая попытка создания может столкнуться с занятым именем. Поэтому исходники хранятся в Git, а рабочее состояние имеет отдельное хранение и ограниченный доступ. В первом уроке состояния ещё нет: мы сохраняем только описание требований.
В каталоге lesson-01 файл versions.tf целиком задаёт профиль среды. Имя файла удобно для человека; Terraform читает все файлы с расширением .tf в текущем корневом модуле. Разбиение на versions.tf и main.tf позднее не создаёт последовательности выполнения. Нельзя ожидать, что файл с более ранним именем будет обработан первым. Зависимости задаются выражениями между ресурсами.
Полный файл versions.tf для этого состояния:
terraform {
required_version = "= 1.12.2"
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "= 3.6.2"
}
}
}
provider "docker" {}
Секция terraform содержит требования к самому инструменту и к провайдеру. Провайдер — адаптер, который умеет читать и изменять ресурсы определённой платформы. Здесь docker является локальным именем, а kreuzwerker/docker — адресом пакета. Версия закреплена точно: это делает учебный профиль понятным, хотя не превращает его автоматически в лучший выбор для любого будущего проекта. Обновление профиля надо обсуждать как изменение исходников.
Где заканчивается текст и начинается действие
Блок provider "docker" {} оставлен без явного адреса удалённого сервера. Он рассчитан на выбранную читателем локальную Docker-среду. Однако слово «локальная» не гарантируется одним пустым блоком: настройки окружения процесса способны направить соединение к другому daemon. Перед дальнейшими командами читатель обязан выяснить фактический endpoint. Не публикуйте Docker API в сеть ради удобства соединения; доступ к нему позволяет управлять контейнерами хоста.
Команда terraform init в будущей самостоятельной работе устанавливает провайдер и подготавливает рабочий каталог. Это уже обращение к источникам пакетов, а не чтение Markdown. Она также создаёт файл фиксации зависимостей .terraform.lock.hcl. В приложенном архиве такого разрешённого файла нет, потому что установка не выполнялась. Не нужно выдавать вручную написанные хеши за результат настоящей установки. После независимой подготовки среды этот файл станет частью воспроизводимого набора исходников.
Каталог .terraform содержит служебные данные конкретной рабочей копии. Его нельзя переносить вместо исходников между компьютерами. В .gitignore мы отдельно исключили состояние, резервные копии состояния, сохранённые планы и локальные переменные. Файл блокировки зависимостей этим правилом не исключается: он описывает выбранные пакеты, тогда как состояние описывает конкретные управляемые объекты. Такое различие помогает понять, какие изменения коллега должен увидеть в Git.
Намерение без созданного сервера
Результат этого урока — не работающий сайт, а минимальный каталог с ясными требованиями. Если открыть конфигурацию, вы сможете назвать инструмент, провайдер и ожидаемый тип среды, но не найдёте ни одного ресурса. Это корректное промежуточное состояние. Следующий урок введёт образ и контейнер, после чего появится предмет для плана изменений. Не добавляйте облачный провайдер «на будущее»: у него другие полномочия и возможные расходы.
Сравните два возможных изменения. Замена версии провайдера меняет программу, которая интерпретирует конфигурацию; добавление ресурса меняет инфраструктурное намерение. Эти операции могут происходить в одном коммите, но при разборе лучше понимать их по отдельности. Если после обновления возникла новая разница в плане, сначала надо выяснить, появилась ли она из-за схемы провайдера или из-за действительно изменённого ресурса.
Ещё один полезный мысленный опыт: скопируйте только versions.tf в пустую папку. Получится новое описание требований, а не второй экземпляр уже управляемой инфраструктуры. Скопируйте туда настоящий state, и появится другое последствие: две рабочие копии начнут претендовать на одни объекты. Именно поэтому независимые окружения в нашем курсе получат свои имена, порты и отдельное состояние, а перенос существующего состояния будет разобран как самостоятельная операция.
Чтение профиля другим человеком
Представьте, что файл получили два коллеги. Один установил подходящий Terraform, второй — более новую версию с изменённой схемой. Точное требование остановит второй вариант раньше управления инфраструктурой и даст предметный повод обсудить обновление. Это лучше молчаливой надежды на одинаковое поведение. При этом ограничение версии не является оценкой безопасности: старый профиль надо независимо пересмотреть перед настоящим использованием.
Ещё один читатель работает в Windows с иной схемой подключения Docker. Пустой provider-блок не обещает одинаковую настройку сокета на всех системах. Он получает отдельную задачу подготовки endpoint своей локальной среды. Исходники остаются полезными, потому что описывают один и тот же ресурсный контракт; параметры соединения рассматриваются как свойство рабочего окружения. Таким образом, воспроизводимость начинается с явного профиля и явных границ, а не с утверждения, что одна команда подходит каждому компьютеру.
Не путайте расширение файла с разрешением на применение. Наличие .tf сообщает инструменту, какие декларации читать; человеческое решение о создании ресурсов находится вне имени файла. Для первой лаборатории удобно отдельно записать допустимую область действий: локальный daemon, один веб-сервер и отсутствие публичного сетевого входа.
Оглавление серии, общая подборка и полный архив исходников. Для этого урока выберите lesson-01.
Первичные источники: Версии Terraform, Учебник Terraform и Docker, Схема Docker container 3.6.2, Схема Docker image 3.6.2, Адреса и moved, Локальный backend, Import, Refresh-only plan.
Редакционный статус: локальный черновик. Учебные команды, приложение и проверки не запускались; описанные результаты ожидаемые. Профиль версий закреплён, разрешённый lockfile не создавался.