Загвоздка в том, что ALB - это не один ресурс, а целая гирлянда из пяти связанных кубиков. Если кликать руками в консоли, легко запутаться и забыть, что куда подключено. Тут и раскрывается вся сила подхода инфраструктура как код: ты описываешь цепочку один раз в HCL, а terraform сам выстраивает граф зависимостей и создаёт ресурсы в правильном порядке. Это, пожалуй, самая многоресурсная практика всего курса по terraform, и именно на ней лучше всего видно, ради чего вообще нужен IaC.
Из чего собирается ALB: пять кубиков
Давай разберём цепочку снизу вверх, от железа к точке входа. Каждый следующий ресурс ссылается на id предыдущего - это и есть тот самый граф зависимостей, который terraform строит автоматически.
- yandex_alb_target_group - группа целей. Это просто список IP-адресов и подсетей, куда в итоге полетит трафик. Сами бэкенды.
- yandex_alb_backend_group - группа бэкендов. Оборачивает target group, добавляет порт, веса и - самое важное - healthcheck. Именно здесь балансировщик решает, жив инстанс или его пора выкинуть.
- yandex_alb_http_router - HTTP-роутер. Контейнер для правил маршрутизации. Сам по себе пустой.
- yandex_alb_virtual_host - виртуальный хост. Привязывается к роутеру и описывает маршруты: какой путь в какую backend group отправлять.
- yandex_alb_load_balancer - сам балансировщик. У него есть listener (на каком порту слушать) и allocation_policy (в каких зонах поднять инстансы ALB).

