Files
hpnn-proto/README.md
T

16 KiB
Raw Blame History

hpnn_v2 — стенд OVS-маршрутизатора и балансировщика нагрузки

Контейнерный прототип основы сервиса: один узел на Open vSwitch, который одновременно выполняет две функции — маршрутизацию между сегментами и балансировку нагрузки на четыре бэкенда. Всё форвардинг-решение принимается в OpenFlow: сетевой стек ядра контейнера транзитный трафик не обрабатывает. Состав пула ведёт подсистема health-check: мёртвые бэкенды выводятся из балансировки, восстановившиеся возвращаются.

Развивает дизайн-концепцию ../hpnn_v1/docs/design.md.

Шаг Содержание План Итоги
1 Окружение, маршрутизация, балансировка план итоги
2 Health-check и таблица слотов план итоги
— Расширение пула до четырёх бэкендов план итоги

Топология

                 клиент из 192.168.5.0/24
                          │
                    [ enp3s0, macvlan ]              сеть 1 — публичный сегмент
                          │ pub0 · ofport 1
        ┌─────────────────┴─────────────────────┐
        │  hpnn-lb  (Open vSwitch, br-lb)       │   узел 192.168.5.20
        │  маршрутизатор + балансировщик        │   VIP  192.168.5.21
        │  hcd: health-check + таблица слотов   │
        └──┬──────────────────────────────┬─────┘
     p2 ·2 │ 10.20.0.1           10.30.0.1 │ p3 ·3
  hcif-p2 ·4 10.20.0.253      10.30.0.253  │ hcif-p3 ·5   (источники проб)
   сеть 2  │ 10.20.0.0/24         сеть 3   │ 10.30.0.0/24
  hpnn-be1 │ 10.20.0.2           hpnn-be2 │ 10.30.0.2
  hpnn-be3 │ 10.20.0.3           hpnn-be4 │ 10.30.0.3
Контейнер Роль
hpnn-lb OVS, все три сети, маршрутизация и балансировка
hpnn-be1, hpnn-be3 web-сервис на Go в приватном сегменте 2
hpnn-be2, hpnn-be4 тот же сервис в приватном сегменте 3

Адресный план

Назначение Адрес
Клиентская сеть 192.168.5.0/24
Адрес узла в публичном сегменте 192.168.5.20
VIP балансировщика 192.168.5.21:80
Шлюз OVS в сети 2 / бэкенды be1, be3 10.20.0.1 / 10.20.0.2:8080, 10.20.0.3:8080
Шлюз OVS в сети 3 / бэкенды be2, be4 10.30.0.1 / 10.30.0.2:8080, 10.30.0.3:8080
Источники health-проб (internal-порты OVS) 10.20.0.253, 10.30.0.253
Шлюзы Docker (собственный egress бэкендов) 10.20.0.254, 10.30.0.254

Адреса 192.168.5.20 и 192.168.5.21 существуют только в правилах OpenFlow — на интерфейсах они не настроены. ARP-респондер OVS отвечает на них MAC-адресом порта pub0. Исключение — адреса .253: это единственные адреса, которые узел держит в ядре, потому что с них уходят health-пробы. Единственный источник правды по адресам — lb/topology.env.

Запуск

make up        # modprobe openvswitch + сборка образов + запуск
make verify    # проверки
make down      # остановка

Проверка с самого хоста требует macvlan-shim: интерфейс macvlan контейнера и физический интерфейс хоста по устройству macvlan не видят друг друга. Клиенту из 192.168.5.0/24 никакой shim не нужен.

make shim      # поднять shim 192.168.5.13 с маршрутами на .20 и .21
make shim-down

Полный сценарий ручной проверки стенда — docs/MANUAL_TEST_PLAN.md (что выполнять на клиенте, что на хосте, чего ожидать на каждом шаге и что означает отклонение). Автоматический аналог — make verify.

Проверка с клиента

ping 192.168.5.20                 # адрес узла — отвечает ICMP-респондер OVS
ping 192.168.5.21                 # VIP

