diff --git a/README.md b/README.md index 36eef35..c6a06eb 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,8 @@ | — | Разделение документации по потокам данных | [план](docs/CHANGE_SPLIT_DATAFLOW_DOCS_PLAN.md) | [итоги](docs/CHANGE_SPLIT_DATAFLOW_DOCS_SUMMARY.md) | Как устроена раскладка слотов — [docs/MAGLEV_SLOT_TABLE.md](docs/MAGLEV_SLOT_TABLE.md) -(справка по `hc/maglev.go` для разработки). +(справка по `hc/maglev.go` для разработки). Что нужно изменить для перевода ноды +на OVS-DPDK в ПРОД — [docs/OVS_DPDK_MIGRATION.md](docs/OVS_DPDK_MIGRATION.md). Потоки данных описаны отдельно для каждого вида балансировки: [публичный трафик](docs/PUBLIC_LB_DATAFLOW.md) — клиент из клиентской сети на diff --git a/docs/OVS_DPDK_MIGRATION.md b/docs/OVS_DPDK_MIGRATION.md new file mode 100644 index 0000000..7aecffc --- /dev/null +++ b/docs/OVS_DPDK_MIGRATION.md @@ -0,0 +1,246 @@ +# Переход на OVS-DPDK: перечень необходимых изменений + +**Дата:** 2026-08-17 +**Зачем:** в ПРОД-окружении нода HPNN (маршрутизатор и балансировщик) работает +на выделенном физическом сервере с OVS-DPDK. Документ фиксирует, что для этого +нужно изменить, что переносится как есть и где ожидаются потери. +**Основание:** фактическое состояние стенда на 2026-08-17 (шаг 3). + +## 1. Исходное состояние — что измерено + +| Параметр | Значение в стенде | +|---|---| +| Сборка OVS | 3.1.0, `dpdk_initialized=false`, `dpdk_version=none` | +| Датапас | `system@ovs-system` — **kernel datapath** | +| `datapath_type` моста | пусто (то есть `system`) | +| Порты моста | 11: `pub0` (macvlan), `p2`/`p3` (veth в docker-бридж), `hcif-p2`/`hcif-p3` (internal), 6 veth к контейнерам ВМ | +| Правил в пайплайне | 105 + 1024 в таблице слотов | +| Действия пайплайна | `load` 91, `goto_table` 52, `move` 36, **`learn` 36**, **`ct` 16**, `IN_PORT` 14, `mod_dl_src` 11, `mod_dl_dst` 10, **`multipath` 4**, `dec_ttl` 3 | +| Состояние ct | ~250 записей, 19 потоков датапаса с `recirc` | + +Вывод из инвентаризации: **вся форвардинг-логика DPDK-совместима** — все +перечисленные действия поддерживает userspace-датапас (`netdev`). Переделке +подлежит не пайплайн, а способ подключения портов, размещение узла и +инструментарий. Отдельный разговор — производительность: `ct`, `learn` и +`multipath` определяют её потолок (раздел 6). + +## 2. Что переносится без изменений + +- **`lb/pipeline.sh` целиком** — таблицы 0/1/5/6/10/11/12/15/16/20/21/24/25, + включая ARP- и ICMP-респондеры (`move`/`load`/`IN_PORT`), `dec_ttl`, + `multipath(symmetric_l4)`, `ct(commit,nat)` и `ct(nat)`, действие `learn`, + рассылку по списку портов и `fail_mode=secure`. +- **Логика Maglev** (`hc/maglev.go`) и формат бандла `ovs-ofctl` — датапаса не + касаются вовсе. +- **Детерминизм выбора члена.** `multipath` считается в `ofproto`, а не в + датапасе, поэтому номер слота для одного 5-tuple не зависит от типа датапаса — + раскладка и дайджест при переходе не меняются. **Подлежит подтверждению + спайком S-DPDK-1** (раздел 8). +- **Адресная модель**: VIP в правилах, шлюзы сегментов, уникальные адреса + источника health-проб, `PRIV_LISTENERS`. + +## 3. Изменения по слоям + +### 3.1. Хост и ОС + +| Что | Требование | +|---|---| +| Hugepages | 1 GB страницы, объём под `dpdk-socket-mem` + vhost-user; `default_hugepagesz=1G hugepagesz=1G hugepages=N` в cmdline | +| Изоляция ядер | `isolcpus`, `nohz_full`, `rcu_nocbs` для ядер PMD; PMD-ядра не отдаются планировщику | +| Драйвер NIC | `vfio-pci` (с `iommu=pt intel_iommu=on`), NIC отвязана от ядра | +| NUMA | PMD-ядра, hugepages и NIC — на одном сокете; проверять `numactl -H` и `/sys/class/net/*/device/numa_node` | +| Пакеты | OVS, собранный с DPDK (`--with-dpdk`), версии OVS и DPDK согласованы | +| Модуль `openvswitch` | **не нужен** — `modprobe openvswitch` из `scripts/host-prereq.sh` и `lb/entrypoint.sh` уходит | + +### 3.2. Конфигурация OVS + +```bash +ovs-vsctl set Open_vSwitch . other_config:dpdk-init=true +ovs-vsctl set Open_vSwitch . other_config:dpdk-socket-mem="2048,0" +ovs-vsctl set Open_vSwitch . other_config:dpdk-lcore-mask=0x1 +ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x3c # ядра PMD +ovs-vsctl set Open_vSwitch . other_config:userspace-tso-enable=true +ovs-vsctl add-br br-lb -- set bridge br-lb datapath_type=netdev \ + -- set bridge br-lb fail_mode=secure \ + -- set bridge br-lb protocols=OpenFlow13,OpenFlow15 +``` + +`datapath_type=netdev` на мосту — единственное изменение в самой сборке моста; +`fail_mode=secure` и набор протоколов сохраняются. + +### 3.3. Порты — главная переделка + +| Сейчас | В ПРОД на DPDK | +|---|---| +| `pub0` — macvlan поверх `enp3s0`, перенесённый в netns контейнера | `type=dpdk`, `options:dpdk-devargs=0000:xx:00.0`; при необходимости `dpdkbond` для LAG | +| `p2`/`p3` — veth в docker-бридж (аплинк сегмента к шлюзу Docker) | физический порт или VLAN-подынтерфейс к фабрике сегмента; шлюза Docker в ПРОД нет | +| Порты ВМ (`be1`…`cli3`) — veth, создаваемые `scripts/attach-segment.sh` через `nsenter` | `type=dpdkvhostuserclient`, `options:vhost-server-path=/var/run/openvswitch/vhostuserN`; порты создаёт гипервизор при старте ВМ | +| `hcif-p2`/`hcif-p3` — `type=internal` с IP в ядре | `type=internal` в netdev-мосте (реализуется как tap) либо отдельная management-NIC в ядре — см. 3.5 | + +Предпосылка шага 3 «порт ВМ — порт OVS» на DPDK выполняется **естественно**: +vhost-user — это и есть порт моста. `scripts/attach-segment.sh` и +`make attach` в ПРОД не нужны — вместе с `network_mode: none` и всей +docker-обвязкой. + +Дополнительно потребуется задать: `mtu_request` (jumbo, если фабрика позволяет), +`options:n_rxq` и `other_config:pmd-rxq-affinity` для распределения очередей по +PMD, `options:n_rxq_desc`/`n_txq_desc`. + +### 3.4. Размещение узла: контейнер → сервер + +- `docker-compose.yml`, `lb/Dockerfile`, `privileged: true`, + `/lib/modules:ro` — уходят. Узел ставится на железо. +- `lb/entrypoint.sh` разделяется на: конфигурацию OVS (systemd-юнит + `ovs-vswitchd` с DPDK-опциями), сборку моста и портов (штатный + `ovsdb`-конфиг, а не скрипт при старте) и запуск `hcd` как systemd-сервиса. +- `lb/topology.env` остаётся источником правды, но описывает физические порты, + PCI-адреса и пути vhost-user вместо MAC docker-интерфейсов. Опознание порта + по MAC (`find_dev_by_mac`) заменяется на PCI-адрес и имя vhost-сокета. +- `lb/apply.sh`, `lb/lbctl.sh` работают как есть — они общаются с OVS через + `ovs-vsctl`/`ovs-ofctl` локально; из них убираются обёртки + `docker compose exec`. + +### 3.5. Health-check (`hc/`) + +Демон менять почти не нужно, но одно место требует решения: пробы уходят из +**сетевого стека ядра** (`net.Dialer.LocalAddr` на адресе internal-порта). В +DPDK-мосте такой порт — tap-устройство, и трафик проб идёт медленным путём через +ядро. Для проб (единицы пакетов в секунду на члена) это допустимо, но требует +проверки задержки и того, что tap не привязан к PMD-ядру. + +Варианты, которые надо сравнить на спайке: + +1. **`type=internal` в netdev-мосте** — минимум изменений, `hcd` остаётся как + есть. Предпочтительный вариант по умолчанию. +2. **Отдельная management-NIC в ядре** — пробы идут в обход датапаса; требует + маршрутной достижимости сегментов бэкендов, то есть меняет топологию. +3. **Пробер поверх DPDK** (собственный TCP-стек или `AF_XDP`) — снимает + зависимость от ядра, но это отдельная разработка; вне рамок миграции. + +Остальное в `hc/` переносится без изменений: `exec ovs-ofctl bundle`, HTTP API, +`/metrics`, Maglev, гистерезис. + +### 3.6. Инструментарий и проверки + +| Файл | Что делать | +|---|---| +| `scripts/host-prereq.sh` | переписать: вместо `modprobe openvswitch` и macvlan-shim — hugepages, `vfio-pci`, проверка изоляции ядер и NUMA | +| `scripts/attach-segment.sh` | удалить: порты создаёт гипервизор | +| `scripts/verify.sh` | сохранить структуру проверок, заменить транспорт: `docker compose exec` → `ssh`/локальные вызовы на ВМ; добавить проверки PMD (загрузка, drops, `pmd-rxq-show`) | +| `Makefile` | цели `up`/`down`/`attach`/`shim` заменяются на управление сервисами; `flows`, `health`, `slots`, `fdb`, `trace` остаются | +| `perftest/k6_bench.js` | k6 через стек ядра не покажет предел DPDK: нужен генератор трафика (TRex, MoonGen, `dpdk-pktgen`) с профилем по **новым соединениям в секунду**, а не только по PPS | + +## 4. Что нужно решить до миграции — влияние на пайплайн + +Ни одно из перечисленного не блокирует запуск, но каждое влияет на потолок +производительности и должно быть решено осознанно. + +| Механизм | Где | Состояние на DPDK | Решение | +|---|---|---|---| +| `ct(commit,nat)` / `ct(nat)` | t12, t15, t24 | userspace conntrack работает, включая NAT | оставить на первом этапе; параллельно вести переход на stateless un-DNAT (`nw_src=member, tp_src=8080 → VIP:80`) — он уже стоит в заделе шага 3 | +| `learn` (FDB t25, adjacency t21) | t0, t1 | работает, но каждый новый MAC/адрес — upcall; несовместим с полным HW offload | для ВМ состав сегмента известен control plane: заменить обучение на **статические записи**, оставив `learn` только как страховку | +| `multipath` | t10 | считается в `ofproto`, работает | оставить; проверить детерминизм спайком | +| `recirculation` | после `ct` | работает | учесть в бюджете PMD-цикла: каждый `recirc` — повторный проход | +| `IN_PORT`, рассылка по списку портов | t5, t25 | работает | при большом числе ВМ в сегменте заменить рассылку на явные группы вывода | +| `type=internal` | `hcif-*` | tap, медленный путь | см. 3.5 | +| Hardware offload (`hw-offload=true`, `rte_flow`) | — | **несовместим** с `ct`, `learn`, `multipath`, `recirc` | на первом этапе не включать; рассматривать только после перехода на stateless-датапас | + +## 5. Изменения по файлам — сводка + +| Файл | Действие | +|---|---| +| `lb/pipeline.sh` | **без изменений** (кроме подстановки новых номеров портов) | +| `hc/maglev.go`, `hc/main.go` | без изменений; открытый вопрос — источник проб (3.5) | +| `lb/topology.env` | переписать порты: PCI-адреса, пути vhost-user, номера `ofport` | +| `lb/entrypoint.sh` | разделить на systemd-юниты и конфигурацию OVS | +| `lb/apply.sh`, `lb/lbctl.sh` | убрать обёртки `docker compose exec` | +| `docker-compose.yml`, `lb/Dockerfile` | не применяются в ПРОД (остаются для стенда) | +| `scripts/attach-segment.sh` | удалить | +| `scripts/host-prereq.sh` | переписать под hugepages/vfio/NUMA | +| `scripts/verify.sh` | адаптировать транспорт, добавить PMD-проверки | +| `perftest/` | заменить k6 на аппаратный генератор | +| `Makefile` | цели жизненного цикла заменить на управление сервисами | + +## 6. Потолок производительности — что определяет предел + +Главный вывод анализа: на DPDK узкое место сместится с PPS на **число новых +соединений в секунду (CPS)**. + +- `multipath` вычисляет слот от 5-tuple, поэтому megaflow для балансируемого + трафика матчит **полный 5-tuple**: каждое новое соединение даёт upcall и + собственную запись в датапасе. То же делает `ct(commit)`. +- Установленные соединения обслуживаются из кэшей (EMC → SMC → megaflow) на + скорости PMD; проблема только в темпе появления новых. +- Настройки, влияющие на это: размер EMC (`emc-insert-inv-prob`), число handler + threads, `pmd-cpu-mask`, `n_rxq`. + +Отсюда программа измерений: отдельно PPS на установленных потоках и отдельно CPS +на новых. Сравнивать надо с текущим kernel-датапасом на том же профиле, иначе +цифры не сопоставимы. + +Второй фактор — `recirculation` после каждого `ct`: удваивает число проходов по +таблицам для балансируемого трафика. + +## 7. План миграции по этапам + +| Этап | Содержание | Критерий готовности | +|---|---|---| +| 1 | Стенд с `datapath_type=netdev` на том же железе, порты — `dpdkvhostuser` + одна физическая NIC | все текущие проверки `verify.sh` проходят, дайджест раскладки совпадает с kernel-датапасом | +| 2 | Спайки S-DPDK-1…3 (раздел 8) | детерминизм подтверждён, источник проб выбран, CPS измерен | +| 3 | Замена `learn` на статические записи от control plane | FDB и adjacency не зависят от трафика; `verify.sh` зелёный | +| 4 | Stateless un-DNAT вместо `ct` в t15 и t24 | сессии живут, `dump-conntrack` пуст, recirc-проходов меньше | +| 5 | Профилирование и тюнинг: `n_rxq`, `pmd-rxq-affinity`, hugepages, TSO | целевые PPS и CPS достигнуты, PMD без drops | +| 6 | Оценка HW offload (только после этапа 4) | измеренный выигрыш против потери гибкости | + +Порядок не произвольный: этапы 3 и 4 снимают именно те механизмы, которые +блокируют offload и дают upcall на каждое соединение. + +## 8. Спайки, которые надо выполнить до перевода ПРОД + +- **S-DPDK-1 — детерминизм `multipath`.** Один и тот же корпус синтетических + 5-tuple через `ofproto/trace` на kernel- и netdev-датапасе обязан давать + одинаковые `reg1`/`reg2`. Блокирующий: на нём стоит вся модель раскладки. +- **S-DPDK-2 — `ct` с NAT в userspace.** Проверить обратную трансляцию в t15 и + t24, поведение при переполнении таблицы соединений, стоимость recirc. +- **S-DPDK-3 — источник health-проб.** Работает ли `type=internal` в + netdev-мосте, какова задержка проб под нагрузкой PMD, не голодает ли tap. +- **S-DPDK-4 — CPS и PPS.** Аппаратный генератор: предел по новым соединениям и + по установленным потокам, с профилем, приближенным к целевому. + +## 9. Открытые вопросы к разработке и эксплуатации + +1. Сколько ВМ в сегменте ожидается — от этого зависит, допустима ли рассылка + списком портов в t25 и нужны ли группы вывода. +2. Нужен ли `dpdkbond`/LACP на аплинке и VLAN-теги на портах сегментов — сейчас + VLAN в пайплайне не используется вовсе. +3. Целевые MTU и включение TSO для vhost-user. +4. Кто создаёт vhost-user порты и заполняет `topology.env` — гипервизор, + оркестратор или control plane HPNN (это же вопрос модели + `Load_Balancer → Listener → Pool → Member` в OVSDB). +5. Останется ли профиль A (default бэкенда мимо балансировщика) или в ПРОД + используется профиль B — от этого зависит объём egress через узел. +6. Где живёт узел относительно фабрики: сегменты приходят отдельными портами, + VLAN'ами или туннелями. + +## 10. Чек-лист конфигурации перед вводом в эксплуатацию + +```bash +# датапас и DPDK +ovs-vsctl get Open_vSwitch . dpdk_initialized # true +ovs-vsctl get bridge br-lb datapath_type # netdev +ovs-appctl dpif/show # netdev@ovs-netdev + +# PMD и очереди +ovs-appctl dpif-netdev/pmd-rxq-show +ovs-appctl dpif-netdev/pmd-stats-show # drops, cycles +ovs-appctl dpif-netdev/pmd-perf-show + +# хост +cat /proc/meminfo | grep Huge +lscpu | grep -i numa; numactl -H +dpdk-devbind.py --status # NIC на vfio-pci + +# функциональность (как в стенде) +make flows && make health && make slots && make fdb +lbctl trace p2 +```