linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/kb/Advanced/tf-dag-internals

kb/advanced ── Advanced ── advanced

DAG в Terraform, как строится граф зависимостей

Terraform строит DAG (directed acyclic graph) из ресурсов и их зависимостей. На plan/apply граф «обходится» в топологическом порядке, параллельно где можно (limit, `-parallelism`, дефолт 10). Implicit dependencies, через interpolation; explicit, `depends_on`. Цикл = `Cycle: ...` ошибка. Понимание DAG объясняет почему apply иногда «застревает» и как ускорить большой граф.

view as markdownaka: terraform-dag, terraform-graph-internals, terraform-walk-order

Что такое DAG в Terraform

DAG = Directed Acyclic Graph. Вершины, ресурсы, провайдеры, модули, переменные, outputs. Рёбра, зависимости. Граф строится для каждой операции: init, plan, apply, destroy.

Идея проста: чтобы создать ресурс B, который ссылается на ресурс A, сначала нужен A. Граф фиксирует это в виде стрелок.

Источники зависимостей

1. Implicit (через interpolation)

hcl
resource "aws_iam_role" "lambda" { ... }
resource "aws_lambda_function" "demo" {
  role = aws_iam_role.lambda.arn   # ← implicit dep
}

Terraform парсит выражения, видит ссылку, добавляет ребро: aws_iam_role.lambda → aws_lambda_function.demo.

2. Explicit (depends_on)

hcl
resource "aws_lambda_function" "demo" {
  depends_on = [aws_iam_role_policy_attachment.lambda]
  # ...
}

Используется когда дeps есть, но через значение не выражены например, IAM-policy создаётся параллельно, но Lambda должна стартовать только после неё. См. tf-depends-on.

3. Provider

hcl
provider "aws" { ... }
resource "aws_s3_bucket" "x" {
  provider = aws.eu  # ← dep на конкретный alias
}

Каждый ресурс зависит от своего провайдера. Это implicit.

4. Модули

hcl
module "network" { ... }
module "app" {
  vpc_id = module.network.vpc_id  # ← dep на network
}

Ресурсы внутри module.app гипотетически могут стартовать когда хотят, но Terraform учитывает explicit dep между модулями через interpolation outputs.

terraform graph, посмотреть DAG

bash
terraform graph | dot -Tpng > graph.png

dot, Graphviz; в Alpine ставится через apk add graphviz.

Альтернатива, rover или tf-summarize, см. tf-rover-visualization.

Walk-порядок

Терраформ обходит граф в топологическом порядке. Ресурсы без зависимостей идут первыми, потом всё что от них зависит. Параллельно где возможно.

Пример:

aws_iam_role.lambda
├── aws_iam_role_policy_attachment.lambda
└── aws_lambda_function.demo
    └── aws_lambda_alias.live
random_password.api_key
└── aws_lambda_function.demo (через env)
aws_s3_bucket.logs    (нет deps на lambda)

Walk-порядок (упрощённо):

  1. Layer 1 (параллельно): aws_iam_role.lambda, aws_s3_bucket.logs, random_password.api_key.
  2. Layer 2 (после Layer 1): aws_iam_role_policy_attachment.lambda, aws_lambda_function.demo.
  3. Layer 3: aws_lambda_alias.live.

Parallelism

По умолчанию Terraform делает до 10 операций параллельно. Каждый AWS-API вызов идёт в своём goroutine.

bash
terraform apply -parallelism=20   # больше параллелизма
terraform apply -parallelism=1    # последовательно (debug)

Больше, быстрее, но рискуешь поймать AWS API throttling (ThrottlingException). Меньше, медленнее, но безопаснее для API с rate-limit'ами.

Optimal зависит от провайдера и масштаба state'а. 10, норм по умолчанию. Для огромных стеков иногда 20-30; для AWS Organizations manipulation, 1-2.

Циклы

Если два ресурса ссылаются друг на друга, Terraform не может выбрать порядок.

Пример (плохо):

hcl
resource "aws_security_group" "web" {
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb.id]  # ← dep на alb
  }
}
resource "aws_security_group" "alb" {
  ingress {
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]  # ← dep на web
  }
}

Error: Cycle: aws_security_group.web, aws_security_group.alb.

Решение, aws_security_group_rule resource'ы отдельно:

