# Blue-green миграция legacy в Terraform _Advanced · TerraformLab Knowledge Base_ **TL;DR:** Когда legacy-инфра (ClickOps в Console, CloudFormation, Ansible) большая, `terraform import` каждого ресурса, недели работы и риски. Альтернатива: blue-green, поднимаешь параллельную копию через Terraform (green), переключаешь traffic, сносишь legacy (blue). Дороже по cloud-bill (двойная инфра несколько дней), но безопаснее: можешь откатиться, никакого destructive-import'а. ## Когда blue-green вместо import `terraform import`, захват существующего ресурса под управление Terraform. Звучит просто, но на legacy-стеке это: - **Манипуляции на каждый ресурс**. 500 ресурсов = 500 import'ов (даже с `import` блоком и `-generate-config-out`). - **State не отражает поведение, отражает атрибуты на момент import'а**. Если HCL и реальность хоть немного разойдутся, плана на drift или destroy. - **Невидимые dependencies в legacy**. CloudFormation-stack может держать stack-internal ссылки, которых ты не знаешь, import их разорвёт. - **Никакого rollback'а**. Сделал import, plan показал destroy, а обратно как? Restore из backup state, надеемся что не было apply. Blue-green: вместо манипуляций со state'ом, **строишь новое рядом**. Когда new working, переключаешь, сносишь old. ## Когда blue-green: - Legacy маленький, до 50 ресурсов, import нормально. - Legacy большой, но statefuk (RDS, EBS), blue-green с migration данных дорог. - Stateless (web-серверы, Lambda, S3-static), blue-green оптимален. - Есть бюджет на параллельную инфру 1-7 дней. - Есть способ переключить traffic (DNS, ALB target group, CDN). ## Сценарий Legacy: ``` VPC (ручной, CloudFormation) ├── 4 EC2 web-servers ├── 1 RDS Postgres ├── ALB └── Route53 record: api.company.com → ALB ``` Цель: ``` VPC (terraform) ├── 4 EC2 web-servers (terraform) ├── 1 RDS Postgres (terraform) ├── ALB (terraform) └── Route53 record: api.company.com → terraform-ALB ``` ### Шаг 1: Создай terraform-stack рядом Полный mirror legacy через HCL. Но имена ресурсов **другие**: `tf-vpc`, `tf-alb`, etc. Так оба stack'а не дерутся. ```bash terraform apply ``` Cloud-bill вдвое. Будет неделю. ### Шаг 2: Миграция stateful-данных RDS: ```bash # snapshot legacy DB aws rds create-db-snapshot --db-instance-identifier legacy-db --db-snapshot-identifier migration-base # restore в tf-RDS: aws rds restore-db-instance-from-db-snapshot \ --db-instance-identifier tf-db \ --db-snapshot-identifier migration-base ``` Или logical-replication для zero-downtime. Зависит от сценария. S3 buckets, пересоздать или `aws s3 sync`: ```bash aws s3 sync s3://legacy-bucket s3://tf-bucket --exact-timestamps ``` ### Шаг 3: Smoke-test green Внутренний DNS-record `tf-api.company.com` указывает на tf-ALB. Прогоняешь test suite, проверяешь функциональность. ### Шаг 4: Traffic switch Если используешь **Route53 weighted records**, постепенно, 10% → 50% → 100%, наблюдая метрики: ```hcl resource "aws_route53_record" "api_legacy" { zone_id = data.aws_route53_zone.main.zone_id name = "api.company.com" type = "A" weighted_routing_policy { weight = 10 } set_identifier = "legacy" alias { name = data.aws_lb.legacy_alb.dns_name zone_id = data.aws_lb.legacy_alb.zone_id evaluate_target_health = true } } resource "aws_route53_record" "api_tf" { zone_id = data.aws_route53_zone.main.zone_id name = "api.company.com" type = "A" weighted_routing_policy { weight = 90 } set_identifier = "tf" alias { name = aws_lb.tf.dns_name zone_id = aws_lb.tf.zone_id evaluate_target_health = true } } ``` Меняешь веса в HCL → apply → traffic смещается. ### Шаг 5: Сноси legacy После N дней мониторинга: ```bash # legacy CloudFormation stack aws cloudformation delete-stack --stack-name legacy-stack # или вручную, если ClickOps aws ec2 terminate-instances --instance-ids i-... # ... ``` Если уверен, можно сразу. Если хочешь подождать, пусть месяц работает «как есть, на 0% веса». Минус, cost. ### Шаг 6: Cleanup tf- Префиксы `tf-` были временные. Переименуй в production-имена: ```hcl moved { from = aws_lb.tf to = aws_lb.main } ``` Apply, `moved` декларативно переименовывает, без пересоздания. ## Migration данных, варианты | Тип данных | Способ | |---|---| | RDS / Aurora | snapshot-restore, или logical-replication для zero-downtime | | DynamoDB | DynamoDB Streams + Lambda на новую таблицу | | S3 | `aws s3 sync` или S3 Replication-rules | | EBS | snapshot + create-volume | | EFS | DataSync или rsync | | Secrets | вручную (Secrets Manager не клонируется автоматически) | Для каждого, свой подход. Stateful, самая дорогая часть миграции. ## Rollback Главное преимущество blue-green: blue ещё жив пока ты не сделал step 5. - Smoke-test зафейлился? Откатываем DNS-вес обратно на legacy. - Что-то странное в prod-нагрузке? Откатываем DNS-вес. - Терраформ-state corrupt'нулся? Worst case, destroy terraform-stack, восстанавливай legacy как было. `terraform import` такой роскоши не даёт: после import + apply ты в Terraform; rollback, это destroy всё что Terraform создал, восстановить через CloudFormation/manual. ## Когда blue-green, плохая идея - **Один global ресурс**. Например, IAM-role с конкретным ARN'ом blue-green «новой ролью» не работает; ARN другой, потребители не знают про новый. - **Inteсточение через external DNS / external systems**. Если кто-то из-вне хардкодит твой ALB-DNS-name, blue-green не помогает. - **Sticky sessions stateful**. Если session-state в memory приложения, переключение traffic середине дня == разрыв сессий пользователей. ## Подводные камни - **Cost.** Двойная инфра неделю, может быть тысячи $. Заложи бюджет заранее. - **DNS-TTL.** Если legacy-record имеет TTL 24h, switch видит через 24h максимум. Снизь TTL заранее (24h до switch'а) до 60s. - **External integrations.** Сторонние сервисы (webhooks, partners, monitoring) могут хардкодить старые URLs. Audit всех integration'ов ДО switch'а. - **Stateful-data drift.** RDS-snapshot → restore, данные на момент snapshot'а. Между snapshot и cutover в legacy могут писаться новые данные. Решение: short-window cutover или logical-replication. - **Internal services могут хардкодить IP'шки.** Internal-ALB создаётся с разными IP. Сервисы, использующие конкретный IP (не DNS-name), не переключатся. - **Не путать с stack-replication.** Blue-green, НЕ копирование state'а. Это создание новой инфры с нуля через HCL. State'ы независимы. - **Terraform не помогает с миграцией данных.** HCL умеет создать RDS-restore из snapshot'а, но не управляет logical-replication. Это отдельная работа DBA. ## Команды ```bash aws rds create-db-snapshot --db-instance-identifier X --db-snapshot-identifier mig-base ``` Сделать snapshot legacy DB перед миграцией. ```bash aws s3 sync s3://legacy s3://terraform-managed --exact-timestamps ``` Скопировать S3-content в новый bucket. ```bash dig @8.8.8.8 api.company.com ``` Проверить traffic после switch'а, на какой ALB resolве. ```bash terraform apply -var=traffic_weight_legacy=0 ``` Постепенный switch через variable weight. ## См. также - [Импорт: захват существующего ресурса под Terraform](/terraform/kb/tf-state-import.md) - [moved блок: переименование без destroy](/terraform/kb/tf-moved-block.md) - [Большой state, иерархия, blast-radius, naming](/terraform/kb/tf-large-scale-state.md)