Облачные роутеры и публичные IP - Академия Selectel

Облачные роутеры и публичные IP

Андрей Гордиенко
Андрей Гордиенко Ведущий специалист
22 января 2025

Осветим такие вопросы, как преимущества использования облачного роутера и способы сэкономить на общедоступных IP‑адресах. Разберем получение доступа во внешнюю сеть через один облачный роутер сразу всеми участниками локальной сети, включая соседние точки в роутере глобальном. Покажем несколько приемов настроек маршрутизатора и, главное, как осуществлять пробросы портов на публичных IP.

Изображение записи

Вступление

Меня зовут Гордиенко Андрей, я ведущий специалист отдела поддержки облачных услуг в Selectel. Мы подготовили серию материалов с целью помочь всем, кто должен разбираться в особенностях сетей и уметь выстраивать новую или улучшать существующую инфраструктуру. Во второй публикации про установление связности между серверами мы незаслуженно обошли вниманием две сущности — пробежались по ним галопом, не задержались на тонкостях и особенностях настроек. Сегодня исправим это упущение.

Я давно занимаюсь поддержкой клиентов по вопросам работы облачной инфраструктуры и часто сталкиваюсь с недостаточным пониманием ими сетевых настроек. Казалось бы, интернет переполнен информацией, но в действительности мало кто знает, как правильно сконфигурировать то или иное устройство.

Начнем с рассмотрения облачного роутера — основы, на которой работает публичный IP (Floating IP, плавающий IP).

Облачный роутер

Облачный роутер отвечает как за маршрутизацию трафика внутри локальной сети, так и за доступ в сеть внешнюю. Мы пристальнее взглянем именно на возможность статической и динамической маршрутизации. Примерами послужат несколько серверов: два облачных в одной локации, один облачный в соседней и, как ни странно, один выделенный. С помощью него мы покажем, как можно масштабировать сервис в случае необходимости, используя связку глобального и облачного роутеров.

Подготовка сетей

Итак, перейдем в панель управления. Создадим демонстрационные серверы. Возьмем зоны ru-1 в Санкт-Петербурге, а также ru-2 и МСК-1 в Москве. Чтобы не запутаться, будем постепенно добавлять сети в глобальный роутер и объяснять причины наших действий.

Первой локацией станет МСК-1. Пока нет ничего примечательного. Сеть выберем 172.22.10.0/24, а шлюз, он же глобальный роутер, пусть будет 172.22.10.1/24.

Облачный сервер разместим в локации ru-1. Пока что не требуется каких‑то нетривиальных действий — используем те же шаги, что и при создании локации в МСК-1. Сервер получен без дополнительных настроек и не подключен к облачному роутеру. Сеть создана 10.10.10.0/24, глобальному роутеру отдадим 10.10.10.1/24.

Наконец, перейдем к локации ru-2. Здесь поступим хитрее — на этом примере изучим те особенности, о которых говорилось в самом начале.

  1. Первая сеть будет создана в глобальном роутере. Назначим ей адрес 192.168.0.0/24, а 192.168.0.1/24 — глобальному роутеру.
  2. Вторая сеть не будет соединяться с глобальным роутером и получит адрес 192.168.111.0/24.
  3. Эти сети будут подключены к работающему шлюзом облачному роутеру. Обратите внимание, что можно использовать любой сервер, выполняющий его роль — например, виртуальную машину на RouterOS.
Первая сеть с глобальным роутером.

Вторая сеть работает без глобального роутера, но подключена к роутеру облачному, IP‑адрес которого — 192.168.111.252/24.

Вторая сеть.

В нашем примере облачный роутер уже присутствует. В противном случае его надо создать, как описано в документации. И первую, и вторую сети добавляем по этой же инструкции. Для удобства визуального восприятия хостовую часть адресов обеих сетей сделаем одинаковой — xxx.xxx.xxx.252/24.

Создание и настройка серверов

Подготовив сети, приступим к созданию серверов. Заказываем по одному в локациях МСК-1 и ru-1c, а также два в ru-2c. Все подключаем к разным сетям.

Настроим сеть на выделенном сервере:


      
/etc/netplan/50-cloud-init.yaml
network:   ethernets:     eth0:       addresses:         - 77.223.118.225/24       gateway4: 77.223.118.1       match:         macaddress: '00:25:90:69:b6:fc'     eth1:       addresses:         - 172.22.10.2/24       nameservers:         addresses:           - 188.93.17.19           - 188.93.16.19       routes:         - to: 192.168.0.0/24           via: 172.22.10.1         - to: 10.10.10.0/24           via: 172.22.10.1   renderer: networkd   version: 2

