Files
hpnn-proto/docs/OVS_DPDK_MIGRATION.md
T
ayurishchevandClaude Opus 5 5e9a035f6e Документ по переходу ноды на OVS-DPDK
Анализ текущего состояния стенда и перечень изменений для ПРОД-окружения
на выделенном сервере с OVS-DPDK.

Основной вывод инвентаризации: вся форвардинг-логика DPDK-совместима,
pipeline.sh переносится без изменений. Переделке подлежат подключение
портов (macvlan и veth -> dpdk и vhost-user), размещение узла
(контейнер -> железо) и инструментарий.

Отдельно зафиксировано, что потолок производительности сместится с PPS
на CPS: multipath и ct(commit) дают upcall на каждое новое соединение,
и они же вместе с learn блокируют hardware offload.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:08:07 +03:00

247 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Переход на 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 <src> <sport> p2
```