Перейти до вмісту

Мережі датацентру й хмари

Ви запускаєте два контейнери на різних серверах, і вони бачать одне одного за IP-адресами так, ніби сидять в одному комутаторі. Віртуальну машину переносять на іншу стійку, і вона зберігає адресу та відкриті з’єднання. Запит до одного веб-сервісу породжує сотню внутрішніх запитів між серверами, яких користувач ніколи не побачить. У мережі, спроєктованій для офісу, нічого з цього не працює, а можливим воно стало завдяки ідеям, розібраним у цьому модулі.

Майже всі ці ідеї складаються з уже знайомого: мости й veth (модуль 4), маршрутизація й ECMP (модуль 7), NAT (модуль 9), UDP (модуль 11). Новизна в масштабі й у тому, що цей набір автоматизовано.

Передумови. Комутація й STP (модуль 4), маршрутизація й ECMP (модуль 7), NAT і conntrack (модуль 9), MTU й фрагментація (модуль 6). Про простори імен і контейнери — у модулі 17 курсу «Операційні системи»: віртуалізація й контейнери.

Чому дерево не масштабується

Section titled “Чому дерево не масштабується”

Класичну корпоративну мережу будують деревом із трьох шарів. Сервери під’єднані до комутаторів доступу, ті до агрегації, агрегація до ядра. Трафік у такій мережі здебільшого йшов «північ–південь»: від користувача всередину, до сервера й назад, тож вузьке місце було вгорі, біля виходу назовні, і там ставили найпотужніше обладнання.

Датацентр інший. Більша частина трафіку йде «схід–захід»: між серверами всередині будівлі. Запит користувача розходиться на десятки сервісів, розподілене сховище реплікує дані, задача машинного навчання обмінюється градієнтами між сотнями машин. Такий трафік дерево обслуговує погано.

  • Перепідписка. Кожен шар угору має менше смуги, ніж сумарно споживають нижні. Сервери під одним комутатором доступу спілкуються швидко, а ті, що на різних гілках, змагаються за вузьку смугу біля ядра.
  • Нерівність шляхів. Залежно від розташування, кількість стрибків між двома серверами різна, отже різні затримки. Розробнику доводиться думати, на якій стійці стоїть його сервіс.
  • STP. Дерево з резервними зв’язками вимагає, щоб зайві з’єднання були вимкнені, бо інакше виникають петлі (модуль 4). Виходить, що половина дорогих зв’язків простоює, доки щось не зламається.
  • Ядро як межа масштабу. Щоб витримати більше, треба купити потужніший, і значно дорожчий, центральний пристрій, а його можливості обмежені.

Вихід запозичено з телефонії, з ідеї комутаційної мережі Клоса (Clos): замість одного потужного центру багато простих комутаторів з’єднують так, щоб вони разом поводилися як один великий.

Мережа spine-leaf: кожен leaf-комутатор з’єднаний з кожним spine-комутатором, між двома серверами на різних leaf є стільки рівноцінних шляхів, скільки spinespine 1spine 2spine 3spine 4leaf 1leaf 2leaf 3leaf 4сервериЄмність збільшують, додаючи spine; порти — додаючи leaf.
Кожен leaf з'єднаний із кожним spine. Суцільна й пунктирна лінії показують два з чотирьох рівноцінних шляхів між першим і четвертим leaf.

Сервери підключають лише до leaf-комутаторів (часто один стоїть нагорі стійки, звідси ToR, top of rack). Кожен leaf має зв’язок із кожним spine. Spine між собою не з’єднані й серверів не мають. Що з цього виходить:

  • Будь-які два сервери на різних leaf розділяють рівно три пристрої: leaf, spine, leaf. Відстань однакова для всіх пар, тож затримка передбачувана й розміщення серверів не важить.
  • Між двома leaf існує стільки незалежних шляхів, скільки spine. Вихід із ладу одного spine прибирає лише одну з N смуг.
  • Ємність росте додаванням. Мало смуги — додайте spine, бракує портів для серверів — додайте leaf. Замінювати центр не треба.

