lesson ── terraform-advanced ── ~16 мин ── 6 шагов
Legacy-инфра, созданная руками или CloudFormation'ом. Захватить её через
terraform import, недели работы и риск destroy. Альтернатива
blue-green: создать параллельную копию через Terraform, переключить
traffic, снести legacy. На уроке сэмулируешь это на S3-bucket'ах
«legacy» и «tf», с символическим traffic-switch'ем.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
Эмуляция legacy, создаём через aws-cli, не через Terraform:
cd /home/student/blue-green
aws --endpoint-url=http://localstack:4566 \
s3api create-bucket --bucket legacy-app-data --region us-east-1
aws --endpoint-url=http://localstack:4566 \
s3api put-bucket-tagging --bucket legacy-app-data \
--tagging 'TagSet=[
{Key=ManagedBy,Value=clickops}, {Key=Owner,Value=unknown}]'
# положим что-то "пользовательское"
echo "important data v1" > /tmp/data-1.txt
echo "important data v2" > /tmp/data-2.txt
aws --endpoint-url=http://localstack:4566 s3 cp /tmp/data-1.txt s3://legacy-app-data/
aws --endpoint-url=http://localstack:4566 s3 cp /tmp/data-2.txt s3://legacy-app-data/
aws --endpoint-url=http://localstack:4566 s3 ls s3://legacy-app-data/
Legacy bucket в облаке, под управлением Terraform его нет.
✓ Legacy на месте. Terraform его не знает.
cat > main.tf <<'EOF'
resource "aws_s3_bucket" "tf_app_data" {bucket = "tf-app-data"
tags = {ManagedBy = "terraform"
MigratedFrom = "legacy-app-data"
}
}
resource "aws_s3_bucket_versioning" "tf_app_data" {bucket = aws_s3_bucket.tf_app_data.id
versioning_configuration {status = "Enabled"
}
}
output "tf_bucket" {value = aws_s3_bucket.tf_app_data.bucket
}
EOF
terraform init -no-color > /dev/null
terraform apply -auto-approve -no-color > /dev/null
aws --endpoint-url=http://localstack:4566 s3 ls
Видишь два bucket'а: legacy-app-data и tf-app-data. Green
пустой, пока.
✓ Green создан, рядом с legacy. Tracker, Terraform.
aws --endpoint-url=http://localstack:4566 \
s3 sync s3://legacy-app-data s3://tf-app-data --exact-timestamps
aws --endpoint-url=http://localstack:4566 s3 ls s3://tf-app-data/
Данные в green. Это самая дорогая часть в реальной миграции (terabytes данных = часы).
В production: либо это short-window cutover, либо logical-replication (RDS, MongoDB).
✓ Данные синхронизированы. Green готов принимать traffic.
В реальной системе switch, это DNS/Route53/ALB. На LocalStack возьмём proxy «который application bucket?»:
# Symlink-эмуляция конфига приложения
cat > current-target.txt <<EOF
target: legacy-app-data
EOF
cat current-target.txt
# smoke-test «приложения», читает указанный bucket
cat > smoke.sh <<'EOF'
#!/bin/bash
TARGET=$(grep '^target' current-target.txt | cut -d' ' -f2)
echo "Reading from $TARGET..."
aws --endpoint-url=http://localstack:4566 s3 ls s3://$TARGET/ | head -2
EOF
chmod +x smoke.sh
./smoke.sh
Сейчас приложение читает legacy. Переключим:
sed -i 's/legacy-app-data/tf-app-data/' current-target.txt
./smoke.sh
Реальный switch через Route53 weighted-records (вес 100 → tf-, 0 → legacy-) или ALB target-group. См. KB tf-blue-green-migration.
✓ Traffic переключён на green. Старый bucket в standby.
В реальном production, несколько дней с включёнными метриками. Symbolically:
cat > /tmp/monitoring.log <<EOF
day-1: requests=10000 errors=0 | green ok
day-2: requests=15000 errors=2 | green ok (within tolerance)
day-3: requests=12000 errors=0 | green ok
EOF
cat /tmp/monitoring.log
Если бы green показывал нестабильность, sed -i 's/tf-app-data/legacy-app-data/' current-target.txt и rollback
готов. Blue ещё цел.
Это и есть преимущество blue-green: rollback дешёвый, decision reversible до момента сноса blue.
✓ Monitoring-окно прошло без incident'ов. Готовы сносить legacy.
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 для полной матрицы.
# очистка содержимого (s3 rb требует пустой bucket если без --force)
aws --endpoint-url=http://localstack:4566 \
s3 rm s3://legacy-app-data --recursive
aws --endpoint-url=http://localstack:4566 \
s3 rb s3://legacy-app-data
aws --endpoint-url=http://localstack:4566 s3 ls
Legacy snесён. Терраформ-state'у он не нужен, tf_app_data всегда
был под управлением Terraform.
Финальная проверка, приложение всё ещё работает:
./smoke.sh
Видит данные в tf-app-data. Миграция завершена.
Никакого terraform import, никаких ужасных state-операций. Только
создание новой инфры рядом и постепенный switch.
✓ Legacy ушёл. Green работает. Blue-green миграция завершена.
Blue-green не серебряная пуля. Случаи когда стоит делать
import или другой путь:
Уникальный ARN/ID в external-системах. IAM-role с хардкоднутым ARN'ом в стороннем сервисе, blue-green не поможет, ARN другой будет.
Stateful с большими данными. 100 TB в RDS, sync дорого,
простой большой. import плюс careful refactor могут быть
cheaper.
Hard-dependencies к specific IP'шкам. Internal-ALB с прибитым IP. Blue-green создаст другой IP, внешние клиенты не догадаются.
Cross-account / cross-team coupling. Если другие команды
ссылаются на твой ресурс по имени, и переименование требует
coordination, import понятнее (имя не меняется).
Решение делается case-by-case. Blue-green, default-выбор для
большинства stateless ресурсов и small statefuls. import
когда blue-green физически дорого или невозможно.
Сценарий: legacy bucket существует, создан вне Terraform. Не import'им его, создаём параллельный tf-bucket, синкаем данные через aws s3 sync, переключаем DNS (или alias), снимаем legacy. На каждом шаге легко откатиться.
команды
aws s3 sync s3://legacy s3://tf-managedкопируем данные.terraform applyразвёртываем green.aws s3 rb s3://legacy --forceфинальный sneeze legacy.концепции