for i in $(seq 20); do curl -s http://192.168.5.21/; done
backend=be1    time=2026-08-16 21:35:02 MSK client=192.168.5.13:47734    served=10.20.0.2:8080   req=24
backend=be3    time=2026-08-16 21:35:02 MSK client=192.168.5.13:47740    served=10.20.0.3:8080   req=11
backend=be2    time=2026-08-16 21:35:02 MSK client=192.168.5.13:47742    served=10.30.0.2:8080   req=30
backend=be4    time=2026-08-16 21:35:02 MSK client=192.168.5.13:47746    served=10.30.0.3:8080   req=17

Что здесь видно:

  • балансировка — ответы приходят от всех четырёх бэкендов примерно поровну;
  • сохранение IP клиента — в поле client стоит реальный адрес из 192.168.5.0/24, а не адрес балансировщика: выполняется только DNAT, SNAT нигде не делается;
  • ключ хэша — (ip_src, tcp_src), поэтому разные соединения одного клиента расходятся по бэкендам, а пакеты одного соединения — нет.

Подсистему маршрутизации видно отдельно от балансировки:

# на клиенте: маршруты в приватные сегменты через узел
ip route add 10.20.0.0/24 via 192.168.5.20
ip route add 10.30.0.0/24 via 192.168.5.20
curl http://10.20.0.2:8080/       # напрямую в бэкенд, мимо VIP

Эти маршруты нужно прописывать на клиентской машине, а не на хосте стенда: на самом хосте они перекроют connected-маршруты docker-бриджей hpnn-p2/hpnn-p3 и оборвут бэкендам выход наружу.

Транзит между приватными сегментами проверяется изнутри:

docker compose exec be1 curl -s http://10.30.0.2:8080/    # be1 -> be2 через OVS

Подсистема health-check

Демон hcd внутри контейнера-балансировщика проверяет живость членов пула и отражает их состав в датапасе. Пробы уходят с уникального адреса узла в сегменте бэкенда (10.20.0.253, 10.30.0.253), а не с VIP — иначе ответ вернулся бы не тому, кто проверял.

make health          # состояние членов пула
make slots           # раскладка слотов и счётчики пакетов по членам
make drain M=be1     # вывести член из балансировки (пробы продолжаются)
make enable M=be1    # вернуть член в балансировку
make metrics         # метрики Prometheus
пул 1: слотов 1024, проба http каждые 2s (rise=2 fall=3)
дайджест раскладки: 0a16713c5a9eb94d

ЧЛЕН   АДРЕС             СОСТОЯНИЕ  ADMIN     В ПУЛЕ   СЛОТОВ    ПРОБ  НЕУДАЧ    МС  ИСТОЧНИК ПРОБ
be1    10.20.0.2:8080    up         enabled   да          256     120       0     1  10.20.0.253
be2    10.30.0.2:8080    up         enabled   да          256     120       0     2  10.30.0.253
be3    10.20.0.3:8080    up         enabled   да          256     120       0     2  10.20.0.253
be4    10.30.0.3:8080    up         enabled   да          256     120       0     2  10.30.0.253

Проверить руками:

docker compose pause be1     # член уходит в down за fall × interval = 6 с
curl http://192.168.5.21/    # трафик обслуживают три оставшихся члена
docker compose unpause be1   # возвращается за rise × interval = 4 с

Параметры проб (тип, интервал, таймаут, rise/fall) — в lb/topology.env, раздел HC_*.

Таблица слотов

Балансировка выполняется не группой OVS, а таблицей из 1024 слотов (таблица 11). Номер слота даёт multipath(symmetric_l4) от заголовков пакета, слот указывает на члена пула. Раскладку слотов по членам считает hcd по алгоритму Maglev (§5 дизайна v1) и заливает атомарным бандлом.

Свойства, которые это даёт:

  • детерминированность — одинаковый состав пула даёт одинаковую раскладку на любом узле и между перезапусками; контролируется дайджестом SHA-256;
  • минимальное возмущение — при выбытии члена переезжают его слоты, а у оставшихся меняются единицы: на этом стенде вывод одного из четырёх членов сдвигает 256 его слотов и лишь 8 чужих (0,8 %); проверяется в make verify;
  • fail-close — когда живых членов нет, таблица пустеет и трафик на VIP отбрасывается со счётчиком, а не уходит на заведомо мёртвый бэкенд;
  • бесплатная per-member статистика из счётчиков правил слотов.