Разом із цим відмовляються від STP: зв’язки між leaf і spine працюють на третьому рівні, кожен є маршрутизованим інтерфейсом із власною підмережею, а між пристроями діє динамічна маршрутизація (модуль 8), найчастіше BGP. Петель на третьому рівні немає, бо пакет має TTL. Шляхи ж використовуються одночасно, і цим займається ECMP.

Маршрутизатор має кілька рівноцінних шляхів до одного префікса, і встановлює їх усі (модуль 7). Перед ним стоїть питання, яким шляхом відправити конкретний пакет. Якщо розкидати пакети по черзі, пакети одного TCP-з’єднання приходитимуть у різному порядку, і TCP сприйме це як втрату. Тому шлях обирають за хешем полів пакета: той самий потік завжди йде тим самим шляхом, а різні потоки розподіляються статистично рівномірно.

Яке поле входить у хеш, вирішує налаштування. Якщо лише адреси відправника й отримувача, усі з’єднання між двома серверами потраплять в один шлях. Якщо ще й порти (5-кортеж: адреси, порти, протокол), різні з’єднання розкидаються. У розділі «Як це насправді в Linux» ви побачите обидва випадки.

Слабке місце ECMP у тому, що він працює з потоками, а не з байтами. Два величезні потоки, які випадково хеш поклав на один шлях, зіткнуться, хоча сусідній шлях простоює. Для датацентру з тривалими потоками (копіювання даних, навчання моделей) це реальна проблема, і для неї придумують додаткові прийоми: більше шляхів, розбиття потоку на короткі порції (flowlet), балансування за зайнятістю.

Третій рівень у фабриці розв’язав проблему масштабу, але дещо зламав. Віртуальна машина, яку переносять на сервер під іншим leaf, опиняється в іншій IP-підмережі. Адреса мала б змінитися, з’єднання обірватися. Крім того, різним клієнтам хмари потрібні власні ізольовані мережі, де вони можуть вільно вибирати адреси, які перетинаються з чужими. VLAN (модуль 5) дає лише 4094 ідентифікатори, а також вимагає тягти рівень 2 крізь усю мережу, тобто повертає петлі й STP.

Розв’язують це оверлеєм (overlay): поверх маршрутизованої фабрики, яку називають підкладкою (underlay), будують віртуальні мережі. Початковий кадр Ethernet віртуальної машини загортають у нову IP-датаграму, звичайним маршрутизованим шляхом доставляють на сервер призначення й там розгортають. Підкладка бачить лише зовнішні заголовки й нічого не знає про віртуальні мережі. Найпоширеніша реалізація — VXLAN (RFC 7348).

Інкапсуляція VXLAN: початковий Ethernet-кадр віртуальної машини стає вмістом UDP-датаграми на порт 4789 між двома VTEP, додається 50 байтів заголовківВМ 1VTEP 1ВМ 2VTEP 2фізична мережа бачить лише зовнішні заголовкизовнішні (underlay)Ethernet 14IP 20UDP :4789 8VXLAN, VNI 8внутрішній кадр (overlay)14 + 20 + 8 + 8 = 50 байтів накладних витратEthernetIPдані
Кадр ВМ не змінюється: його вміщують у UDP-датаграму на порт 4789 і доставляють між двома VTEP. Фізична мережа бачить лише зовнішні заголовки.

Вузол, що загортає й розгортає кадри, називається VTEP (VXLAN Tunnel Endpoint). Це може бути Linux на сервері або leaf-комутатор. Заголовок VXLAN несе 24-бітний ідентифікатор віртуальної мережі VNI (VXLAN Network Identifier): це приблизно шістнадцять мільйонів мереж проти чотирьох тисяч у VLAN. Зовнішня датаграма йде звичайним UDP на порт 4789, тож фабрика маршрутизує її як будь-який інший IP-трафік. До того ж вихідний порт UDP обчислюють за хешем внутрішнього кадру, тож навіть оверлейний трафік між двома VTEP розкидається ECMP по різних шляхах: для фабрики це різні потоки.

Накладні витрати постійні: 14 байтів зовнішнього Ethernet, 20 IPv4, 8 UDP і 8 VXLAN — разом 50 байтів. Тому кадр із MTU 1500 всередині не влізе в підкладку з тим самим MTU 1500, і про це доводиться дбати окремо: або знижувати MTU внутрішніх інтерфейсів (типове 1450), або піднімати MTU підкладки (у датацентрах зазвичай так: jumbo-кадри близько 9000). Це та сама проблема, що й Path MTU Discovery з модуля 6, і на практиці вона так само незручна.

