Облачные роутеры и публичные IP
Осветим такие вопросы, как преимущества использования облачного роутера и способы сэкономить на общедоступных 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. Здесь поступим хитрее — на этом примере изучим те особенности, о которых говорилось в самом начале.
- Первая сеть будет создана в глобальном роутере. Назначим ей адрес 192.168.0.0/24, а 192.168.0.1/24 — глобальному роутеру.
- Вторая сеть не будет соединяться с глобальным роутером и получит адрес 192.168.111.0/24.
- Эти сети будут подключены к работающему шлюзом облачному роутеру. Обратите внимание, что можно использовать любой сервер, выполняющий его роль — например, виртуальную машину на RouterOS.
Вторая сеть работает без глобального роутера, но подключена к роутеру облачному, IP‑адрес которого — 192.168.111.252/24.
В нашем примере облачный роутер уже присутствует. В противном случае его надо создать, как описано в документации. И первую, и вторую сети добавляем по этой же инструкции. Для удобства визуального восприятия хостовую часть адресов обеих сетей сделаем одинаковой — xxx.xxx.xxx.252/24.
Создание и настройка серверов
Подготовив сети, приступим к созданию серверов. Заказываем по одному в локациях МСК-1 и ru-1c, а также два в ru-2c. Все подключаем к разным сетям.
Настроим сеть на выделенном сервере:
/etc/netplan/50-cloud-init.yamlnetwork: 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_1root@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_2root@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_3root@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.
Перед началом проясним работу проброса портов, чтобы исключить возможные неясности.
- Один сетевой порт может использоваться только одним сервером. Например, с определенным адресом связан сервер, работающий на порту 443. Мы не сможем развернуть другой сервер, который использует эти же адрес и порт: правило в таблице маршрутизации будет одно — src port в пределах своего IP‑адреса уникален.
- Такое принципиальное ограничение заставляет тщательно планировать распределение портов. К примеру, веб‑серверы обычно работают на портах 80, 443 и 8080. Каждый из них может служить точкой связи только для одного экземпляра самой программы.
- Аналогично, порт назначения может быть тоже только один — 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‑адресах.