246 lines
19 KiB
Markdown
246 lines
19 KiB
Markdown
# Переход на 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
|
|||
|
|
```
|