Залишається питання, куди надсилати кадр: VTEP мусить знати, за яким VTEP стоїть чужа MAC-адреса. Найпростіший варіант — навчання за потоком, як у комутатора (модуль 4): невідомий адресат розсилають усім VTEP, відповідь дає відповідність «MAC → VTEP». У великих мережах це дорого, і сучасні фабрики розповсюджують ці відповідності через BGP (розширення EVPN).

Контейнер має власний простір імен мережі, отже власний стек: інтерфейси, адреси, таблицю маршрутів. Щоб він кудись достукався, цей простір треба з’єднати зі світом.

Міст + veth + NAT — типовий режим Docker. Docker створює на господарі міст (docker0), для кожного контейнера пару veth: один кінець у просторі контейнера, інший приєднаний до мосту. Контейнери отримують адреси з приватної підмережі, а шлюзом служить міст. Щоб вийти в зовнішню мережу, пакети проходять NAT: правило masquerade підставляє адресу господаря (модуль 9). Щоб зовні дістатися контейнера, публікують порт, і це вже DNAT із порту господаря в адресу й порт контейнера. Жодної магії: усе це ви збираєте нижче за десять рядків.

macvlan обходить міст господаря: контейнер отримує власну MAC-адресу на фізичному інтерфейсі й виглядає в мережі як окрема машина, з адресою з вашої підмережі. Швидко й без NAT, але за замовчуванням сам господар не може зв’язатися з такими контейнерами через той самий інтерфейс, і комутатор на порту має дозволяти кілька MAC-адрес.

Оверлей між господарями. Контейнери з різних машин мусять бачити одне одного. Тут і повертається VXLAN: міст на кожному господарі з’єднується тунелем із мостами інших, і контейнери опиняються в одній віртуальній мережі.

Kubernetes ставить до мережі жорстку вимогу: кожен под (pod) має власну IP-адресу, і будь-який под може звернутися до будь-якого іншого за цією адресою без NAT, навіть якщо вони на різних вузлах. Вимога зручна для застосунків: порти не треба перепризначати, а адреса, яку под бачить у себе, збігається з тією, що бачать інші.

Виконувати її Kubernetes не вміє сам. Він делегує цю роботу CNI-плагіну (Container Network Interface). Коли вузол запускає под, то створює простір імен мережі й викликає плагін: той підключає простір до мережі (пара veth, адреса з діапазону вузла) і налаштовує маршрути. Різні плагіни реалізують мережу по-різному. Одні будують оверлей на VXLAN (Flannel), інші розповсюджують маршрути до подів через BGP без інкапсуляції (Calico), ще одні реалізують усе на eBPF (Cilium). Вибір плагіна визначає накладні витрати, видимість і політики безпеки.

Поди недовговічні. Їх замінюють, переносять, масштабують, тож IP-адреса пода не годиться для клієнтів. Для цього існує Service: стабільна віртуальна адреса (ClusterIP) і назва в DNS, за якою ховається множина подів. На кожному вузлі працює kube-proxy: він стежить за набором подів, що стоять за Service, і записує в ядро правила, які підмінюють адресу призначення віртуальної адреси на адресу одного зі справжніх подів, тобто це DNAT із випадковим вибором. Правила зазвичай реалізовано в iptables або IPVS. Якщо вони відстають від реального набору подів, частина запитів іде на адреси подів, яких уже немає. Деякі плагіни (Cilium) замінюють kube-proxy програмою eBPF, яка вирішує те саме без ланцюжка правил.

Шлях пакета між подами на різних вузлах

Section titled “Шлях пакета між подами на різних вузлах”

Щоб скласти картину докупи, простежимо, що відбувається, коли под A на вузлі 1 надсилає запит до Service, за яким стоїть под B на вузлі 2.

  1. Под A звертається до віртуальної адреси Service. Пакет виходить з простору імен пода через veth на вузол.

  2. На вузлі 1 правила kube-proxy (або програма eBPF) переписують адресу призначення з віртуальної на адресу пода B. Запис про це зберігає conntrack (модуль 9), щоб відповідь повернулася тим самим шляхом.

  3. Пакет із адресою пода B маршрутизується вузлом. Залежно від CNI він або йде звичайним маршрутом (Calico з BGP), або потрапляє в тунель VXLAN до вузла 2 (Flannel).

  4. Вузол 2 доставляє пакет у veth пода B. Відповідь іде назад, і conntrack на вузлі 1 повертає їй адресу Service, тож под A бачить відповідь саме від тієї адреси, до якої звертався.

Усе це звичайні механізми цього курсу (простір імен, veth, маршрути, NAT, інколи VXLAN), лише з автоматичним керуванням. Коли щось не працює, діагностика йде тими самими кроками: де пакет зник, який маршрут чи правило його відкинуло, чи не підмінили адресу не там, де треба.

У традиційному комутаторі два завдання живуть в одному корпусі: площина керування (control plane) вирішує, як пересилати, за протоколами на кшталт OSPF і BGP, а площина даних (data plane) виконує рішення на швидкості каналу. Кожен пристрій окремо вирішує за себе, і мережа як ціле не має одного місця, де її можна описати.

Програмно-визначена мережа (SDN) ділить ці площини. Керування виносять у контролер, який має повну картину мережі, а пристрої перетворюються на виконавців: контролер записує в їхні таблиці правила, а пристрої пересилають за ними. Найвідоміший протокол такого запису — OpenFlow. На Linux роль програмованого комутатора відіграє Open vSwitch.

Так мережу можна змінювати з програми: контролер розгортає віртуальну мережу клієнта хмари, додає правила безпеки для нового контейнера, переналаштовує шляхи. Але контролер стає ще однією критичною системою, а мережа — програмою, яку можна зламати помилкою. На практиці «чистий» SDN зустрічається рідше, ніж його ідеї: оверлеї, якими керує центральна система, декларативна конфігурація фабрики й програмовані комутатори.

Кожен пакет, який ядро отримує з мережевої карти, проходить довгий шлях: виділення sk_buff, обробка netfilter, маршрутизація, сокет. Якщо треба відкинути мільйони пакетів DDoS за секунду або розкидати їх між серверами, вартість цього шляху стає основною.

XDP (eXpress Data Path) дозволяє приєднати програму eBPF до драйвера мережевої карти, і вона бачить пакет ще до виділення sk_buff. Програма повертає вердикт: пропустити (XDP_PASS), відкинути (XDP_DROP), повернути з того самого інтерфейсу (XDP_TX) або переспрямувати на інший (XDP_REDIRECT). Верифікатор доводить, що програма завершиться й не вийде за межі пам’яті (модуль 18 курсу «Операційні системи» розповідає про механізм eBPF докладніше). У датацентрах на XDP будують балансувальники L4 і фільтри DDoS, а плагіни CNI на eBPF вставляють політики й балансування в ядро замість ланцюжків iptables.

Автоматизація й мережа як код

Section titled “Автоматизація й мережа як код”

Фабрику з сотнею leaf і десятком spine вручну не налаштуєш. Мережу описують як дані й код. Бажаний стан записують у файлах (адреси, підмережі, сусіди BGP), шаблони перетворюють його на конфігурації пристроїв, а інструменти (Ansible, Terraform, протоколи NETCONF і gNMI з моделями YANG) застосовують їх. Так мережа отримує те, що давно мають програми: історію змін у Git, рецензування, відтворюваність і відкат.

Надійним цей підхід робить ідемпотентність: повторне застосування тієї самої конфігурації нічого не змінює й зайвого не додає. Так само важить декларативність: ви описуєте, яким має бути результат, а не послідовність команд. Тоді розбіжність між записаним і справжнім станом (drift) можна виявити й усунути автоматично.

Перевіряти зміни на справжній мережі страшно. Тому їх проганяють на моделі: мережеві операційні системи запускають у контейнерах, а топологію описують у файлі. Інструмент containerlab робить саме це: у YAML ви перелічуєте вузли й з’єднання між ними, а він створює контейнери й з’єднує їх парами veth. Це природний наступний крок після просторів імен цього курсу. Принцип той самий, але замість голого Linux у вузлі працює образ справжньої мережевої ОС, з яким можна відтворити фабрику з цього модуля.

