Самый сложный этап — перенос 30 уже работающих серверов под управление Terraform. Нельзя просто описать ресурсы и выполнить apply — Terraform попытается создать дубликаты. Нужен terraform import.
Первым делом мы сделали полную инвентаризацию:
# Получаем список всех VM
yc compute instance list --format json | jq '.[] | {name, id, status, zone: .zone_id, cores: .resources.cores, memory_gb: (.resources.memory / 1073741824)}'
# Результат (фрагмент):
# {"name": "prod-app-1", "id": "fhm1abc...", "status": "RUNNING", "zone": "ru-central1-a", "cores": 4, "memory_gb": 8}
# {"name": "prod-app-2", "id": "fhm2def...", "status": "RUNNING", "zone": "ru-central1-a", "cores": 4, "memory_gb": 8}
# {"name": "prod-db-1", "id": "fhm3ghi...", "status": "RUNNING", "zone": "ru-central1-a", "cores": 8, "memory_gb": 32}
# ... (ещё 27 серверов)
# Список сетей и подсетей
yc vpc network list
yc vpc subnet list
# Security groups
yc vpc security-group list --format json | jq '.[] | {name, id, rules_count: (.rules | length)}'
# Диски
yc compute disk list --format json | jq '.[] | {name, id, size_gb: (.size / 1073741824), type: .type_id}'
Инвентаризация выявила 6 «забытых» ресурсов: 3 неиспользуемых диска по 100 ГБ, 2 статических IP без привязки и 1 snapshot месячной давности. Суммарная экономия от их удаления — 12 000 руб/мес.
Для каждого ресурса мы: 1) описывали его в HCL, 2) импортировали в state, 3) запускали plan для проверки совпадения:
# Шаг 1: Описываем сеть в Terraform
# modules/network/main.tf
resource "yandex_vpc_network" "main" {
name = "cloudmetrics-network"
description = "Main VPC network"
}
resource "yandex_vpc_subnet" "app" {
name = "app-subnet"
zone = "ru-central1-a"
network_id = yandex_vpc_network.main.id
v4_cidr_blocks = ["10.10.1.0/24"]
}
resource "yandex_vpc_subnet" "db" {
name = "db-subnet"
zone = "ru-central1-a"
network_id = yandex_vpc_network.main.id
v4_cidr_blocks = ["10.10.2.0/24"]
}
resource "yandex_vpc_security_group" "app" {
name = "app-sg"
network_id = yandex_vpc_network.main.id
ingress {
protocol = "TCP"
port = 443
v4_cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS"
}
ingress {
protocol = "TCP"
port = 80
v4_cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
}
ingress {
protocol = "TCP"
port = 22
v4_cidr_blocks = ["10.10.0.0/16"]
description = "SSH internal"
}
egress {
protocol = "ANY"
v4_cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound"
}
}
# Шаг 2: Импортируем существующие ресурсы
cd environments/production
# Импорт сети
terraform import module.network.yandex_vpc_network.main enp1abc2def3ghi
# Импорт подсетей
terraform import module.network.yandex_vpc_subnet.app e9b1abc2def3ghi
terraform import module.network.yandex_vpc_subnet.db e9b4jkl5mno6pqr
# Импорт security group
terraform import module.network.yandex_vpc_security_group.app enp7stu8vwx9yz0
# Шаг 3: Проверяем, что plan показывает "No changes"
terraform plan
# module.network.yandex_vpc_network.main: Refreshing state...
# module.network.yandex_vpc_subnet.app: Refreshing state...
# No changes. Your infrastructure matches the configuration.
Когда plan показывает изменения — значит описание в HCL не совпадает с реальностью. Это и есть обнаружение дрифта. Мы корректировали HCL до полного совпадения.
Импортировать 30 серверов вручную — утомительно. Мы написали скрипт для генерации HCL и команд импорта:
#!/bin/bash
# generate-import.sh — генерирует HCL и команды импорта для всех VM
FOLDER_ID="b1g1abc2def3ghi"
# Получаем все VM в формате JSON
VMs=$(yc compute instance list --folder-id $FOLDER_ID --format json)
# Генерируем HCL-ресурсы
echo "$VMs" | jq -r '.[] | @base64' | while read VM_B64; do
VM=$(echo "$VM_B64" | base64 -d)
NAME=$(echo "$VM" | jq -r '.name')
ID=$(echo "$VM" | jq -r '.id')
CORES=$(echo "$VM" | jq -r '.resources.cores')
MEMORY=$(echo "$VM" | jq -r '.resources.memory / 1073741824 | floor')
DISK_SIZE=$(echo "$VM" | jq -r '.boot_disk.disk_size / 1073741824 | floor' 2>/dev/null || echo "20")
ZONE=$(echo "$VM" | jq -r '.zone_id')
SUBNET_ID=$(echo "$VM" | jq -r '.network_interfaces[0].subnet_id')
INTERNAL_IP=$(echo "$VM" | jq -r '.network_interfaces[0].primary_v4_address.address')
TF_NAME=$(echo "$NAME" | tr '-' '_')
echo "# terraform import yandex_compute_instance.${TF_NAME} ${ID}"
echo "resource \"yandex_compute_instance\" \"${TF_NAME}\" {"
echo " name = \"${NAME}\""
echo " zone = \"${ZONE}\""
echo " platform_id = \"standard-v3\""
echo " resources {"
echo " cores = ${CORES}"
echo " memory = ${MEMORY}"
echo " }"
echo "}"
echo ""
done
За один день мы импортировали все 30 серверов, 5 подсетей, 8 security groups, 3 managed-базы данных и 12 дисков. Полная инфраструктура теперь описана в коде.