lesson ── terraform-beginner ── ~15 мин ── 5 шагов
Терраформ молчалив по умолчанию. Apply упал, короткое сообщение. План странный, догадывайся. Это норма для CLI, но за этим есть полные диагностические логи и инструменты, которые их читают.
В этом уроке поработаешь с тремя сценариями: сломанная зависимость (cycle), непонятная ошибка от провайдера (нужны TF_LOG), и непредсказуемый план (нужен graph). К концу, будешь знать что делать когда «у меня что-то не работает и я не понимаю что».
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
Цикл, это когда A зависит от B, а B от A. Граф Terraform, ациклический по контракту, такой код упадёт на plan.
Создай в ~/tf-debug файл main.tf:
resource "aws_s3_bucket" "first" { bucket = "linuxlab-cycle-first-${random_id.suffix.hex}" tags = {PairedWith = aws_s3_bucket.second.bucket
}
}
resource "aws_s3_bucket" "second" { bucket = "linuxlab-cycle-second-${random_id.suffix.hex}" tags = {PairedWith = aws_s3_bucket.first.bucket
}
}
resource "random_id" "suffix" {byte_length = 4
}
first ссылается на second.bucket, second, на first.bucket.
Кто из них создавать первым? Никто не знает.
Запусти plan:
cd /home/student/tf-debug
terraform init -input=false
terraform plan
Получишь:
Error: Cycle: aws_s3_bucket.first, aws_s3_bucket.second
Это защита, не баг. Terraform не угадывает, он отказывается.
Если не получаешь Cycle: проверь что обе ссылки на месте (grep PairedWith main.tf).
✓ Cycle поймана. Теперь визуализируем граф чтобы увидеть проблему.
Terraform умеет напечатать граф в формате Graphviz dot:
terraform graph
Это текст. Чтобы увидеть картинку. Нужно graphviz (dot команда).
В sandbox он установлен:
terraform graph | dot -Tpng > /tmp/graph.png
Файл /tmp/graph.png содержит визуализацию. В нашем случае ты
увидишь две стрелки между aws_s3_bucket.first и
aws_s3_bucket.second, туда и обратно. Это и есть цикл.
С TF 1.4+ есть подсветка циклов прямо в команде:
terraform graph -draw-cycles
Узлы в циклах помечаются красным в выводе dot.
Подробнее, tf-graph.
Если `dot: command not found`: graphviz не установлен. В sandbox должен быть, но если падает: пропусти этот шаг и иди дальше.
✓ Граф напечатан. Теперь починим цикл.
Самый чистый способ разорвать цикл, вынести зависимое значение в третий ресурс или в local. Замени main.tf на:
resource "random_id" "suffix" {byte_length = 4
}
locals { first_name = "linuxlab-cycle-first-${random_id.suffix.hex}" second_name = "linuxlab-cycle-second-${random_id.suffix.hex}"}
resource "aws_s3_bucket" "first" {bucket = local.first_name
tags = {PairedWith = local.second_name
}
}
resource "aws_s3_bucket" "second" {bucket = local.second_name
tags = {PairedWith = local.first_name
}
}
Теперь оба бакета зависят от random_id, но не друг от друга.
Имена парных бакетов в тегах берутся из locals, это просто
строки, не атрибуты ресурсов.
terraform plan
terraform apply -auto-approve
Plan покажет 2 to add (random_id уже создан, или будет), apply
пройдёт.
Не забудь убрать оригинальные перекрёстные ссылки `aws_s3_bucket.X.bucket`: они и были источником цикла.
✓ Цикл разорван, оба бакета созданы. Это типичный паттерн: вынести общее имя в local.
Подкинь намеренную ошибку, попробуй создать бакет с невалидным именем. S3 не разрешает заглавные буквы и спецсимволы.
Создай файл bad.tf рядом:
resource "aws_s3_bucket" "broken" { bucket = "LinuxLab-INVALID-${random_id.suffix.hex}"# ^^^^^^^^^^^^^^^^^^^^^^^^ заглавные буквы недопустимы в S3
}
Запусти apply с обычным выводом:
terraform apply -auto-approve
Получишь короткое сообщение об ошибке от провайдера, в духе "InvalidBucketName" или "BucketAlreadyExists" (LocalStack может выдать чуть другое). Что именно не так, не очевидно.
Теперь то же самое, но с TF_LOG=DEBUG и сохранением:
TF_LOG=DEBUG terraform apply -auto-approve 2>&1 | tee /tmp/tf.log
Файл /tmp/tf.log содержит весь диагностический вывод. Ищем
HTTP-ответ от облака:
grep -A 5 'HTTP/1.1 4' /tmp/tf.log | head -30
Увидишь оригинальное сообщение S3 API, обычно с XML-блоком
<Error><Code>...</Code><Message>...</Message></Error>. Этот
Message понятнее, чем то что Terraform показал в обычном выводе.
См. tf-log-debug про уровни логирования и фильтрацию.
Если grep ничего не находит. TF_LOG не сработал. Проверь что переменная установлена: `echo $TF_LOG`. Должно быть `DEBUG`.
✓ TF_LOG включился, дебаг-вывод сохранён. В реальной работе это первый шаг при странных ошибках.
OpenTofu держит CLI и state совместимыми с Terraform по командам
этого шага: миграция обычно проходит через mv .terraform .terraform.bak; tofu init -upgrade. Но при первом переходе
сделай backup state и прогон на feature-branch - расхождения
концентрируются в новых фичах (variables в backend,
state-encryption, OCI registry-backed модули). См.
tf-opentofu-parity для полной матрицы.
Удали bad.tf:
rm /home/student/tf-debug/bad.tf
Применим. Terraform увидит что broken нет в HCL и снесёт его
из state (если он попал туда; обычно при ошибке создания не
попадает):
terraform apply -auto-approve
terraform plan -detailed-exitcode
echo "exit: $?"
Должно быть exit: 0. Это главный инвариант: после apply повторный
plan чистый.
Закрепили: каждый раз когда apply падает или plan непонятный,
TF_LOG=DEBUG, графа, поиск 4xx в логах. Это рутина, не
героизм.
Если plan не чистый: посмотри какие изменения он показывает (`terraform plan` без флагов). Скорее всего что-то осталось от broken.
✓ State и HCL совпадают. Beginner-трек закрыт. Можешь возвращаться, можешь ждать intermediate.
Когда что-то не работает и непонятно почему:
terraform validate, синтаксис ли это? Опечатка?terraform fmt, может стиль косвенно мешает (редко, но бывает).terraform plan -detailed-exitcode, есть ли вообще
изменения? Может ты уже всё применил.terraform graph -draw-cycles, может цикл?TF_LOG=DEBUG terraform apply 2>&1 | tee /tmp/tf.log,
полный дебаг-лог.grep -A 5 'HTTP/1.1 4' /tmp/tf.log, что говорит облако?terraform console, какие реальные значения у переменных
и выражений?Этот порядок, от дешёвого к дорогому. Не лезь сразу в TF_LOG если ошибка, простая опечатка которую validate бы нашёл за секунду.
Три инструмента дебага. TF_LOG=DEBUG, для непонятных ошибок от провайдера. terraform graph, для разбора зависимостей и циклов. Чтение HTTP-запросов в логах, для случаев, когда сообщение Terraform скрывает суть, а облако даёт оригинальную ошибку.
команды
TF_LOG=DEBUG terraform apply 2>&1 | tee tf.logлог + сохранение для разбораterraform graph | dot -Tpng > graph.pngвизуализация графа зависимостейterraform graph -draw-cyclesTF 1.4+: подсветить циклыgrep -A 3 'HTTP/1.1 4' tf.logответы 4xx от облака: обычно понятнееконцепции