Усе нижче запущено в просторах імен на одній машині. Мережа, що розгортається, вміщається в мегабайти.

VXLAN між двома «серверами»

Section titled “VXLAN між двома «серверами»”

Два простори імен (dc-s1, dc-s2) відіграють роль серверів, з’єднаних підкладкою 192.168.100.0/24. У кожному є віртуальна машина (dc-vm1, dc-vm2) за мостом, який з’єднаний тунелем VXLAN з VNI 10.

Terminal window
for n in dc-s1 dc-s2 dc-vm1 dc-vm2; do ip netns add $n; ip -n $n link set lo up; done
ip link add u1 type veth peer name u2
ip link set u1 netns dc-s1; ip link set u2 netns dc-s2
ip -n dc-s1 addr add 192.168.100.1/24 dev u1; ip -n dc-s1 link set u1 up
ip -n dc-s2 addr add 192.168.100.2/24 dev u2; ip -n dc-s2 link set u2 up
for i in 1 2; do
ip -n dc-s$i link add vx10 type vxlan id 10 local 192.168.100.$i \
remote 192.168.100.$((3-i)) dstport 4789
ip -n dc-s$i link add br10 type bridge
ip -n dc-s$i link set vx10 master br10
ip link add vm$i type veth peer name vmp$i
ip link set vm$i netns dc-vm$i; ip link set vmp$i netns dc-s$i
ip -n dc-s$i link set vmp$i master br10
ip -n dc-s$i link set vx10 up; ip -n dc-s$i link set br10 up; ip -n dc-s$i link set vmp$i up
ip -n dc-vm$i addr add 10.10.10.$i/24 dev vm$i; ip -n dc-vm$i link set vm$i up
done
ip netns exec dc-vm1 ping -c2 -W1 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=64 time=0.377 ms
64 bytes from 10.10.10.2: icmp_seq=2 ttl=64 time=0.093 ms

Віртуальні машини в одній підмережі 10.10.10.0/24, але між ними не Ethernet-кабель, а маршрутизована підкладка. ttl=64 не зменшився: для ВМ це один L2-сегмент. Що побачила підкладка:

Terminal window
ip netns exec dc-s2 tcpdump -nn -e -v -c 2 -i u2 udp &
ip netns exec dc-vm1 ping -c1 10.10.10.2
... ethertype IPv4 (0x0800), length 148: (... proto UDP (17), length 134)
192.168.100.1.46964 > 192.168.100.2.4789: VXLAN, flags [I] (0x08), vni 10
a6:0c:84:20:3a:54 > 32:7d:06:23:6e:a8, ethertype IPv4 (0x0800), length 98: (... proto ICMP (1), length 84)
10.10.10.1 > 10.10.10.2: ICMP echo request, id 3146, seq 1, length 64

Кадр довжиною 148 байтів: внутрішній кадр (98 байтів) плюс 50 байтів заголовків. Зовнішній рівень: UDP від 192.168.100.1 до 192.168.100.2 на порт 4789 із vni 10. Усередині видно внутрішній Ethernet і ICMP між адресами ВМ. Порт відправника 46964 обчислено за хешем внутрішнього потоку, про який ішлося вище.

Обіцяну проблему MTU теж легко побачити. Підкладка має MTU 1500, а внутрішній інтерфейс ВМ теж, тож повний кадр із 50 байтами накладних витрат не пройде:

Terminal window
ip netns exec dc-vm1 ping -c1 -W1 -M do -s 1472 10.10.10.2
ip netns exec dc-vm1 ping -c1 -W1 -M do -s 1422 10.10.10.2
PING 10.10.10.2 (10.10.10.2) 1472(1500) bytes of data.
From 10.10.10.2 icmp_seq=1 Frag needed and DF set (mtu = 1450)
PING 10.10.10.2 (10.10.10.2) 1422(1450) bytes of data.
1430 bytes from 10.10.10.2: icmp_seq=1 ttl=64 time=0.199 ms

Пакет 1500 байтів не проходить, 1450 проходить: сума цього пакета й накладних витрат (50 байтів) якраз дорівнює MTU підкладки. Відповідь «Frag needed» Linux повертає сам тунельний інтерфейс, від імені віддаленого адресата, щоб TCP зміг підлаштувати розмір сегмента.