Осмотр датапаса

make ports                        # порты моста
make conns                        # таблица соединений ct
make trace SRC=192.168.5.13       # ofproto/trace сессии клиент -> VIP
make flows                        # перечитать pipeline.sh и перезалить правила
docker compose exec lb-router lbctl flows 21    # правила конкретной таблицы
docker compose exec lb-router lbctl status      # состояние пула в JSON

Как устроен пайплайн

Все правила рендерит lb/pipeline.sh из topology.env и применяет атомарным бандлом (ovs-ofctl --bundle replace-flows), поэтому обновление не создаёт окна с полузалитым пайплайном.

Таблица Назначение
0 Классификация по входному порту; обучение MAC клиентов для обратного пути
5 ARP-респондер для адресов узла, VIP и источников проб
6 ICMP echo-респондер для адресов узла и VIP
10 Листенеры: VIP:80 → multipath кладёт номер слота в reg1; прочий IP → маршрутизация
11 Таблица слотов: reg1 → reg2 (член пула). Ведёт демон hcd
12 DNAT выбранным членом пула (ct(commit, nat))
15, 16 Обратный путь: ct(nat) снимает DNAT, источник снова становится VIP
20 Маршрутизация: приоритет = длина префикса, dec_ttl, MAC источника
21 Adjacency: MAC next-hop и выходной порт

Регистры: reg1 — номер слота, reg2 — идентификатор члена пула.

Нумерация таблиц не произвольна: goto_table разрешает переход только вперёд, поэтому обратный путь (15/16) стоит до общей маршрутизации (20).

Чего нет у чисто-OpenFlow узла

Ядро контейнера в форвардинге не участвует, поэтому всё, что обычно делает сетевой стек, реализовано правилами: ARP-ответы (таблица 5), ответы на ping (таблица 6), поиск MAC клиента (действие learn в таблице 0 вместо ARP-резолвера). ICMP-ошибки (TTL exceeded, fragmentation needed) на шаге 1 не генерируются — см. ограничения в docs/STEP1_SUMMARY.md.

Особенности окружения

  • Kernel datapath OVS namespace-aware, поэтому контейнер держит собственный ovs-system — это настоящий датапас ядра, а не эмуляция. Нужны privileged: true, проброс /lib/modules и загруженный на хосте модуль openvswitch (make prereq, модуль прописывается в автозагрузку).
  • Публичная сеть объявлена с подсетью 192.168.55.0/24 — это фиктивный пул для Docker IPAM. Реальную 192.168.5.0/24 объявить нельзя: её уже занимает macvlan-сеть соседнего стенда router-functest, и Docker отказывается регистрировать пересекающийся пул. Для L2 это безразлично — macvlan включён в enp3s0, а адреса стенда живут в OpenFlow.
  • Шлюзы приватных сетей смещены на .254, чтобы адрес .1 занял OVS-роутер и не конфликтовал с host-бриджем Docker.
  • Профиль A (§3.2 дизайна v1): у бэкендов через балансировщик маршрутизируются только клиентские префиксы и соседний приватный сегмент, а default остаётся на шлюзе Docker — собственный исходящий трафик бэкендов идёт мимо балансировщика.

Структура

docker-compose.yml     три сети и три контейнера
Makefile               up / down / flows / verify / health / диагностика
lb/topology.env        адреса, MAC, номера портов, параметры проб и слотов
lb/pipeline.sh         генератор OpenFlow-правил
lb/apply.sh            атомарная заливка правил и запрос слотов у hcd
lb/entrypoint.sh       запуск OVS, сборка моста, порты проб, запуск hcd
lb/hc-config.sh        конфигурация health-check из topology.env
lb/lbctl.sh            осмотр датапаса (ports / flows / health / slots / trace)
hc/maglev.go           раскладка слотов, дайджест, рендер бандла
hc/main.go             пробер, состояния членов пула, применение, API
backend/main.go        web-сервис: имя хоста, дата и время, адрес клиента
scripts/host-prereq.sh модуль ядра и macvlan-shim
scripts/verify.sh      проверки стенда