hcl
resource "aws_security_group" "web" {}
resource "aws_security_group" "alb" {}
resource "aws_security_group_rule" "web_from_alb" {
  type                     = "ingress"
  security_group_id        = aws_security_group.web.id
  source_security_group_id = aws_security_group.alb.id
  # ...
}
resource "aws_security_group_rule" "alb_from_web" {
  type                     = "ingress"
  security_group_id        = aws_security_group.alb.id
  source_security_group_id = aws_security_group.web.id
  # ...
}

Теперь два security_group'а независимы, rules, отдельные ресурсы без cycle'а.

Debug cycle'а:

bash
terraform graph -draw-cycles | dot -Tsvg > cycle.svg

Циклы подсвечены красным. См. tf-graph.

Walk-state vs plan vs apply

ОперацияЧто в DAG
initЗагрузка провайдеров и модулей. DAG строится частично.
validateПолный DAG, но без API-вызовов.
planПолный DAG + refresh-фаза для каждого ресурса. Считает diff.
applyWalk DAG, для каждой ноды, provider call. Parallel где можно.
destroyReverse DAG, топологический порядок наоборот.

Destroy сложнее: нужно удалить child'ов до parent'а. Reverse-walk.

Какие зависимости НЕ ловятся автоматически

  • Side effects через external script. data "external" исполняет скрипт, но Terraform не знает что внутри. Если скрипт читает из S3-бакета, который только что создан тем же plan'ом может race-condition'ить.

  • Ресурсы, создаваемые через provisioners. local-exec / remote-exec запускаются после ресурса, но если они что-то создают в облаке (через CLI), Terraform этого не видит.

  • Через provider-default'ы. default_tags { ... }, теги применяются ко всем ресурсам, но в DAG нет ребра «всё зависит от default_tags».

  • terraform_remote_state. Зависимость на другой stack, это data source, читается на каждом plan. Граф этого не моделирует cross-stack.

Подводные камни

  • Цикл сложно вычислить визуально. На большом графе circular-deps через 5 ресурсов руками не найдёшь. terraform graph -draw-cycles , единственный sane debug.

  • depends_on создаёт «жёсткое» ребро. Использовать только когда implicit dep нет. Лишние depends_on замедляют apply (parallelism падает).

  • Module-level depends_on (TF 1.0+). Можно поставить на весь модуль , но тогда все ресурсы модуля зависят от него. Часто overkill.

  • count/for_each + dynamic, DAG строится по элементам. Каждый aws_s3_bucket.x[0], [1], [2], своя нода в графе.

  • Большой граф = долгий plan. Refresh каждой ноды, API call. 5000 ресурсов = 5000 GET-вызовов. См. tf-large-scale-state про разбиение.

  • Parallelism vs API throttling. На larger stacks -parallelism=20 может ускорить, но AWS отвечает 429. Эмпирически подбирай.

§ команды

bash
terraform graph | dot -Tpng > graph.png

DAG в визуальном виде. Требует graphviz (dot).

bash
terraform graph -draw-cycles | dot -Tpng > cycles.png

Циклы подсвечены красным. Главный debug-инструмент.

bash
terraform apply -parallelism=20

Параллелизм 20 (default 10). Быстрее, риск throttling.

bash
terraform apply -parallelism=1

Последовательно, для debug, чтобы видеть точный порядок.

§ см. также

  • tf-depends-onЗависимости ресурсов: явные и неявныеTerraform автоматически вычисляет порядок создания ресурсов из ссылок в HCL (неявные зависимости). Когда такой ссылки нет, но порядок важен: есть depends_on. Использовать редко: чаще это сигнал что архитектура спорная.
  • tf-graphterraform graph: граф зависимостей ресурсов`terraform graph` выводит directed graph (DAG) зависимостей ресурсов в формате Graphviz dot. Из него видно, что от чего зависит, и почему Terraform делает что-то именно в таком порядке. Используется при дебаге cycle-ошибок и для понимания крупных проектов.
  • tf-large-scale-stateБольшой state, иерархия, blast-radius, namingОдин state на 5000 ресурсов = боль: refresh минуты, lock-contention, любой apply трогает всё. Решение, иерархия state'ов: network/iam отдельно, apps отдельно, между ними `terraform_remote_state`. Blast-radius, критерий разделения. Naming для бакетов и lock-таблиц предсказуемая структура.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки