lesson ── terraform-intermediate ── ~18 мин ── 6 шагов
Один и тот же сервис нужен в dev, stage, prod. Как разложить код, чтобы не дублировать HCL? Два подхода:
terraform workspace.
Простой, но опасный (легко сделать apply не в тот env).envs/dev/, envs/prod/. Каждый env,
свой root с своим backend, общий код в модуле.В этом уроке сделаешь оба, поймёшь tradeoff'ы.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
cd /home/student/tf-envs/workspaces-style
cat > main.tf <<'EOF'
resource "random_id" "suffix" {byte_length = 4
}
locals {env = terraform.workspace
}
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-${local.env}-${random_id.suffix.hex}" tags = {Environment = local.env
}
}
output "current_workspace" {value = terraform.workspace
}
EOF
terraform init
Создай два workspace:
terraform workspace new dev
terraform workspace new prod
terraform workspace list
Звёздочка указывает активный.
Workspaces, это отдельные state'ы в одном backend. При local
backend state живёт в terraform.tfstate.d/<workspace>/. При S3, с
разными key.
✓ Два workspace созданы. Каждый: свой state.
Переключи на dev и применит:
terraform workspace select dev
terraform workspace show
terraform apply -auto-approve
terraform output current_workspace
Должно создаться: бакет с именем linuxlab-dev-<hash>.
Переключи на prod:
terraform workspace select prod
terraform workspace show
terraform apply -auto-approve
terraform output current_workspace
Создался другой бакет: linuxlab-prod-<hash>. State у этих
двух workspaces разные, поэтому каждый создаёт свой ресурс.
Проверь:
aws --endpoint-url=http://localstack:4566 s3 ls
Видишь оба бакета, linuxlab-dev-... и linuxlab-prod-....
✓ Workspaces изолировали state: два разных бакета из одного HCL.
Главная проблема workspaces, легко перепутать активный.
Сценарий: ты работаешь в dev, переключился в prod чтобы посмотреть
что-то, забыл переключиться обратно. Делаешь terraform apply,
применил dev-изменения к prod-state.
Защита, precondition на data-source external который проверяет
workspace:
check "workspace_match" { assert {condition = terraform.workspace != "default"
error_message = "Apply на 'default' workspace запрещён: выбери dev/stage/prod."
}
}
Это часть «защитного пояса». В большой команде workspaces для prod, не используются, чтобы исключить эту ошибку. Для feature-branch экспериментов в локальном dev, ок.
Возвращайся в dev:
terraform workspace select dev
✓ Опасность понятна. В проде: directory style.
mkdir -p /home/student/tf-envs/dirs-style/modules/app
cat > /home/student/tf-envs/dirs-style/modules/app/main.tf <<'EOF'
resource "random_id" "suffix" {byte_length = 4
}
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-${var.env}-${random_id.suffix.hex}" tags = {Environment = var.env
}
}
output "bucket_arn" {value = aws_s3_bucket.demo.arn
}
EOF
cat > /home/student/tf-envs/dirs-style/modules/app/variables.tf <<'EOF'
variable "env" {type = string
validation {condition = contains(["dev", "stage", "prod"], var.env)
error_message = "env must be dev, stage, or prod."
}
}
EOF
Это переиспользуемый модуль app. Принимает env, создаёт
бакет с правильным именем.
Теперь env-roots:
✓ Модуль app описан. Теперь: root'ы по средам.
cat > /home/student/tf-envs/dirs-style/envs/dev/main.tf <<'EOF'
module "app" {source = "../../modules/app"
env = "dev"
}
output "bucket_arn" {value = module.app.bucket_arn
}
EOF
cat > /home/student/tf-envs/dirs-style/envs/prod/main.tf <<'EOF'
module "app" {source = "../../modules/app"
env = "prod"
}
output "bucket_arn" {value = module.app.bucket_arn
}
EOF
Применяем dev:
cd /home/student/tf-envs/dirs-style/envs/dev
terraform init
terraform apply -auto-approve
terraform output bucket_arn
Применяем prod:
cd /home/student/tf-envs/dirs-style/envs/prod
terraform init
terraform apply -auto-approve
terraform output bucket_arn
У каждого env, свой .terraform/, свой state. Невозможно случайно
применить dev-конфиг к prod, для этого надо физически перейти в
другую директорию.
✓ Два env-root'а, каждый со своим state. Изоляция железная.
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 для полной матрицы.
Перейди в один из env-root'ов:
cd /home/student/tf-envs/dirs-style/envs/dev
terraform state list
Видишь:
module.app.aws_s3_bucket.demo
module.app.random_id.suffix
Сравнение:
| Аспект | Workspaces | Directory-per-env |
|---|---|---|
| HCL дублируется | Нет | Минимум (root тонкий, всё в module) |
| Backend dublировать | Нет, один | Да (каждый env свой backend) |
| Случайно apply не в тот env | Легко | Сложно. Нужно cd |
| Расхождение между env | Невозможно, один HCL | Возможно (хорошо или плохо?) |
| Сложность на старте | Низкая | Средняя |
| Production-scale | Не рекомендован | Стандарт |
В реальной работе: workspaces, для коротких экспериментов, dirs-per-env, для всего серьёзного. Многие компании используют Terragrunt для управления dirs-style (см. advanced-трек).
См. tf-workspace про workspace детали и tf-init-backends про backend per env.
✓ Intermediate-трек закрыт. Дальше: production.
Несмотря на всё сказанное, workspaces всё ещё полезны:
Антипаттерн: workspaces для разделения customer'ов («tenant per workspace»). На сотне tenant'ов state-операции (refresh, plan) на одном backend становятся медленными, риски смешения растут. Для multi-tenant, directory-style или multi-account.
Workspaces, лёгкие, один HCL, переключатель в state. Подходят для feature-branch экспериментов или близких env. Directory layout, тяжелее на старте, но безопаснее и масштабируемее. Production обычно использует directory-per-env с общим модулем.
команды
terraform workspace new devсоздать workspaceterraform workspace select prodпереключить активныйterraform workspace showкакой сейчас активен: критично перед applycd envs/prod && terraform applydirectory style: env = директорияконцепции