Как автоматизировать создание и настройку глобального роутера в облаке через Terraform Provider
Как с помощью Terraform Provider автоматизировать настройку глобального роутера.
Итак, у вас есть две виртуальные машины в разных регионах, на которых крутится дев, тест, прод (нужное подчеркнуть). И вам нужно соединить их приватной сетью. В целом это можно сделать и ручками. Но когда инфраструктура разрастется, вы точно не захотите заниматься подобными вещами.
Эта статья рассчитана на специалистов, которые умеют работать с Terraform на базовом уровне. Если вы не в их числе, рекомендую начать с основ.
Исходные условия
Начнем с описания исходной инфраструктуры. Допустим, у нас есть две виртуальных машины в Москве и Санкт-Петербурге.
Санкт-Петербург, зона ru-1:
- облачная сеть с подсетью 192.168.0.0/28,
- ВМ в этой сети с IP 192.168.0.2,
- облачный роутер, подключенный к сети, и плавающий IP: 95.213.230.121.

Москва, зона ru-2:
- облачная сеть с подсетью 172.16.0.0/28,
- ВМ в этой сети с IP 172.16.0.2,
- облачный роутер, подключенный к сети, и плавающий IP: 176.114.69.92.

Плавающий, или Floating, IP на обеих ВМ нужны для удобства демонстрации сетевой связанности. В реальном примере они могут отсутствовать — доступ будет осуществляться через виртуальную консоль в панели управления.
Допустим, на ВМ1 в Санкт-Петербурге у меня запущен сервис с базой данных. Я решаю обеспечить катастрофоустойчивость и на ВМ2 в Москве поднимаю копию БД и настраиваю репликацию с ВМ1. Схематично это будет выглядеть так:

В реальном мире я бы предпочел решить именно такую задачу с помощью облачных баз данных. Но статья носит обучающий характер, а я, допустим, решил попрактиковаться в репликации.
В описанном примере у обеих ВМ есть плавающие IP-адреса, но настраивать через них связь нельзя по ряду причин. Вот две наиболее популярные:
- Трафик через плавающий IP часто проходит через NAT-шлюзы и внешние границы сети провайдера. Это увеличивает задержки (ping) по сравнению с прямой передачей пакетов внутри приватной сети.
- Используя плавающий IP, вы выставляете сервисы виртуальных машин во внешнюю сеть. Даже если вы настроили файрвол, риск атаки все равно будет выше, чем при использовании изолированного внутреннего канала.
Отличная альтернатива — настройка сетевой связанности через Глобальный роутер Selectel. В этом случае ВМ2 будет доступна с ВМ1 по внутреннему IP-адресу в серой сети. Это приводит нас к тому, как провалидировать работоспособность созданной топологии:
Логинимся по SSH на ВМ2, ставим утилиту tcpdump и смотрим ICMP-пакеты.

Далее логинимся на ВМ1 и запускаем ping до ВМ2 по внутреннему адресу 172.16.0.2:

Глобальный роутер не настроен, поэтому связи между виртуальными машинами нет. Значит, нам нужно его добавить и настроить, чтобы в итоге получить вот такой вид:

Это можно сделать двумя способами:
- через панель управлениями (без автоматизации),
- автоматически через Terraform.
Способ 1. Настраиваем в панели управления
Не буду погружаться детально в этот подход, так как он довольно подробно описан в нашей документации. Достаточно напомнить, что потребуется:
- создать ресурс Глобального роутера,
- подключить к созданному роутеру сеть и подсеть в ru-1,
- подключить к созданному роутеру сеть и подсеть в ru-2.
В панели управления результат будет выглядеть следующим образом:

Проверим наш ping после настройки и увидим, что связанности все еще нет! Как так? Я же обещал, что будет все работать! А дело в том, что надо просто добавить статические маршруты:
- На ВМ1:
ip r add 172.16.0.0/28 via 192.168.0.1, - На ВМ2:
ip r add 192.168.0.0/28 via 172.16.0.1.
Наконец, мы получаем работающий вариант:

Больше статей о том, как работает облако:
Способ 2. Автоматическая настройка с помощью Terraform Provider
Теперь соберем аналогичную конструкцию с помощью Terraform-ресурсов. Как известно, хорошим тоном является организация шаблона по файлам:
- versions.tf,
- main.tf,
- vars.tf,
- outputs.tf.
Я предлагаю временно его нарушить для простоты демонстрации и описывать все в одном файле (main.tf). Это упростит определение параметров ресурсов. При росте размера шаблона или желании его параметризации рекомендую реорганизовать код и разделить на упомянутые файлы.
Инициализация провайдера
Ресурсы глобального роутера были добавлены недавно и доступны с версии 7.3.0 Terraform Provider. Указываем в шаблоне эту версию:
terraform {
required_providers {
selectel = {
source = "selectel/selectel"
version = "~> 7.3.0"
}
}
required_version = ">= 1.9.8"
}
Далее требуется инициализировать Selectel provider согласно официальной инструкции:
provider "selectel" {
domain_name = “123456”
username = “user”
password = “password”
auth_region = “ru-1”
auth_url = https://cloud.api.selcloud.ru/identity/v3/
}
Для проверки корректной настройки достаточно выполнить команду:
terraform init
Ожидаемый вывод при успешной настройке:

