
Итак, у вас есть две виртуальные машины в разных регионах, на которых крутится дев, тест, прод (нужное подчеркнуть). И вам нужно соединить их приватной сетью. В целом это можно сделать и ручками. Но когда инфраструктура разрастется, вы точно не захотите заниматься подобными вещами.
Привет, Хабр! Меня зовут Сергей, я разработчик облачной платформы в Selectel. В этой статье я расскажу, как с помощью Terraform Provider автоматизировать настройку глобального роутера. DevOps-инженеры и все, кто знаком с Terraform, статья для вас.
Эта статья рассчитана на специалистов, которые умеют работать с 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-адреса, но настраивать через них связь нельзя по ряду причин. Вот две наиболее популярные:
Трафик через плавающий 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-шаблонов. По ссылке вы найдете шаблоны для создания двух ВМ в разных зонах, соединенных в одну сеть с помощью Глобального роутера. Всю эту топологию можно создать за пару команд в Terraform и сразу зайти на одну из виртуальных машин, чтобы отправить ping на другую.
Буду рад обратной связи по использованию новых ресурсов в Terraform. Найденные проблемы можно оформлять в виде issue проекта на GitHub.
В следующей статье планирую рассказать о еще одном популярном сценарии использования Глобального роутера: как соединить виртуальную машину в облаке с выделенным сервером. Рассмотрю как использовать Terraform для автоматизации такой топологии.
