103 lines
7.8 KiB
Markdown
103 lines
7.8 KiB
Markdown
# Шаг 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 отсутствует;
|
|||
|
|
MAC зафиксированы в `docker-compose.yml`.
|
|||
|
|
- **Только 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.
|