Saturday, January 26, 2019

Why Azure supports only unicast traffic?

Заинтересовался таким вопросом: почему микрософтовское облачко, Azure, не позволяет групповой (multicast) и широковещательный (broadcast) трафик между виртуальными машинами? Не означает ли это, что они используют на уровне ниже IP не Ethernet, а какой-то свой протокол, специально под облако заточенный?

(Я не имел дела с Амазоном, но и там та же петрушка, насколько я понимаю.)

Ответ: в общем, да.

В Azure решают проблему создания миллионов виртуальных сетей поверх единой физической благодаря протоколу VXLAN - если Ethernet-фрейм, посылаемый виртуалкой, предназначается для виртуалки на другом хосте, то он инкапсулируется виртуальным свитчем (процессом, бегущим на гипервизоре) внутрь UDP-пакета, и этот пакет доставляется по физической сети этому другому хосту. Там виртуальный свитч извлекает оригинальный фрейм и передаёт его своей виртуалке. Для обоих виртуальных машин всё выглядит так, словно они сидят в одном сегменте Ethernet.

Как в такой архитектуре можно передавать multicast или broadcast трафик? Два способа:

1. Рассовывать его по пакетам-носителям типа multicast и посылать их всем хостам, на которых определёна та же виртуальная Ethernet-сеть (насколько я понимаю, ей в Azure соответствует объект Subnet).

2. Рассовывать его по пакетам-носителям типа unicast и размножать их: посылать отдельную копию для каждого хоста из того же списка.

Обе опции проблемны. Допустим, у нас есть 1000 хостов, но из них виртуалки в этой сети существуют лишь на 20-ти. Тогда:
  • В первом случае нам придётся доставлять пакет-носитель всей тысяче хостов. Раутеры и свитчи в физической сети между ними понятия не имеют, какие виртуальные сети существуют на каких хостах, и потому оптимизировать трафик, доставляя такой пакет лишь заинтересованным в нём хостам (подобно тому, как это делается функцией IGMP snooping в традиционных VLAN-ах) не могут. Стало быть, если у нас какая-то виртуалка сгенерирует multicast-поток в 100Mbps, то каждый хост в датацентре получит все эти 100Mbps, в большинстве случаев совершенно для него бесполезные.
  • Во втором же случае поток в 100Mbps приведёт к тому, что хосту-отправителю придётся высылать 1.9Gbps (и даже слегка больше, учитывая overhead), высылая на каждый пакет с виртуалки девятнадцать своих, по одному на каждый хост с той же сетью.
То есть оба варианта могут значительно добавить нагрузку на облако - на сеть и на процессорное время, потраченное на обработку бесполезного трафика. Именно поэтому инженеры Azure решили, что единственным разрешённым видов трафик будет unicast. На каждый пакет, посылаемый виртуалкой, будет приходиться максимум один пакет, посылаемый по физической сети меж хостами - либо вообще ни одного, если предназначен он виртуалке на том же хосте.

Тут внимательный читатель воскликнет - позвольте, а как же ARP? Как же виртуалки будут узнавать MAC-адреса друг друга или раутеров, и что вообще будет писаться в поле "destination MAC" исходящих пакетов?

А вот что: на любой ARP-запрос с виртуалки vSwitch, то есть процесс, бегущий на хосте, ответит одним и тем же фальшивым MAC-адресом: 12:34:56:78:9a:bc. То есть любой пакет, на любой IP-адрес, будет послан виртуалкой на этот destination MAC.

Ну хорошо, а как же тогда пакеты доставлять? На адрес A в той же сети пакет ушёл с этим MAC, и на адрес B в ней же, и на адрес C в другой сети, и на адрес D вообще где-то в Интернете - и у всех MAC одинаковый. Что же с ним делать?

Ответ: а ничего не делать. Облаку плевать на MAC, доставка происходит прямо по IP-адресу получателя. То есть - к примеру, vSwitch получает от виртуалки пакет, где dst IP = 1.2.3.4 и dst MAC = 12:34:56:78:9a:bc. Если адрес 1.2.3.4 принадлежит другой виртуалке на том же хосте и той же сети, то пакет будет передан ей, и конец делу. Если же на другом хосте - то пакет будет обёрнут в VXLAN-пакет на адрес того хоста, и послан туда. Там, соответственно, будет декапсулирован и передан по назначению.

То есть традиционный Ethernet-свитчинг  в Azure customer networks  реально не используется - Ethernet лишь эмулируется для виртуалок. Этим эти сети отличаются от customer networks в OpenStack или CloudStack, где виртуальные и физические свитчи по-прежнему имеют дело с MAC-адресами.

Отсюда следуют несколько важных следствий:

1. Любой протокол не на основе IP (скажем, IPX, если кто ещё помнит такой) работать в Azure не может по определению.

2. Нельзя просто раздать виртуалкам дополнительные адреса (secondary IPs, loopback adapters) или передавать через них трафик из других сетей (скажем, использовать их для VPN) и ожидать, что всё будет работать. Нет. Облако должно знать, какие адреса относятся к каким машинам. Если машина посылает пакет с source IP, который ей не принадлежит и о котором облако не знает, он будет выкинут нафиг.
Есть два способа это починить:

  • если этот source IP относится к тому же сабнету - то можно облаку указать его как secondary IP этой виртуалки.
  • или же можно указать, что виртуалка занимается передачей чужого трафика (включить IP forwarding) и настроить на неё user-defined route (UDR), чтобы ответы на эти адреса приходили к ней. Обычно это делают, если виртуалка работает как firewall / VPN gateway.
3. Допустим, у нас есть сеть 10.10.10.0/24, в ней есть две машины (10.10.10.11 и 10.10.10.12), и для обеих определён общий default gateway, 10.10.10.1.
И допустим, что вторая виртуалка работает VPN-гейтом, передавая трафик от удалённой сети 192.168.12.0/24.
Тогда чтобы позволить первой машинке общаться с этой удалённой сетью, в обычных сетях нам надо было бы определить на ней маршрут через вторую:
    route -p add 192.168.12.0/24 10.10.10.12

Но в Azure в этом нет необходимости. Поскольку в тот момент, что первая виртуалка пошлёт пакет на адрес, скажем, 192.168.12.222 - будет совершенно неважно, имела ли она в мыслях передать его напрямую, или на дефолтовый гейт 10.10.10.1, или на VPN-гейт 10.10.10.12, и чей именно MAC-адрес она запрашивала по ARP миллисекундой ранее. Пакет будет выглядеть совершенно одинаково:
src IP  = 10.10.10.11,     dst IP  = 192.168.12.222
src MAC = some_MAC_of_VM1, dst MAC = 12:34:56:78:9a:bc


После чего произойдёт следующее: vSwitch проверит свою таблицу маршрутов и узрит в ней user-defined route: на сеть 192.168.12.0/24 ступай через 10.10.10.12. После чего пакет передан второй виртуалке - либо напрямую, если она на том же хосте, что и первая, либо, если она на другом хосте - то через туннель VXLAN между этими хостами.

Вот такие пироги.