Обратите внимание на маршруты: мы явно указываем, где должна находиться та или иная сеть. В примере они намеренно указаны разными. На практике же, при работе с сетями вида xxx.xxx.1.0/24, xxx.xxx.2.0/24 и т. п, маршрут можно объединить большой сетью:


    routes:
  - to: xxx.xxx.0.0/22
    via: 172.22.10.1

Проверяем сетевую связность:


    root@Curie:~# ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=1.67 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.415 ms
^C
--- 192.168.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.415/1.042/1.669/0.627 ms

root@Curie:~# ping 10.10.10.1
PING 10.10.10.1 (10.10.10.1) 56(84) bytes of data.
64 bytes from 10.10.10.1: icmp_seq=1 ttl=63 time=9.27 ms
64 bytes from 10.10.10.1: icmp_seq=2 ttl=63 time=9.16 ms
^C
--- 10.10.10.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 9.163/9.218/9.273/0.055 ms

Шлюз каждой сети вне МСК-1 доступен, а следовательно связность установлена:


    root@Curie:~# ping 10.10.10.2
PING 10.10.10.2 (10.10.10.2) 56(84) bytes of data.
64 bytes from 10.10.10.2: icmp_seq=1 ttl=62 time=9.00 ms
64 bytes from 10.10.10.2: icmp_seq=2 ttl=62 time=9.05 ms
^C
--- 10.10.10.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 9.003/9.024/9.046/0.021 ms

root@Curie:~# ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=63 time=0.285 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=63 time=0.343 ms
^C
--- 192.168.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.285/0.314/0.343/0.029 ms

Настройка маршрутизации

Для дальнейших действий понадобится установленный и настроенный OpenStack — консольный клиент для работы с облачной инфраструктурой. Мы на этом моменте останавливаться не будем, исчерпывающая инструкция — в нашей документации.

Вывод некоторых команд имеет сложное табличное форматирование, поэтому для удобства восприятия в отдельных случаях мы использовали отображение вывода картинкой.

Переходим к настройке облачного роутера:


    openstack router list
Вывод табличных данных в терминале.

    openstack router show 33cba694-a768-4dde-ac0a-3b4e04e62d6d
Вывод табличных данных в терминале.

В этом выводе нам нужно обратить внимание на строчку с полем routes — сейчас она пустая. Добавим маршруты до смежных сетей:


    openstack router add route \
  --route destination=172.22.10.0/24,gateway=192.168.0.1 \
  33cba694-a768-4dde-ac0a-3b4e04e62d6d
 
openstack router add route \
  --route destination=10.10.10.0/24,gateway=192.168.0.1 \
  33cba694-a768-4dde-ac0a-3b4e04e62d6d

Доступа к консоли роутера у нас нет, но проверить сетевую связность можно на выделенном сервере:


    root@Curie:~# ping 192.168.0.252
PING 192.168.0.252 (192.168.0.252) 56(84) bytes of data.
64 bytes from 192.168.0.252: icmp_seq=1 ttl=63 time=15.9 ms
64 bytes from 192.168.0.252: icmp_seq=2 ttl=63 time=0.152 ms
^C
--- 192.168.0.252 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.152/8.006/15.860/7.854 ms

Все отлично! Сейчас мы посмотрели, как работают статические маршруты на облачном роутере и как можно установить сетевую связность при помощи OpenStack CLI.

Теперь рассмотрим динамическую маршрутизацию. Ранее мы создавали два сервера в разных сетях и подключали к одному роутеру. Для демонстрации будем использовать скриншоты панели управления, так как удаленного доступа к серверам пока нет.

Изначально Server_1 сконфигурирован так, что соединение осуществляется через глобальный роутер:

Server_2, наоборот — по умолчанию к глобальному роутеру не подключен и работает через облачный.

Обратите внимание на маршрутизацию на сервере Server_1: данные передаются через глобальный роутер, так как указан именно его шлюз. Ранее мы описывали желаемую схему, где упоминали, что сеть 192.168.0.1/24 должна быть подключена и к глобальному роутеру, и к облачному. Так получится одновременно иметь и сетевую связность с локальной сетью, и доступ в интернет.