Создание ресурсов Глобального роутера
Теперь осталось описать ресурсы Глобального роутера:
- роутер,
- зона ru-1,
- зона ru-2,
- vpc сеть в регион ru-1,
- vpc подсеть в регионе ru-1,
- vpc сеть в регион ru-2,
- vpc подсеть в регионе ru-2.
Типы ресурсов и их параметры доступны в документации. Их можно найти по префиксу selectel_global_router_.
Шаг 1. Описываем глобальный роутер
Создание глобального роутера выполняется с использованием ресурса selectel_global_router_router_v1.
resource "selectel_global_router_router_v1" "gr_router" {
name = "terraform-router"
}
Выполним команду:
terraform plan
Ожидаемый ответ:

Шаг 2. Получаем UUID зон облака
Для подключения сетей к Глобальному роутеру в ресурсе сети необходимо указать параметр zone_id — UUID зон облака. Это можно сделать через датасорсы. В нашем облаке зоны имеют имена: ru-1, ru-2 и тип vpc.
data "selectel_global_router_zone_v1" "vpc_zone1" {
name = “ru-1”
service = "vpc"
}
data "selectel_global_router_zone_v1" "vpc_zone2" {
name = “ru-2”
service = "vpc"
}
Шаг 3. Подключаем сети к созданному роутеру
Описываем ресурсы сетей для подключения к роутеру. Перечисляем параметры:
- router_id – UUID ранее описанного роутера,
- zone_id – в какой зоне находится Openstack сеть, на основе дата сорсов выше,
- project_id – UUID проекта, в котором находятся искомые сети,
- name – имя для сети в глобальном роутере,
- os_network_id – UUID Openstack сети в соответствующей зоне.
UUID Openstack сети доступно как через CLI, так и через панель управления:

resource "selectel_global_router_vpc_network_v1" "gr_network_1" {
router_id = selectel_global_router_router_v1.gr_router.id
zone_id = data.selectel_global_router_zone_v1.vpc_zone1.id
os_network_id = "dcbb82ab-9d5d-4438-ab14-0a86242bcf09"
project_id = "8178a2a9d6834238a608d9d339519e6b"
name = "gr_net_1"
}
resource "selectel_global_router_vpc_network_v1" "gr_network_2" {
router_id = selectel_global_router_router_v1.gr_router.id
zone_id = data.selectel_global_router_zone_v1.vpc_zone2.id
os_network_id = "c83d4fb4-2036-41c5-b8c3-6faf1444b071"
project_id = "8178a2a9d6834238a608d9d339519e6b"
name = "gr_net_2"
}
Валидируем итог через команду:
terraform plan
В выводе должны добавиться два новых ресурса с указанными параметрами:

Шаг 4. Подключаем подсети к созданному роутеру
В заключительном блоке с ресурсами подсетей указываем следующие параметры:
- network_id — UUID одной из ранее созданных сетей, в которой находится подсеть;
- os_subnet_id — UUID Openstack подсети в соответствующей сети;
- name – имя для подсети в глобальном роутере;
- cidr — CIDR подсети, он должен совпадать с CIDR подсети в Openstack;
- service_addresses — два IP-адреса, которые будут использованы сервисом для внутренних целей. По аналогии с созданием в панели управления выберем два свободных адреса из указанного CIDR подсети, заканчивающихся на
.13,.14; - gateway — сервисный IP-адрес шлюза. По аналогии с созданием в панели управления выберем первый адрес из указанного CIDR подсети, заканчивающийся на
.1.
UUID и CIDR Openstack подсети доступно как через CLI, так и через панель управления:

resource "selectel_global_router_vpc_subnet_v1" "gr_subnet_1" {
network_id = selectel_global_router_vpc_network_v1.gr_network_1.id
os_subnet_id = "b44dba75-bb76-4fc1-85e6-da07066fdc7f"
cidr = "192.168.0.0/28"
gateway = "192.168.0.1"
service_addresses = ["192.168.0.13", "192.168.0.14"]
name = "gr_subnet_1"
}
resource "selectel_global_router_vpc_subnet_v1" "gr_subnet_2" {
network_id = selectel_global_router_vpc_network_v1.gr_network_2.id
os_subnet_id = "310dc6da-1d97-4ab3-9268-ab56d70beb86"
cidr = "172.16.0.0/28"
gateway = "172.16.0.1"
service_addresses = ["172.16.0.13", "172.16.0.14"]
name = "gr_subnet_2"
}
Вывод terraform plan должен быть вида:

Создание и проверка
Финально убеждаемся, что все ресурсы описаны корректно и создаем их:
terraform plan
terraform apply
Лог успешного создания всех ресурсов:

Проверяем, что у нас было создано через панель управления:

И запускаем тест проверки работоспособности, который использовали ранее:
- логинимся по SSH на ВМ2, ставим утилиту tcpdump и смотрим ICMP-пакеты;
- логинимся на ВМ1 и запускаем ping до ВМ2 по внутреннему адресу 172.16.0.2.


Как видно, сеть была настроена аналогичным образом и виртуальные машины могут получить доступ друг к другу по IP-адресам внутри приватных сетей.
Заключение
Если вы только осваиваете Terraform и хотите создать схожую инфраструктуру, вам может пригодиться наш репозиторий с примерами Terraform-шаблонов. По ссылке вы найдете шаблоны для создания двух ВМ в разных зонах, соединенных в одну сеть с помощью Глобального роутера. Всю эту топологию можно создать за пару команд в Terraform и сразу зайти на одну из виртуальных машин, чтобы отправить ping на другую.
Буду рад обратной связи по использованию новых ресурсов в Terraform. Найденные проблемы можно оформлять в виде issue проекта на GitHub.
В следующей статье планирую рассказать о еще одном популярном сценарии использования Глобального роутера: как соединить виртуальную машину в облаке с выделенным сервером. Рассмотрю как использовать Terraform для автоматизации такой топологии.