Таблиця пересилання моста й тунелю показує, що «MAC → VTEP» вивчено:

Terminal window
ip netns exec dc-s1 bridge fdb show | grep vx10
32:7d:06:23:6e:a8 dev vx10 master br10
32:7d:06:23:6e:a8 dev vx10 dst 192.168.100.2 self
00:00:00:00:00:00 dev vx10 dst 192.168.100.2 self permanent

Рядок із нульовою MAC-адресою — запис за замовчуванням: усе, що не вивчено (у тому числі широкомовні кадри), відправляється на цей VTEP. Рядок із конкретною MAC вивчено з першого ж кадру, який прийшов.

Прибрати:

Terminal window
for n in dc-s1 dc-s2 dc-vm1 dc-vm2; do ip netns del $n; done

Мінімальна фабрика: два leaf, два spine. Між dc-lf і dc-lf2 два рівноцінні шляхи, по одному через кожен spine. Маршрут на dc-lf до адреси 10.9.9.9 (петлі на dc-lf2) має два nexthop.

Terminal window
ip -n dc-lf route show 10.9.9.9
10.9.9.9
nexthop via 10.0.1.2 dev a1 weight 1
nexthop via 10.0.2.2 dev a2 weight 1

Надсилаємо сто UDP-потоків з різними вихідними портами (40000–40099) і дивимося лічильники прийнятих пакетів на інтерфейсах spine. Спершу з налаштуванням за замовчуванням:

Terminal window
ip netns exec dc-lf sysctl net.ipv4.fib_multipath_hash_policy
net.ipv4.fib_multipath_hash_policy = 0

За політики 0 хеш рахується лише за адресами відправника й отримувача. Після ста потоків на spine 1 додалося рівно сто пакетів, на spine 2 нуль: усе пішло одним шляхом. Перемикаємо на 5-кортеж:

Terminal window
ip netns exec dc-lf sysctl -qw net.ipv4.fib_multipath_hash_policy=1

Тепер ті самі сто потоків розподілилися приблизно порівну: на spine 1 додалося 57 пакетів, на spine 2 — 44. Рівно порівну хеш не розкладає, бо він статистичний; звідси й слабкість ECMP на великих потоках.

Docker без Docker: міст, veth і NAT

Section titled “Docker без Docker: міст, veth і NAT”

Кроки, які робить Docker, відтворюються в чотирьох просторах імен: господар (dc-host), два «контейнери» і зовнішній світ (dc-out).

Terminal window
ip -n dc-host link add br0 type bridge
ip -n dc-host addr add 172.17.0.1/16 dev br0; ip -n dc-host link set br0 up
for i in 1 2; do
ip link add hv$i type veth peer name cv$i
ip link set hv$i netns dc-host; ip link set cv$i netns dc-c$i
ip -n dc-host link set hv$i master br0 up
ip -n dc-c$i addr add 172.17.0.$((i+1))/16 dev cv$i; ip -n dc-c$i link set cv$i up
ip -n dc-c$i route add default via 172.17.0.1
done
ip netns exec dc-host sysctl -qw net.ipv4.ip_forward=1
# зовнішня ділянка ho (203.0.113.1) <-> oh (203.0.113.99) налаштована так само
ip netns exec dc-host nft -f - <<'E'
table ip nat {
chain post { type nat hook postrouting priority 100;
ip saddr 172.17.0.0/16 oifname "ho" masquerade; }
chain pre { type nat hook prerouting priority -100;
iifname "ho" tcp dport 8080 dnat to 172.17.0.2:80; }
}
E

Контейнери бачать одне одного через міст, а назовні виходять із підміненою адресою. На стороні dc-out зазирнемо в tcpdump, коли dc-c1 пінгує зовнішній світ:

IP 203.0.113.1 > 203.0.113.99: ICMP echo request, id 3988, seq 1, length 64
IP 203.0.113.99 > 203.0.113.1: ICMP echo reply, id 3988, seq 1, length 64

Зовні видно адресу господаря 203.0.113.1, а не 172.17.0.2. Таблицю відстеження з’єднань бачить conntrack -L на господарі:

icmp 1 29 src=172.17.0.2 dst=203.0.113.99 type=8 code=0 id=3988 src=203.0.113.99 dst=203.0.113.1 type=0 code=0 id=3988 ...

Праворуч зворотний напрям: відповідь повертається на 203.0.113.1, і ядро за записом повертає її на 172.17.0.2. Опублікований порт перевіряємо зі стороннього простору:

Terminal window
ip netns exec dc-c1 python3 -m http.server 80 --bind 172.17.0.2 &
ip netns exec dc-out curl -s -o /dev/null -w '%{http_code}\n' http://203.0.113.1:8080/
200

Запит на порт 8080 господаря потрапив до контейнера на порт 80: це й є -p 8080:80 у Docker. Контейнер бачить запит від зовнішньої адреси, а не від господаря, бо DNAT міняє лише адресу призначення. Поверніть усе на місце: зупиніть http.server і видаліть простори імен (dc-host, dc-c1, dc-c2, dc-out).

Для macvlan достатньо одного рядка на господарі: ip link add mv0 link eth0 type macvlan mode bridge. Створення такого інтерфейсу поверх veth у просторі імен працює, а от коректні адреси й доступність ззовні залежать від вашої фізичної мережі.

Типові помилки розуміння

Section titled “Типові помилки розуміння”

Spine-leaf просто швидший за дерево. Швидкість каналів може бути та сама. Різниця в тому, що між будь-якою парою leaf є однакова кількість рівноцінних шляхів і мережа росте додаванням однакових блоків. Швидкий канал у дереві однаково впирається в перепідписку.

ECMP розподіляє навантаження порівну. Він розподіляє потоки, а не байти. Два великі потоки можуть потрапити на один шлях, а інший шлях простоюватиме. Різні політики хешування дають різні результати, як видно з досліду: за хеша лише за адресами всі потоки між двома хостами йшли одним шляхом.

VXLAN шифрує трафік. Інкапсуляція нічого не шифрує. Внутрішній кадр у підкладці лежить у відкритому вигляді, як видно в tcpdump. Шифрування додають окремо, наприклад IPsec або WireGuard.

MTU у тунелі лишається 1500. Оверлей додає 50 байтів, отже внутрішній MTU мусить бути меншим або MTU підкладки більшим. Інакше великі пакети губляться, а дрібні працюють, і це важко діагностувати.

Контейнер із портом 8080:80 слухає на 8080. Всередині контейнера слухається порт 80. Порт 8080 господаря відображається на нього правилом DNAT, і в самому контейнері 8080 жодного сокета немає.

Service у Kubernetes — це процес, який проксіює трафік. Адреса Service віртуальна: на жодному інтерфейсі її немає, а пакети до неї переписуються правилами ядра на адресу одного з подів. Якщо пакет не доходить, то перевіряють правила iptables або IPVS і маршрути, а не процес.

SDN означає, що мережа більше не потребує протоколів маршрутизації. Часто вона їх лишає, а контролер керує поверх. Фабрики spine-leaf найчастіше працюють на BGP без централізованого контролера взагалі.

Перевір себе

1. Чому в датацентрі класичне триярусне дерево гірше за spine-leaf?
2. Чому ECMP обирає шлях за хешем потоку, а не розкидає пакети по черзі?
3. Політика хешування за замовчуванням використовує лише адреси відправника й отримувача. Що станеться зі ста зʼєднаннями між двома серверами?
4. Навіщо VXLAN загортає кадри саме в UDP, а не просто в IP?
5. Яка загальна кількість байтів накладних витрат VXLAN поверх IPv4, і що з цього випливає?
6. Контейнер у Docker опублікував порт `-p 8080:80`. Де буде слухати застосунок і що робить порт 8080?
7. Що таке адреса Service у Kubernetes (ClusterIP)?
8. Що дає XDP порівняно з обробкою пакета у звичайному мережевому стеку?

Окремої лабораторної для цього модуля немає. Практика, яка спирається на нього, — у вправах розділу «Як це насправді в Linux»: VXLAN між двома просторами імен, ECMP із двома spine й контейнерна мережа з нуля. Міст і veth, потрібні для цього, ви вже будували в B1, NAT — у B2.