Актуализируем параметры маршрутизации. Для начала проверим маршрут, используемый по умолчанию, а также доступность серверов:


    root@server-1:~# ip route
default via 192.168.0.1 dev eth0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
root@server-1:~# ping 172.22.10.2
root@server-1:~# ping 192.168.111.2

Напомним, что 172.22.10.2 — адрес выделенного сервера в зоне МСК-1, а 192.168.111.2 — облачного сервера с сетью, не подключенной к глобальному роутеру в зоне ru-2.

Один из серверов недоступен, так как маршрут ведет на глобальный роутер, который ничего не знает о сети 192.168.111.0/24. Исправляем ситуацию и перенаправляем трафик на облачный роутер:


    root@server-1:~# ip route delete default via 192.168.0.1 dev eth0
root@server-1:~# ip route add default via 192.168.0.252 dev eth0
root@server-1:~# ip route
default via 192.168.0.252 dev eth0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2

Убедимся в подключении и к глобальной, и к локальной сетям. Для первого случая отправим эхо‑запросы, например, на один из DNS‑серверов Google, адрес которого легко запомнить:


    root@server-1:~# ping 8.8.8.8
root@server-1:~# ping 192.168.111.2

На скриншоте выше видно, что маршрут default указывает на облачный роутер — успешно выполняется команда ping до сервера в изолированной сети, при этом доступны и глобальные ресурсы. Поскольку каждая сеть в облачной платформе является отдельным, изолированным VXLAN, то при помощи облачного или другого роутера мы имеем возможность делить и объединять их.

Доступ к сетям через глобальный роутер также сохранится, однако могут появляться затруднения в работе с маршрутами: при долгом отсутствии активности связность иногда теряется. В таком случае потребуется отправка нескольких сетевых пакетов для ее восстановления. По этой причине рекомендуется дополнить маршруты статикой до смежных сетей. О способах это сделать вручную и автоматически читайте в нашей документации.

Публичный IP

При необходимости иметь несколько публичных точек доступа можно арендовать единственный IP‑адрес, а не пул.

Подробнее о работе с публичными IP, в том числе подготовке подсетей и подключении балансировщика нагрузки, можно почитать в нашей документации.

Предварительные условия

Использование одного адреса вместо нескольких происходит по следующей схеме. Арендованный IP назначается облачному роутеру, за которым располагается NAT, где осуществляется проброс портов 1:1. Следовательно, весь трафик, поступающий на определенный порт внешнего адреса, перенаправляется на соответствующий адрес внутренней подсети.

Однако немногие знают, что можно сэкономить еще больше. Для примера построим простейшую схему из нескольких серверов — скажем, трех.

Использование глобального роутера позволяет объединить несколько зон доступности и даже регионов, при этом обеспечивая хорошее резервирование. Однако в общедоступной сети такой гибкости нет. Даже при использовании глобального и облачного роутеров в единой связке, публичный IP‑адрес не может быть присвоен резервному серверу в другой зоне или регионе — только внутри своего пула адресов.

Так как в основном вся работа производится с помощью OpenStack CLI, то следующие примеры будут взяты из консоли.

Напомним, что в предыдущем разделе про облачный роутер мы создали три сервера:


    openstack server list
Вывод табличных данных в терминале.

Ту же информацию о созданных серверах можно увидеть и в панели управления:

Параметры серверов следующие:


      
Параметры сервера Server_1
root@server-1:~# ip address 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether fa:16:3e:fb:44:e4 brd ff:ff:ff:ff:ff:ff inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fefb:44e4/64 scope link valid_lft forever preferred_lft forever

      
Параметры сервера Server_2
root@server-2:~# ip address 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether fa:16:3e:fb:44:e4 brd ff:ff:ff:ff:ff:ff inet 192.168.0.3/24 brd 192.168.0.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fefb:44e4/64 scope link valid_lft forever preferred_lft forever

      
Параметры сервера Server_3
root@server-3:~# ip address 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether fa:16:3e:fb:44:e4 brd ff:ff:ff:ff:ff:ff inet 192.168.0.4/24 brd 192.168.0.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fefb:44e4/64 scope link valid_lft forever preferred_lft forever

Маршрутизация у каждого из них одинаковая. Пример первого сервера. Для двух других вывод лкоманды будет аналогичный:


    root@server-1:~# ip route