Собираем цепочку в HCL
Будем считать, что сеть, подсеть и инстанс-группа (yandex_compute_instance_group) у тебя уже есть из прошлых уроков. У инстанс-группы удобно то, что она сама умеет регистрировать свои виртуалки в target group: достаточно блока application_load_balancer внутри неё, и target group наполнится автоматически. Но чтобы лучше понять механику, соберём target group явно и руками.
Начнём с целей и бэкендов:
Код: Выделить всё
resource "yandex_alb_target_group" "web" {
name = "web-targets"
target {
subnet_id = yandex_vpc_subnet.app-a.id
ip_address = yandex_compute_instance.web-1.network_interface.0.ip_address
}
target {
subnet_id = yandex_vpc_subnet.app-a.id
ip_address = yandex_compute_instance.web-2.network_interface.0.ip_address
}
}
resource "yandex_alb_backend_group" "web" {
name = "web-backend-group"
http_backend {
name = "web-http"
weight = 1
port = 80
target_group_ids = [yandex_alb_target_group.web.id]
healthcheck {
timeout = "2s"
interval = "5s"
http_healthcheck {
path = "/health"
}
}
}
}
Дальше - роутер и виртуальный хост:
Код: Выделить всё
resource "yandex_alb_http_router" "main" {
name = "main-router"
}
resource "yandex_alb_virtual_host" "main" {
name = "main-vhost"
http_router_id = yandex_alb_http_router.main.id
route {
name = "all-traffic"
http_route {
http_route_action {
backend_group_id = yandex_alb_backend_group.web.id
timeout = "5s"
}
}
}
}
И наконец сам балансировщик:
Код: Выделить всё
resource "yandex_alb_load_balancer" "main" {
name = "main-alb"
network_id = yandex_vpc_network.main.id
allocation_policy {
location {
zone_id = "ru-central1-a"
subnet_id = yandex_vpc_subnet.app-a.id
}
}
listener {
name = "http-listener"
endpoint {
address {
external_ipv4_address {}
}
ports = [80]
}
http {
handler {
http_router_id = yandex_alb_http_router.main.id
}
}
}
}
Запускай привычную пару terraform plan apply. В plan ты увидишь, как terraform насчитал порядок: сначала target group и роутер, потом backend group и virtual host, и только в конце load balancer. Ты нигде не писал depends_on - граф вывелся сам из ссылок на .id. Это ядро всей идеи terraform plan apply: ты декларируешь связи, движок выводит последовательность.
Проверяем балансировку
После apply забери внешний IP из вывода (добавь output с listener-адресом или глянь в консоли). Дальше простой тест в цикле:
Код: Выделить всё
for i in $(seq 1 10); do
curl -s http://<ALB_IP>/ | grep -i hostname
done
Альтернатива попроще: Network Load Balancer (L4)
Если тебе не нужна маршрутизация по путям и хостам, а надо просто раскидать TCP-трафик, ALB избыточен. Бери Network Load Balancer - там кубиков всего два: yandex_lb_target_group (внимание, это другой ресурс, не alb-шный) и yandex_lb_network_load_balancer.
Код: Выделить всё
resource "yandex_lb_network_load_balancer" "main" {
name = "main-nlb"
listener {
name = "tcp-listener"
port = 80
external_address_spec {
ip_version = "ipv4"
}
}
attached_target_group {
target_group_id = yandex_lb_target_group.web.id
healthcheck {
name = "http"
http_options {
port = 80
path = "/health"
}
}
}
}
Типичные грабли
- Забыл healthcheck или указал не тот path. Если /health отдаёт 404, ALB считает все цели больными и возвращает 502 на любой запрос. Балансировщик есть, а трафик в никуда.
- Перепутал alb_target_group и lb_target_group. Это разные ресурсы для разных балансировщиков. В ALB - yandex_alb_target_group, в NLB - yandex_lb_target_group. Скрестить их не выйдет.
- Строки против чисел в таймаутах. В ALB interval и timeout - строки "5s", в NLB - числа 5. Provider честно ругнётся, но новички теряют на этом время.
- Маршрут "/" первым. Поставил prefix "/" в начало route - и все последующие маршруты не работают, потому что матч идёт по порядку.
- Security group закрыта. Если на балансировщик или на инстансы не открыт нужный порт в security group, healthcheck не пройдёт и трафик не пойдёт. Это не ошибка terraform, а сетевой нюанс, который ловится не сразу.
- destroy в неверном порядке. Иногда удаление ALB подвисает, потому что target group ещё держится инстанс-группой. terraform destroy обычно разруливает сам, но если ругается - проверь, не цепляется ли group за target group через свой блок application_load_balancer.
- Подними 2-3 инстанса (или возьми инстанс-группу из прошлого урока) с простейшим веб-сервером, отдающим hostname на /.
- Собери полную цепочку ALB: target group -> backend group с healthcheck -> http router -> virtual host -> load balancer.
- Сделай terraform plan и найди в выводе порядок создания. Убедись, что ты нигде не писал depends_on вручную.
- После apply прогони curl в цикле и убедись, что ответы чередуются между инстансами.
- Сломай /health на одной цели, подожди и проверь, что трафик уехал на здоровую. Почини - и убедись, что цель вернулась.
- Для сравнения собери NLB на тех же целях (через yandex_lb_target_group) и почувствуй, насколько он короче.
- В конце обязательно сделай terraform destroy и проверь по консоли, что ничего не осталось висеть и капать деньгами.
- В каком порядке ссылаются друг на друга пять ресурсов ALB и почему terraform создаёт их именно так, без явного depends_on?
- Зачем backend group нужен healthcheck и что произойдёт с трафиком, если он начнёт падать на всех целях сразу?
- Чем yandex_alb_target_group отличается от yandex_lb_target_group и когда выбирать NLB вместо ALB?
- Почему маршрут с prefix "/" опасно ставить первым в списке route виртуального хоста?
ALB в Yandex Cloud - это цепочка из пяти ресурсов, где каждый ссылается на id соседа: target group -> backend group -> http router -> virtual host -> load balancer. Healthcheck в backend group - сердце всей конструкции, без него балансировщик слепой. terraform сам строит граф зависимостей из ссылок на .id, поэтому порядок создания и удаления выводится автоматически - это и есть наглядная демонстрация смысла terraform state и terraform plan apply. Если L7-роутинг не нужен, бери NLB из двух кубиков. И всегда делай destroy после лабы: балансировщик с публичным IP - это деньги на счётчике.
P.S. про инструмент. Весь HCL из урока одинаково работает и в terraform, и в opentofu - это форк terraform под открытой лицензией MPL 2.0 под крылом Linux Foundation, тогда как terraform с версии 1.6 живёт под BSL 1.1. Для модулей terraform и провайдера yandex разницы по этому уроку нет, можешь смело набирать tofu вместо terraform в командах. Что выбрать для команды - отдельный разговор, но синтаксис инфраструктуры как код у них общий.