Представь типичную боль. У тебя есть веб-приложение, и ты поднял под него три виртуалки через yandex_compute_instance. Скопипастил блок ресурса три раза, поменял имена, выкатил. Работает. А потом начинается жизнь: одна нода умерла ночью - никто не заметил, пока не упала нагрузка на оставшиеся две. Нужно выкатить новую версию образа - идёшь и руками пересоздаёшь каждую ВМ по очереди, молясь, чтобы не уронить все сразу. Трафика стало больше - добавляешь четвёртый блок в конфиг и снова terraform apply. Это не инфраструктура как код, это ручная пастушья работа над стадом, которое разбредается.
В AWS эту проблему решает Auto Scaling Group. В Yandex Cloud аналог называется группа виртуальных машин, а в Terraform - ресурс yandex_compute_instance_group. Идея простая: ты описываешь не конкретные машины, а шаблон одной машины плюс правила, сколько таких машин держать, в каких зонах, как их обновлять и как проверять здоровье. Дальше платформа сама поддерживает нужное количество живых экземпляров. Умерла нода - группа поднимет новую. Сказал "хочу пять вместо трёх" - доедет до пяти. Это и есть тот уровень абстракции, ради которого вообще существует terraform и подход iac: ты управляешь желаемым состоянием кластера, а не дёргаешь отдельные сущности.
Важно сразу зафиксировать: yandex_compute_instance_group - это не data, который просто читает существующие ВМ. Это управляющий ресурс, который сам создаёт и убивает машины. В terraform state он хранится как одна запись, но за ней стоит целый парк инстансов. Поэтому это один из ключевых ресурсов всего курса - кто понял группу, тот понял половину прод-эксплуатации в облаке.