default via 192.168.0.1 dev eth0 
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.N

Обратите внимание на существенный момент в дальнейшей настройке. Так как мы хотим разделить публичный IP‑адрес на несколько серверов, то он должен быть свободным — после резервирования назначить его какому‑либо серверу уже не получится.

Заказываем IP‑адрес. В этот раз достался 94.26.236.174:


    openstack floating ip create external-network
openstack floating ip list

Приступаем к настройке. В примерах возьмем порты 80 для веб‑сервера и 22 для SSH. 

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

  1. Один сетевой порт может использоваться только одним сервером. Например, с определенным адресом связан сервер, работающий на порту 443. Мы не сможем развернуть другой сервер, который использует эти же адрес и порт: правило в таблице маршрутизации будет одно — src port в пределах своего IP‑адреса уникален.
  2. Такое принципиальное ограничение заставляет тщательно планировать распределение портов. К примеру, веб‑серверы обычно работают на портах 80, 443 и 8080. Каждый из них может служить точкой связи только для одного экземпляра самой программы.
  3. Аналогично, порт назначения может быть тоже только один — dst port в пределах своего IP‑адреса уникален.

Проброс портов на примере веб‑сервера

Продвигаемся дальше. Установим nginx на каждый из трех наших серверов, настроем их. В примере ниже показана работа на Server_1. Те же самые действия надо повторить и на двух других.


    root@server-1:~# apt update
root@server-1:~# apt install -y apache2
root@server-1:~# systemctl start apache2
root@server-1:~# systemctl enable apache2
root@server-1:~# echo "Hello World from $(hostname) - $(hostname -I)" > /var/www/html/index.html

Проверяем работоспособность установленных экземпляров nginx. Подойдет любой компьютер, административных прав не требуется:


    $ curl 94.26.236.174
Hello World from server-1 - 192.168.0.2

Аналогичную проверку можно выполнить в браузере: если ввести в строку адреса 94.26.236.174, то должна отобразиться страница с заданной строкой: «Hello World from server-1 — 192.168.0.2»

Экземпляры nginx на всех трех серверах работают. Теперь очередь организовать доступ к каждому из них по единому IP‑адресу 94.26.236.174. При этом, server_1 будет доступен по порту 80, Server_2 — по порту 8080, а Server_3 — по 8081. В результате мы должны будем увидеть IP‑адреса каждого из этих серверов.

Приступаем к настройке публичного адреса:


    openstack floating ip show 872b6a22-2ff5-4e2d-a79e-731484027f6d
Вывод табличных данных в терминале.

Сейчас сервер ожидаемо не подключен, так как fixed_ip_address не задан. Строка 872b6a22-2ff5-4e2d-a79e-731484027f6d — идентификатор публичного IP‑адреса, его мы видели в выводе команды openstack floating ip list.

Добавляем правила, как описано в нашей документации.


    openstack port list
Вывод табличных данных в терминале.

Следующая команда настраивает проброс порта так, что любой трафик, поступающий на плавающий IP-адрес (ID 7bcd328e-fb78-47db-ae01-8eb3d50284ca) по порту 80, будет перенаправлен на внутренний сервер с IP-адресом 192.168.0.2, также на порт 80. Это типичная конфигурация для веб-сервера.


    openstack floating ip port forwarding create \
  --internal-ip-address 192.168.0.2 \
  --port 7bcd328e-fb78-47db-ae01-8eb3d50284ca \
  --internal-protocol-port 80 \
  --external-protocol-port 80 \
  --protocol tcp \
 872b6a22-2ff5-4e2d-a79e-731484027f6d
Вывод табличных данных в терминале.

Следующая команда, как и предыдущая, создает правило проброса портов для плавающего IP-адреса (ID 6270bf07-5551-4dc2-9a8d-1044ad69dac0), но с небольшим отличием. Поступающий по порту 8080 трафик будет перенаправлен на порт 80 внутреннего сервера 192.168.0.3 — ведь порт 80 на внешнем хосте уже занят.


    openstack floating ip port forwarding create \
  --internal-ip-address 192.168.0.3 \
  --port 6270bf07-5551-4dc2-9a8d-1044ad69dac0 \
  --internal-protocol-port 80 \
  --external-protocol-port 8080 \
  --protocol tcp \
 872b6a22-2ff5-4e2d-a79e-731484027f6d
