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
  • Собеседование
Cluster

← все кластеры

Рефакторинг и большие state

Что делать когда монолитный root вырос до 3000 ресурсов и plan занимает семь минут. moved/removed-блоки, разбиение state на workspace'ы и отдельные root'ы, zero-downtime миграция. Senior-вопросы из реальных миграций в банках и крупных продуктовых командах.

5 вопросов · ~26 мин чтения

Questions

На этой странице

  1. 01Зачем нужен `moved {}` блок и чем он лучше `terraform state mv`?
  2. 02Что такое `removed {}` блок и когда он нужен?
  3. 03Монолитный state на 3000 ресурсов, plan семь минут. Как разбить?
  4. 04Как мигрировать ресурс между типами без downtime? Пример: ALB на новый VPC.
  5. 05`terraform plan` занимает 7 минут на 5000 ресурсах. Что делать?

#moved-block-vs-state-mv

intermediateиногда

Зачем нужен `moved {}` блок и чем он лучше `terraform state mv`?

Что отвечать

`moved {}` объявляет переезд ресурса в HCL: «то, что было `module.old.x`, теперь `module.new.x`». На plan Terraform видит блок и переносит запись в state как часть change set, без destroy+create. Главное отличие от `state mv`: декларативность. Блок коммитится в репо, ревьюится в PR, виден в `terraform plan` коллеге. После того как все коллеги сделали `apply` - можно удалить блок через несколько релизов. `state mv` мутирует state локально, в PR никак не отражается, коллегам нужно вручную делать то же самое - источник рассинхрона.

Что хотят услышать

Senior должен: - назвать что moved-блок появился в 1.1 как штатный механизм рефакторинга - объяснить «грейс-период»: блок остаётся в HCL несколько релизов пока все среды его применят, потом удаляется - сказать что moved работает только для адресов: переименовать ресурс, перенести в child-модуль, поменять префикс. Не работает для смены типа ресурса или провайдера - упомянуть что один блок описывает переезд одного ресурса; для массового рефакторинга - несколько блоков, ревью требует внимания

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

  • ✗ Использовать `state mv` в команде - твой состояние знает, состояние коллег не знает; на их apply увидят destroy+create
  • ✗ Удалить moved-блок до того как все среды его проиграли - неприменённые ещё среды получат destroy+create
  • ✗ Думать что moved может изменить тип ресурса (`aws_s3_bucket` → `aws_s3_bucket_v2`) - не может; для смены типа нужен import + rm

Follow-up

  • ? Сколько релизов держать moved-блок в HCL?
  • ? Как переименовать ресурс между провайдерами (`aws` → `awscc`)?
  • ? Что произойдёт если два moved-блока противоречат друг другу?

Глубина в базе знаний

  • moved блок: переименование без destroy
  • state mv, state rm, state pull/push: ручные операции
  • Паттерны рефакторинга: count→for_each, split files, extract module
tags: refactor, moved-blockbook: mastering.terraform.epub:ch12

#removed-block

intermediateредко

Что такое `removed {}` блок и когда он нужен?

Что отвечать

`removed {}` (с 1.7) - декларативный способ убрать ресурс из управления Terraform'ом, не уничтожая его в провайдере. Альтернатива `terraform state rm`, который мутирует state локально. Пишешь блок с адресом ресурса и `lifecycle { destroy = false }` - на следующем apply Terraform удаляет запись из state, ресурс в AWS остаётся жив. Удобно когда передаёшь ресурс другой команде или другому root-модулю: они импортнут его себе, ты removed'нешь у себя - в рамках одного PR-цикла видно обеим сторонам.

Что хотят услышать

Senior должен: - назвать что removed-блок - аналог moved-блока, но «во вне»: ресурс уходит из управления, не переезжает - различить два варианта: `destroy = false` (оставить ресурс в провайдере) и `destroy = true` (удалить и из state, и из провайдера; это просто запланированный destroy) - объяснить кейс «передачи владения»: команда A пишет removed, команда B пишет import - в одном time-window - упомянуть что removed принимает только адрес, не can-фильтр; для массового rm пишешь несколько блоков

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

  • ✗ Использовать `state rm` вместо `removed {}` в shared-проекте - коллеги не видят что ты сделал
  • ✗ Сделать `removed { destroy = false }` для ресурса, который нужен другому root - но забыть в другом root импортнуть; ресурс в провайдере становится orphan
  • ✗ Думать что removed-блок надо держать долго - удаляется в том же релизе после applied

Follow-up

  • ? Когда `removed { destroy = false }` лучше чем `terraform state rm`?
  • ? Как координировать removed + import между двумя root-модулями?
  • ? Можно ли использовать removed чтобы откатить случайный import?

Глубина в базе знаний

  • [[tf-removed-block]]
  • state mv, state rm, state pull/push: ручные операции
  • Паттерны рефакторинга: count→for_each, split files, extract module
tags: refactor, removed-block

#monolithic-state-splitting

seniorиногда

Монолитный state на 3000 ресурсов, plan семь минут. Как разбить?

Что отвечать

Сначала измерь, где тормозит: refresh, граф, провайдер. Большая часть обычно - refresh каждого ресурса по API провайдера. Стратегия разделения: выделить stable-уровень (VPC, IAM, DNS) отдельно от часто меняющегося (приложения). Каждый кусок - отдельный root со своим state. Связи между ними - через remote_state data source или через SSM/Parameter Store. Поэтапно: новый root пустой → import ресурсов из монолитного → removed-блок в монолите → проверяешь что обе стороны видят одно состояние → следующий кусок.

Что хотят услышать