Анатомия ресурса: шаблон, масштаб, зоны и деплой
Группа собирается из нескольких блоков. Разберём четыре главных.
instance_template - это чертёж одной машины. Всё, что ты раньше писал в yandex_compute_instance, переезжает сюда: platform_id, блок resources с cores и memory, boot_disk с initialize_params (image_id, size, type), network_interface с network_id и subnet_ids, метаданные с ssh-ключами. Группа штампует машины строго по этому шаблону. Поменял шаблон - группа поймёт, что инстансы устарели, и начнёт их пересоздавать по правилам деплоя.
scale_policy - сколько машин держать. Тут два взаимоисключающих режима: либо fixed_scale (фиксированный размер), либо auto_scale (автомасштабирование по метрике). Об этом подробно ниже, это сердце группы.
allocation_policy - в каких зонах доступности раскидывать машины. Передаёшь список zones, и группа равномерно размазывает инстансы по ним. Это твоя страховка от падения целой зоны: если описал ru-central1-a и ru-central1-b, то отказ одного дата-центра не унесёт весь кластер.
deploy_policy - как обновлять группу, чтобы не уронить сервис. Здесь живут те самые ручки катящегося обновления. max_unavailable - сколько работающих машин разрешено вывести из строя одновременно во время апдейта. max_creating - сколько можно создавать за раз. max_expansion - сколько машин разрешено временно поднять сверх целевого размера (чтобы сначала поднять новые, потом погасить старые - так сервис не проседает). max_deleting - сколько удалять за раз. По доке обязательны max_expansion и max_unavailable, остальные опциональны. Логика как у rolling update в Kubernetes: либо сначала гасим часть и поднимаем новые на их место (играешь max_unavailable), либо сначала раздуваешь группу новыми и потом гасишь старые (играешь max_expansion). Второй путь безопаснее для трафика, но на время апдейта ест больше квоты.
Ещё пара важных полей верхнего уровня. service_account_id - обязателен, группе нужен сервисный аккаунт, под которым она имеет право создавать и удалять ВМ. И variables - карта переменных, через которую можно подставлять значения внутрь шаблона.
Минимальная рабочая группа выглядит так:
Код: Выделить всё
resource "yandex_compute_instance_group" "web" {
name = "web-ig"
folder_id = data.yandex_resourcemanager_folder.this.id
service_account_id = yandex_iam_service_account.ig_sa.id
instance_template {
platform_id = "standard-v3"
resources {
cores = 2
memory = 4
}
boot_disk {
mode = "READ_WRITE"
initialize_params {
image_id = data.yandex_compute_image.ubuntu.id
size = 20
type = "network-ssd"
}
}
network_interface {
network_id = yandex_vpc_network.this.id
subnet_ids = [yandex_vpc_subnet.a.id, yandex_vpc_subnet.b.id]
nat = true
}
metadata = {
ssh-keys = "ubuntu:${file("~/.ssh/id_rsa.pub")}"
}
}
scale_policy {
fixed_scale {
size = 3
}
}
allocation_policy {
zones = ["ru-central1-a", "ru-central1-b"]
}
deploy_policy {
max_unavailable = 1
max_expansion = 1
}
}
Автомасштабирование: scale_policy под нагрузкой
fixed_scale - это просто "держи N штук". Полезно, но настоящая магия в auto_scale, когда группа сама подстраивает количество ВМ под нагрузку. Замени блок scale_policy на такой:
Код: Выделить всё
scale_policy {
auto_scale {
initial_size = 2
min_zone_size = 1
max_size = 10
measurement_duration = 60
cpu_utilization_target = 70
warmup_duration = 120
stabilization_duration = 180
}
}
Есть ещё auto_scale_type - ZONAL по умолчанию или REGIONAL. При REGIONAL автоскейлер смотрит на нагрузку по всему региону целиком, а не отдельно по зонам - удобнее для равномерных кластеров. А если простого CPU мало, в auto_scale есть блок custom_rule, где можно масштабироваться по произвольной метрике из Yandex Monitoring (например, по длине очереди или числу запросов). Но это уже продвинутая тема, для старта хватит CPU.
Health checks и связь с балансировщиком
Группа умеет не только считать машины, но и проверять, живы ли они. За это отвечает блок health_check:
Код: Выделить всё
health_check {
interval = 5
timeout = 2
healthy_threshold = 2
unhealthy_threshold = 3
http_options {
port = 80
path = "/healthz"
}
}
И вот мы подошли к балансировщику. Сама по себе группа - это набор машин, но трафик-то надо на них раскидывать. Группа умеет автоматически регистрировать свои инстансы в целевой группе балансировщика. Для сетевого балансировщика (L4) это блок load_balancer, для L7 - блок application_load_balancer:
Код: Выделить всё
application_load_balancer {
target_group_name = "web-tg"
target_group_description = "ALB target group for web IG"
}
Типичные грабли
- Забыли сервисный аккаунт или не выдали ему роль. Группе нужен service_account_id с правами на создание ВМ (минимум compute.editor в нужном фолдере). Без этого apply упадёт уже на этапе создания группы.
- Указали и fixed_scale, и auto_scale разом. Можно только что-то одно, иначе провайдер ругнётся. Переключение между ними - это смена режима, не одновременное использование.
- Слишком агрессивный deploy_policy. Поставил max_unavailable равным размеру группы - и при обновлении образа платформа честно погасит сразу все машины. Сервис ляжет. Для прода держи max_unavailable маленьким, а лучше играй через max_expansion.
- Нет health_check при автомасштабировании. Автоскейлер по CPU подымет машину, но если приложение на ней не стартовало, группа этого не заметит. Health check - обязательный спутник любой серьёзной группы.
- Слишком короткий warmup_duration. Если приложение прогревается дольше, чем ты указал, автоскейлер начнёт учитывать метрики ещё холодной ВМ и может неверно посчитать нагрузку. Ставь с запасом под реальное время старта.
- Меняешь instance_template и удивляешься, что terraform plan apply хочет пересоздать кучу машин. Это нормально: смена шаблона - это и есть катящееся обновление парка. Просто смотри, чтобы deploy_policy был щадящим.
Собери руками то, что мы разобрали, и прогони через terraform plan apply (или tofu plan apply, если ты на OpenTofu - синтаксис идентичен).
- Опиши группу из 2 машин в режиме fixed_scale, раскидай по двум зонам через allocation_policy. Сделай apply, убедись, что поднялись именно две ВМ в разных зонах.
- Добавь health_check с http_options на путь /healthz. Если приложения нет - подними простой nginx через metadata и user-data, чтобы было что чекать.
- Переведи scale_policy с fixed_scale на auto_scale: initial_size 2, max_size 5, cpu_utilization_target 60. Сделай plan и прочитай, что именно Terraform собирается поменять.
- Покрути deploy_policy: поставь max_unavailable 1 и max_expansion 1, потом измени image_id в шаблоне и посмотри в plan, как группа собирается катить обновление.
- Добавь блок application_load_balancer с target_group_name и проверь в выводе apply, что появился target_group_id.
- Чем yandex_compute_instance_group принципиально отличается от трёх отдельных yandex_compute_instance и какую боль он снимает?
- В чём разница между fixed_scale и auto_scale и почему их нельзя указать одновременно?
- За что отвечают max_unavailable и max_expansion в deploy_policy и как через них сделать обновление без простоя?
- Зачем группе health_check и что плохого случится при автомасштабировании без него?
Группа экземпляров - это переход от управления отдельными машинами к управлению желаемым состоянием кластера, ровно та идея, ради которой существует инфраструктура как код. Шаблон instance_template описывает одну машину, scale_policy решает сколько их (фиксированно или авто по CPU), allocation_policy раскидывает по зонам ради отказоустойчивости, deploy_policy катит обновления без простоя, а health_check плюс блок балансировщика превращают набор ВМ в самовосстанавливающийся кластер за балансером. Это базовый строительный блок отказоустойчивой инфраструктуры в terraform yandex cloud, и модули terraform поверх группы дальше собираются буквально на раз. Освоил группу - дальше прод уже не страшен.