Вывод табличных данных в терминале.

Создадим еще одно правило, аналогичное предыдущим, но с другим внешним портом. В результате, трафик, приходящий на плавающий IP-адрес (ID 5b7a6c47-989b-4de0-8aec-2e94a4187297) по порту 8081, будет перенаправлен на порт 80 внутреннего сервера 192.168.0.4.


    openstack floating ip port forwarding create \
  --internal-ip-address 192.168.0.4 \
  --port 5b7a6c47-989b-4de0-8aec-2e94a4187297 \
  --internal-protocol-port 80 \
  --external-protocol-port 8081 \
  --protocol tcp \
 872b6a22-2ff5-4e2d-a79e-731484027f6d
Вывод табличных данных в терминале.

Мы разместили три веб-сервера за одним плавающим IP‑адресом, используя разные порты для доступа к каждому из них. Посмотреть созданные правила можно следующей командой, указав идентификатор публичного IP‑адреса:


    openstack floating ip port forwarding list 872b6a22-2ff5-4e2d-a79e-731484027f6d
Вывод табличных данных в терминале.

Эту информацию мы не увидим в панели управления, а значит документировать все подобные изменения нужно самостоятельно. Обратите внимание на следующий скриншот — наша работа с OpenStack никак не отражена:

Осталось проверить работоспособность созданных правил. На каждом из вышеперечисленных портов — 80, 8080, 8081 — должна открыться веб‑страничка с соответствующим внутренним IP‑адресом. Можно воспользоваться командой curl, а можно ввести данные — IP‑адрес и порт, записанные также через двоеточие — в адресную строку браузера. По умолчанию подразумевается порт 80, поэтому в первом случае его можно не указывать.


    $ curl 94.26.236.174
Hello World from server-1 - 192.168.0.2 

$ curl 94.26.236.174:8080
Hello World from server-2 - 192.168.0.3 

$ curl 94.26.236.174:8081
Hello World from server-3 - 192.168.0.4

Все три сервера отлично работают при обращении через единый IP‑адрес.

Проброс портов на примере SSH

Аналогично пробросим порты для SSH.


    openstack floating ip port forwarding create \
  --internal-ip-address 192.168.0.2 \
  --port 7bcd328e-fb78-47db-ae01-8eb3d50284ca \
  --internal-protocol-port 22 \
  --external-protocol-port 22 \
  --protocol tcp \
 872b6a22-2ff5-4e2d-a79e-731484027f6d

openstack floating ip port forwarding create \
  --internal-ip-address 192.168.0.3 \
  --port 6270bf07-5551-4dc2-9a8d-1044ad69dac0 \
  --internal-protocol-port 22 \
  --external-protocol-port 222 \
  --protocol tcp \
 872b6a22-2ff5-4e2d-a79e-731484027f6d

openstack floating ip port forwarding create \
  --internal-ip-address 192.168.0.4 \
  --port 5b7a6c47-989b-4de0-8aec-2e94a4187297 \
  --internal-protocol-port 22 \
  --external-protocol-port 2222 \
  --protocol tcp \
 872b6a22-2ff5-4e2d-a79e-731484027f6d

Еще раз введем команду перечисления имеющихся правил. Несложно заметить, что список стал больше — он пополнился маршрутизацией для портов 22, 222, 2222:


    openstack floating ip port forwarding list 872b6a22-2ff5-4e2d-a79e-731484027f6d
Вывод табличных данных в терминале.

Для проверки пытаемся установить SSH-соединение. При необходимости прерываем процесс нажатием Ctrl-C.


    $ ssh -p 22 root@94.26.236.174
SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11
root@94.26.236.174's password:
^C

$ ssh -p 222 root@94.26.236.174
SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11
root@94.26.236.174's password:
^C

$ ssh -p 2222 root@94.26.236.174
SH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11
root@94.26.236.174's password:
^C

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


    $ ssh -p 2223 root@94.26.236.174
ssh: connect to host 94.26.236.174 port 2223: Connection refused

Заключение

Упущение второй части исправлено. Мы рассмотрели преимущества облачного роутера и способы экономии на публичных IP-адресах. Объяснили механизм доступа во внешнюю сеть через облачный роутер для всех участников локальной сети, включая соседние точки в глобальном роутере. Продемонстрировали приемы настройки маршрутизатора и проброс портов на публичных IP‑адресах.