Files
hpnn-proto/docs/STEP1_SUMMARY.md
T

104 lines
8.0 KiB
Markdown
Raw Normal View History

# Шаг 1 — итоги: прототип окружения hpnn_v2
**Дата:** 2026-08-16
**Статус:** выполнено, стенд поднят и проверен (30 из 30 проверок)
**План:** [STEP1_IMPLEMENTATION_PLAN.md](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](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.