Senior должен: - назвать измерение раньше разделения: `time terraform plan`, `TF_LOG=trace` чтобы увидеть где висит refresh - различить уровни «жизненного цикла»: networking меняется раз в квартал, deployment может крутиться раз в день. Эти штуки не должны делить state - сказать что -parallelism (по умолчанию 10) можно поднять для ускорения refresh; на больших инфрах помогает, но не панацея - упомянуть `terraform stacks` (Cloud-only, beta в 2025-26) как будущий стандарт для multi-state coordination - назвать что разделение должно быть постепенным - не «всё разом», а один кусок, потом следующий

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

  • ✗ Разделить state по «удобной красивости» (по типу ресурса), не по уровню жизненного цикла - связи между state-ами множатся, связи через remote_state становятся узким местом
  • ✗ Использовать -parallelism 50 как «фикс» - провайдер начнёт отбивать запросы rate-limit'ом, обратный эффект
  • ✗ Делать разделение через `terraform state mv` в команде - см. урок про moved/removed; здесь та же проблема, но больше масштаба

Follow-up

  • ? Что такое `terraform stacks` и чем оно отличается от ручного разделения root'ов?
  • ? Как измерить где именно plan тратит время?
  • ? Когда `-parallelism` помогает, а когда вредит?

Глубина в базе знаний

  • Большой state, иерархия, blast-radius, naming
  • Terraform Stacks, нативная multi-stack оркестрация
  • Паттерны рефакторинга: count→for_each, split files, extract module
  • state mv, state rm, state pull/push: ручные операции
tags: refactor, large-scale, performancebook: terraform.in.depth.pdf:ch11

#zero-downtime-resource-migration

seniorиногда

Как мигрировать ресурс между типами без downtime? Пример: ALB на новый VPC.

Что отвечать

Канонический pattern: blue/green. (1) Создаёшь green: новый ALB в новом VPC параллельно старому. (2) Подключаешь оба к одной DNS через weighted routing (Route53 weight 0 на green, 100 на blue). (3) Постепенно перекатываешь вес - 10/90, 50/50, 90/10. (4) Когда зелёный держит весь трафик и стабилен - destroy blue. В Terraform это две версии HCL в одном root (или два root'а), DNS-веса через variable. Без weighted routing - ddmm, через `create_before_destroy` на ALB и пересоздание DNS-records.

Что хотят услышать

Senior должен: - назвать blue/green как pattern, не как Terraform-фичу - это архитектурный подход - объяснить почему `create_before_destroy` сам по себе не даёт zero-downtime для всего: nuances в зависимостях (DNS records, target groups) - упомянуть необходимость отдельных state-state'ов на blue и green в крупных миграциях - тогда blue убирается без риска для green - сказать что миграция RDS требует другого подхода - read replica, promote, blue/green встроенный в RDS

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

  • ✗ Делать blue/green одним PR со всеми изменениями - blast radius огромный, откатить через `revert` невозможно за разумное время
  • ✗ Не настроить health-check на green перед перекаткой трафика - можешь свести продукт
  • ✗ Забыть destroy blue после успешного перехода - двойная цена инфры месяцами

Follow-up

  • ? Чем blue/green отличается от canary?
  • ? Как делать blue/green для RDS, где данные в одном экземпляре?
  • ? Какой минимальный набор observability нужен для безопасного перекатывания трафика?

Глубина в базе знаний

  • Blue-green миграция legacy в Terraform
  • lifecycle: управляем поведением resource
  • Паттерны рефакторинга: count→for_each, split files, extract module
tags: refactor, migration, zero-downtimebook: mastering.terraform.epub:ch13

#plan-slow-on-large-infra

seniorиногда

`terraform plan` занимает 7 минут на 5000 ресурсах. Что делать?

Что отвечать

Сначала диагностика: `TF_LOG=debug terraform plan` покажет где время. Обычно три причины: (1) refresh каждого ресурса по API - решается `-refresh=false` для скоростных проверок, либо разделением state на куски; (2) большой граф - тысячи рёбер пересчитываются, решается рефакторингом структуры HCL и for_each'ей; (3) тяжёлый провайдер - например, kubernetes-provider тащит API discovery каждый раз. Радикальное лечение - разделение state на root'ы по уровню жизненного цикла.

Что хотят услышать

Senior должен: - назвать `-refresh=false` для daily PR-plan'ов (быстрые), плюс периодический полноценный plan для drift-detection - сказать что `-target` не должен использоваться как «ускоритель» - искажает граф, опасно - упомянуть `-parallelism N` (default 10) - поднимать осторожно, провайдеры рейтлимитятся - назвать что разделение state по жизненным циклам - наиболее устойчивое решение, остальное палиативы - упомянуть что Terraform stacks (2025-26) решает эту проблему нативно

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

  • ✗ Использовать `-target` в pipeline для ускорения - первый же drift в untargeted-ресурсе уходит незамеченным
  • ✗ Выключить refresh глобально - плохо для drift detection, компромисс должен быть осознанным
  • ✗ Поставить parallelism = 50 без backoff'а в провайдере - получаешь throttle и retry, медленнее чем 10

Follow-up

  • ? Чем `-refresh=false` опасно для долгой стратегии?
  • ? Почему `-target` это smell, а не решение?
  • ? Как `terraform stacks` решает проблему медленного plan?

Глубина в базе знаний

  • Большой state, иерархия, blast-radius, naming
  • terraform plan: посмотреть, что Terraform собирается сделать
  • Terraform Stacks, нативная multi-stack оркестрация
tags: refactor, performance, large-scale
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки