Files
ayurishchevandClaude Opus 5 fa389f119b Шаг 5: динамическое изучение MAC/ARP для бэкендов
MAC каждого члена пула больше не статическая константа в topology.env,
а резолвится демоном hcd через обычный ARP ядра — в реальном
окружении MAC бэкенда заранее не известен (сервер ещё не подключён,
NIC может замениться), топология не описывается статически, в отличие
от стенда.

hcif-порты (единственные адреса узла в ядре) уже были настоящими
L3-интерфейсами в тех же сегментах, что и бэкенды — единственное, что
мешало обычному ARP, это permanent-записи ip neigh в entrypoint.sh.
Убрав их и добавив hc/neigh.go (читает ip -json neigh show, точечно
заливает бандл в таблицу 21 на том же тикере, что и health-пробы),
получили резолвер без нового OpenFlow-контроллера.

Таблица 21 стала единственным источником MAC для обоих путей —
маршрутизируемого и коммутируемого (шаг 3): таблица 12 больше не
дублирует MAC инлайново, ct(commit) ведёт сразу в таблицу 21.

Исправлен попутно найденный баг: после apply.sh (replace-flows)
таблица 21 не восстанавливалась, поскольку syncNeighbors сравнивал
MAC с памятью демона, а не с датапасом. Добавлен force-режим по
аналогии с Agent.apply(), плюс регрессионная проверка в verify.sh.

Проверено на живом стенде: MAC всех четырёх членов резолвлен и
совпадает с реальными интерфейсами; смена MAC "железа" обнаружена и
применена без вмешательства за счёт штатного старения ARP ядра.
Регрессия: 94 из 94 проверок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 12:33:48 +03:00

8.0 KiB
Raw Permalink Blame History

Шаг 1 — итоги: прототип окружения hpnn_v2

Дата: 2026-08-16 Статус: выполнено, стенд поднят и проверен (30 из 30 проверок) План: STEP1_IMPLEMENTATION_PLAN.md

Что сделано

Собрано контейнерное окружение из трёх сетей и трёх контейнеров, на котором работают обе подсистемы — маршрутизация и балансировка нагрузки. Всё форвардинг-решение принимается в OpenFlow: сетевой стек ядра контейнера транзитный трафик не обрабатывает.

  • Контейнер 1 (hpnn-lb) — Open vSwitch с kernel datapath в собственном netns, мост br-lb с тремя портами. Пайплайн из 42 правил и группы балансировки рендерится скриптом из topology.env и заливается атомарным бандлом.
  • Контейнеры 2 и 3 (hpnn-be1, hpnn-be2) — идентичный web-сервис на Go, каждый в своём приватном сегменте; отдаёт имя хоста, дату, время и адрес клиента.
  • Сети — публичный сегмент через macvlan поверх enp3s0 (настоящий L2 клиентской сети) и два приватных bridge-сегмента.
  • Инструментарий — make up/down/flows/verify, lbctl для осмотра датапаса, scripts/host-prereq.sh для подготовки хоста.

Результаты проверок

Проверка Результат
Kernel datapath OVS в netns контейнера system@ovs-system, поддержка recirculation, ct_state_nat, ct_zone
macvlan-интерфейс как порт OVS работает, pub0 — ofport 1
selection_method=hash, fields(ip_src,tcp_src) принят OVS 3.1
Балансировка 20 запросов с клиента be1/be2 ≈ 10/10, счётчики обоих бакетов растут
Сохранение IP клиента бэкенд видит 192.168.5.13 — DNAT без SNAT подтверждён
ARP- и ICMP-респондеры OVS ping до узла и до VIP проходит
Маршрутизация между приватными сегментами be1 → be2 через таблицы 20/21
Профиль A (§3.2 дизайна v1) egress бэкенда наружу работает и на порту p2 не появляется
ofproto/trace пути клиент → VIP DNAT, dec_ttl, перепись MAC, выход в порт 2

Отличия от плана

  1. Публичная сеть объявлена с подсетью 192.168.55.0/24, а не 192.168.5.0/24. Docker отказывается регистрировать пул, пересекающийся с уже существующей macvlan-сетью соседнего стенда router-functest (Pool overlaps with other one on this address space). Объявленная подсеть нужна только Docker IPAM: адреса узла и VIP живут исключительно в правилах OpenFlow, а macvlan включён в enp3s0 и работает на реальном L2.

  2. Обратный путь занял таблицы 15 и 16 вместо 30 и 31. goto_table в OpenFlow разрешает переход только вперёд, поэтому таблицы обратного пути обязаны стоять до общей маршрутизации (20).

  3. DNAT вынесен из бакетов группы в отдельную таблицу 11. Бакет только кладёт номер члена пула в reg2 и делает resubmit. Так вся логика трансляции лежит в одном месте и не зависит от того, какие действия допустимы внутри бакета.

  4. Обучение MAC клиентов сужено до трафика, адресованного стенду (VIP, адрес узла, приватные сегменты). В первой редакции learn срабатывал на любой IP-пакет с macvlan-порта и заносил в таблицу 21 весь широковещательный шум сегмента — DHCP, mDNS, SSDP всех соседей по LAN.

  5. Проверка профиля A добавлена в verify.sh — в плане она была только ручной.

Известные ограничения

Осознанные упрощения шага 1, каждое — задача следующих шагов:

  • Датапас stateful. Обратную трансляцию выполняет ct(nat), а не stateless un-DNAT из §2 дизайна v1. Следствие: прямой и обратный трафик сессии обязаны проходить через один узел, то есть Active/Active ECMP в такой схеме работать не будет.
  • ICMP-ошибки не генерируются. У чисто-OpenFlow узла нет стека, который бы формировал TTL exceeded и fragmentation needed; PMTUD через стенд не работает, traceroute не показывает узел.
  • Фрагменты IP не обрабатываются — правила матчат L4-заголовок, которого во втором и последующих фрагментах нет.
  • Один узел LB. Ни BGP, ни BFD, ни ECMP-набора: FRR в стенде нет, место под него (internal-порты с IP) на шаге 1 не занято, поскольку маршрутизация выполняется целиком в OpenFlow.
  • Нет health-check. Бэкенды считаются живыми всегда; отказ бэкенда не выводит его из группы. У приложения есть /healthz как задел.
  • Пул и листенер статические — заданы в topology.env, control plane и OVSDB-схемы из §6 дизайна v1 нет.
  • MAC бэкендов прописаны статически. ARP-резолвер next-hop отсутствует — снято на шаге 5: hcd резолвит MAC членов пула через обычный ARP ядра, см. STEP5_SUMMARY.md.
  • Только TCP и только IPv4. UDP- и L3-листенеров нет.

Задел на шаг 2

  1. Заменить ct(nat) на stateless DNAT/un-DNAT с таблицей слотов и multipath(symmetric_l4) — это спайк S0 из §12 дизайна v1 и предпосылка для Active/Active.
  2. Проверить детерминизм выбора бэкенда: один и тот же 5-tuple должен давать один и тот же слот на разных узлах и между перезапусками (ofproto/trace по корпусу синтетических кортежей).
  3. Подсистема health-check: пробы с уникального адреса узла, вывод члена из пула, перерасчёт таблицы слотов.
  4. Control plane: модель Load_Balancer → Listener → Pool → Member в OVSDB вместо topology.env, агент-рендерер пайплайна.
  5. ICMP-транслятор в userspace (packet-in) — для PMTUD и корректных ICMP-ошибок от имени VIP.