Стройте ответ вокруг владения состоянием, лимитов, отказа и восстановления. Определение становится инженерным ответом только в конкретном production-сценарии.
Вопросы и ответы
8 подробных ответов
01Зачем Terraform вообще нужен стейт и как защитить его при командной работе?
middle
Короткий ответ: Стейт-файл — это маппинг адресов ресурсов из конфига на реальные ID и атрибуты в облаке плюс граф зависимостей. Без него Terraform не знал бы, чем управляет, и сканировал бы всё облако на каждый plan. В команде: remote backend + лок + шифрование.
Подробно:
- Маппинг —
aws_instance.web↔i-0abc123с атрибутами; именно по нему plan считает diff. - Производительность — refresh опрашивает только известные стейту ресурсы, а не весь аккаунт.
- Чувствительные данные — пароли БД и ключи лежат в стейте открытым текстом: стейт защищают как секрет.
Командная работа:
terraform {
backend "s3" {
bucket = "corp-tfstate"
key = "prod/network.tfstate"
dynamodb_table = "tf-locks" # залочить стейт
encrypt = true
}
}
- Remote backend (S3/GCS/Terraform Cloud) — единый источник правды вместо стейта на чьём-то ноутбуке;
- Locking (DynamoDB) — параллельные apply не перетирают друг друга;
- Шифрование + узкий доступ — из-за секретов внутри.
⚠️ Частая ошибка: «стейт — это просто кэш, можно удалить и пересоздать». Потеряв маппинг, Terraform «забывает» ресурсы и на следующем apply создаёт дубли.
02Кто-то поменял инфраструктуру руками в консоли. Как поведёт себя Terraform и что вы сделаете?
middle
Короткий ответ: Это дрифт. Terraform ничего не заметит до следующего plan: на refresh он сравнит реальное облако со стейтом и покажет расхождение. Дальше решение по каждому случаю — откатить (apply вернёт декларируемое состояние) или узаконить (внести изменение в конфиг).
Подробно:
- Обнаружение —
terraform plan(refresh) читает актуальные атрибуты из API и показывает diff против конфига. - Решение по ситуации:
| Вариант | Когда | Действие |
|---|---|---|
| Откатить | ручная правка — ошибка или временный hotfix | terraform apply возвращает декларируемое состояние |
| Узаконить | изменение нужно навсегда | внести в конфиг, добиться plan → «no changes» |
- Профилактика — консоль read-only для людей (изменения только через пайплайн), регулярный drift-detection plan в CI с алертом на непустой diff.
⚠️ Частая ошибка: думать, что Terraform сам непрерывно откатывает ручные правки. Это поведение GitOps-реконсилеров (Argo CD и т.п.) — Terraform сверяет реальность с конфигом только когда его запустили.
03Как взять под управление Terraform ресурс, созданный руками в проде?
middle
Короткий ответ: terraform import (или import-блоки в современном Terraform) привязывает существующий ресурс к адресу в стейте. Классический import НЕ генерирует конфиг — его пишете вы и итерируете, пока plan не покажет «no changes».
Подробно:
# 1. Написать каркас конфига
resource "aws_s3_bucket" "legacy" { ... }
# 2. Привязать реальный ресурс к адресу в стейте
terraform import aws_s3_bucket.legacy my-prod-bucket
# 3. Доводить конфиг, пока plan не станет пустым
terraform plan # → No changes = конфиг совпал с реальностью
# TF 1.5+: декларативно, с генерацией конфига
import {
to = aws_s3_bucket.legacy
id = "my-prod-bucket"
}
terraform plan -generate-config-out=generated.tf
- Import меняет только стейт — облако не трогается; опасный момент наступает на apply, если plan ещё не чистый.
terraform state mv— для рефакторингов (переименование ресурса, перенос в модуль) без destroy/recreate: меняется адрес в стейте, а не ресурс.
⚠️ Частая ошибка: сделать import и сразу apply с недописанным конфигом — Terraform «исправит» прод под неполный конфиг и снесёт настройки, которых в нём нет.
04terraform plan показывает destroy и recreate продовой базы данных. Почему это возможно и что делать?
senior
Короткий ответ: Чаще всего: изменён ForceNew-атрибут (неизменяемый в API провайдера), переименован адрес ресурса (Terraform видит «старый удалить, новый создать») или сменился идентификатор. Проверяемый навык — умение ЧИТАТЬ plan и остановиться до apply.
Подробно:
# aws_db_instance.main must be replaced
-/+ resource "aws_db_instance" "main" {
~ engine_version = "14.7" -> "15.3"
~ identifier = "app-db" -> "app-db-v2"
# forces replacement
}
- ForceNew-атрибуты — параметр нельзя изменить in-place через API (AZ, identifier, тип некоторых дисков) → провайдер требует пересоздание; plan помечает виновника «forces replacement».
- Переименование адреса — лечится
moved-блоком илиterraform state mv, а не destroy/recreate. - Защита:
lifecycle { prevent_destroy = true }на критичных ресурсах — apply упадёт с ошибкой; в CI apply строго из сохранённого plan-файла, чтобы исполнялось ровно то, что ревьюили.
⚠️ Частая ошибка: пробежать план глазами и нажать yes. Маркер -/+ («must be replaced») — главное, что интервьюер хочет услышать про чтение плана.
05Два инженера одновременно запускают terraform apply. Что будет со стейт-локом и без него?
junior
Короткий ответ: С локом второй apply заблокируется или упадёт с ошибкой «Error acquiring the state lock» — стейт залочен первым. Без лока — гонка: оба читают одну версию стейта и перетирают записи друг друга, стейт становится неконсистентным.
Подробно:
с локом: A: apply ──► lock OK ──► работает ──► unlock
B: apply ──► Error acquiring the state lock ✗
без лока: A: read state v1 ──► write v2a ─┐
B: read state v1 ──► write v2b ─┴─► кто записал
последним, тот и «прав»
- Последствия гонки — потерянные записи: ресурс создан в облаке, но его нет в стейте (сирота), или стейт бит и его чинят руками через
state rm/import. - Механика — backend берёт лок на время операции (для S3 — таблица DynamoDB),
force-unlockтолько для зависших локов. - Правильный ответ целиком — apply выполняет только CI-пайплайн, последовательно; люди локально запускают plan.
⚠️ Частая ошибка: считать лок опциональной мелочью «для больших команд» — достаточно одного пересечения apply, чтобы потом разгребать стейт руками.
06Как структурировать Terraform-код для окружений dev/stage/prod?
middle
Короткий ответ: Общие версионированные модули + тонкие корневые конфиги на окружение (директория на env или workspaces). Стейт режется по blast radius — network / data / app отдельно, чтобы один apply физически не мог снести всё сразу.
Подробно:
modules/ # общие, версионированные
vpc/ rds/ app/
envs/
dev/ main.tf ──► свой стейт
stage/ main.tf ──► свой стейт
prod/
network/ ──► стейт 1 (слои = blast radius)
data/ ──► стейт 2
app/ ──► стейт 3
- Директория на env vs workspaces — директории явные: разные версии модулей, разные backend'ы, diff между env виден в git; workspaces компактнее, но окружения отличаются лишь переменной, и легко применить не туда.
- Разрез стейта по слоям — сеть меняется редко, приложение часто; отдельные стейты сокращают радиус поражения и время plan.
- Связи между стеками — data source
terraform_remote_stateили публикация outputs через SSM.
⚠️ Частая ошибка: один гигантский стейт на всю компанию — каждый plan длится вечность, а любой apply потенциально трогает всё.
07Terraform и Ansible: когда использовать каждый?
junior
Короткий ответ: Terraform — декларативный provisioning со стейтом и жизненным циклом: создать/изменить/удалить инфраструктуру. Ansible — управление конфигурацией и оркестрация: настроить то, что уже существует, агентлесс по SSH. Типичная связка: Terraform поднимает VM/кластер, Ansible доводит его до готовности.
Подробно:
| Terraform | Ansible | |
|---|---|---|
| Задача | provisioning инфраструктуры | конфигурация и оркестрация |
| Модель | декларативная + стейт | плейбук сверху вниз, модули идемпотентны |
| Стейт | стейт-файл, plan/apply, дрифт | нет стейта — сверяется с фактом на хосте |
| Доставка | API облака | агентлесс push по SSH |
| Сильная сторона | lifecycle, зависимости, destroy | пакеты, конфиги, сервисы, ad-hoc задачи |
- Граница сдвигается — с immutable-образами (Packer) конфигурация запекается в образ на этапе сборки, и роль Ansible в рантайме сжимается до минимума.
- Без стейта нет удаления — Ansible не удалит то, что вы просто убрали из плейбука; Terraform удалит, потому что помнит.
⚠️ Частая ошибка: пытаться «всё одним инструментом» — Terraform неудобен для тонкой настройки ОС, а Ansible без стейта плохо управляет жизненным циклом облачных ресурсов.
08Что означает идемпотентность в Ansible и как задачи shell/command её ломают?
middle
Короткий ответ: Идемпотентность — повторный прогон плейбука сходится к описанному состоянию, а не повторяет действия: модуль проверяет текущее состояние и возвращает changed или ok. command/shell выполняются всегда и всегда рапортуют changed, если их не ограничить.
Подробно:
# Плохо: выполняется при каждом прогоне
- shell: tar xzf /opt/app.tar.gz -C /opt/app
# Приемлемо: guard-условия
- shell: tar xzf /opt/app.tar.gz -C /opt/app
args:
creates: /opt/app/bin/run # пропустить, если файл уже есть
- command: /usr/local/bin/migrate
register: out
changed_when: "'applied' in out.stdout"
# Лучше всего: родной модуль — сам проверяет состояние
- unarchive:
src: /opt/app.tar.gz
dest: /opt/app
remote_src: true
- Почему это важно — вечный
changedломает отчётность и handlers (нотификации срабатывают на каждом прогоне), а сами действия могут быть неповторяемыми. - Инструменты:
creates:/removes:,changed_when:/failed_when:— или замена на профильный модуль.
⚠️ Частая ошибка: плейбук из сырых shell-задач — это bash-скрипт с YAML-синтаксисом: идемпотентности в нём нет, только её видимость.
Источники
Источники и редакционная политика
Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.
От чтения к воспроизведению
Отрепетируйте полный цикл интервью.
RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.