# Переход на 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 ```