Архитектура стенда (ANSI)
+-------------------------------------------------------------------+
| ФИЗИЧЕСКИЙ ХОСТ (Linux) |
| |
| +-------------------------+ +---------------------+ |
| | Виртуальная Машина | | Сетевой Пространство| |
| | (VM Libvirt) | | Имен (Netns) | |
| | | | | |
| | +-------------------+ | | +---------------+ | |
| | | eth0 (virtio-net) | | | | tap-ns1 | | |
| +--+---------+---------+--+ +--+-------+-------+--+ |
| | (Shared Memory) | |
| v (Hugepages) | |
| /var/run/openvswitch/vhost-vm1 | |
| | | |
| v (vhostuserclient) v (internal) |
| +------------+-------------------------------------+----------+ |
| | [vhost-vm1] [tap-ns1] | |
| | | |
| | Бридж OVS (br-dpdk) | |
| | (datapath_type=netdev) | |
| | | |
| | +-------------------------------------------------------+ | |
| | | [dpdk-p0] (Физический порт сетевой карты под vfio-pci) | | |
| | +---------------------------+---------------------------+ | |
| | | | |
| | v (PMD Threads / CPU Cores) | |
| +------------------------------+------------------------------+ |
| | |
| v (DPDK Driver Bypass Kernel) |
| +---------------------------+ |
| | Физическая сетевая карта | |
| | (PCI: 0000:03:00.0) | |
+-------------------+---------------------------+-------------------+
ШАГ 1: Конфигурация GRUB (Изоляция ядер и Hugepages при загрузке)
Это фундамент. Если не изолировать ядра на уровне загрузчика, планировщик Linux завалит PMD-потоки OVS своими системными прерываниями.
- Узнаем NUMA-ноду вашей сетевой карты: Найдите PCI-адрес карты (например, 0000:03:00.0) через lspci | grep Ethernet. Проверьте её NUMA-ноду:
cat /sys/class/net/<имя_интерфейса>/device/numa_nodeДопустим, команда вернула 0. Значит, выделяем память и сажаем ВМ на NUMA-ноду 0. 2. Редактируем параметры загрузки:
sudo nano /etc/default/grubНайдите строку GRUB_CMDLINE_LINUX_DEFAULT и приведите её к следующему виду (выделяем 2 огромные страницы по 1 ГБ, изолируем ядра 2 и 4 под PMD-потоки):
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash default_hugepagesz=1G hugepagesz=1G hugepages=2 isolcpus=2,4 nohz_full=2,4 rcu_nocbs=2,4 iommu=pt intel_iommu=on"
(Если процессор AMD, замените intel_iommu=on на amd_iommu=on).
- Применяем конфигурацию и уходим в ребут:
sudo update-grub
sudo reboot- Проверяем статус после перезагрузки:
# Должно выдать 2,4
cat /sys/devices/system/cpu/isolated
# Должно показать Total: 2
cat /proc/meminfo | grep Hugepages_TotalШАГ 2: Привязка физической сетевой карты к DPDK
Ядро Linux больше не должно управлять этой картой. Переводим её под контроль vfio-pci.
- Загружаем модули ядра:
sudo modprobe vfio-pci
echo "vfio-pci" | sudo tee -a /etc/modules- Включаем безопасный режим No-IOMMU (если работаете в виртуалке или тест-стенде без аппаратного IOMMU):
echo 1 | sudo tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode- Отвязываем карту и отдаем DPDK:
# Устанавливаем тулзы dpdk, если их нет
sudo apt update && sudo apt install -y dpdk
# Привязываем интерфейс по его PCI-адресу
sudo dpdk-devbind.py --bind=vfio-pci 0000:03:00.0Проверяем: команда dpdk-devbind.py --status должна показать карту в секции "Network devices using DPDK-compatible driver".
ШАГ 3: Установка и настройка OVS-DPDK
- Устанавливаем OVS с поддержкой DPDK и переключаем бинарник по умолчанию:
sudo apt install -y openvswitch-switch-dpdk
sudo update-alternatives --set ovs-vswitchd /usr/lib/openvswitch-switch-dpdk/ovs-vswitchd-dpdk- Конфигурируем демоны OVS (Выделение ядер и сокетов памяти):
# Включаем саму поддержку DPDK
sudo ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true
# Выделяем по 1 ГБ памяти из Hugepages конкретно на NUMA-ноду 0 (где сидит карта)
sudo ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-socket-mem="1024,0"
# Привязываем PMD-потоки к изолированным ядрам 2 и 4 (шестнадцатеричная маска 0x14)
sudo ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x14
# Перезапускаем сервис для применения настроек
sudo systemctl restart openvswitch-switch- Проверяем успешность инициализации:
sudo ovs-vsctl get Open_vSwitch . dpdk_initializedОбязано вернуть true. Если вернуло ошибку — смотрите лог /var/log/openvswitch/ovs-vswitchd.log.
ШАГ 4: Создание виртуального коммутатора и портов
- Создаем Userspace-бридж типа netdev:
sudo ovs-vsctl add-br br-dpdk -- set bridge br-dpdk datapath_type=netdev- Добавляем привязанную физическую карту в качестве аплинка:
sudo ovs-vsctl add-port br-dpdk dpdk-p0 -- set Interface dpdk-p0 type=dpdk options:dpdk-devargs=0000:03:00.0- Добавляем порт для ВМ (vhostuserclient):
sudo ovs-vsctl add-port br-dpdk vhost-vm1 -- set Interface vhost-vm1 type=dpdkvhostuserclient options:vhost-server-path=/var/run/openvswitch/vhost-vm1- Добавляем внутренний порт для пространства имен (netns):
sudo ovs-vsctl add-port br-dpdk tap-ns1 -- set Interface tap-ns1 type=internalШАГ 5: Настройка Сетевого Пространства Имен (Netns)
- Создаем netns и перемещаем туда созданный интерфейс:
sudo ip netns add my-ns
sudo ip link set tap-ns1 netns my-ns- Инициализируем интерфейс внутри netns и вешаем IP:
sudo ip netns exec my-ns ip link set tap-ns1 up
sudo ip netns exec my-ns ip addr add 10.0.0.10/24 dev tap-ns1ШАГ 6: Настройка прав доступа и обход AppArmor (Важнейший фикс)
Если пропустить этот шаг, Libvirt упадет с ошибкой Permission Denied при старте ВМ, так как системы защиты заблокируют QEMU доступ к сокету OVS.
- Выставляем права на UNIX-сокет:
sudo chown libvirt-qemu:kvm /var/run/openvswitch/vhost-vm1
sudo chmod 777 /var/run/openvswitch/vhost-vm1- Добавляем исключение в AppArmor для профиля QEMU:
sudo nano /etc/apparmor.d/abstractions/libvirt-qemuЛистаем в самый конец файла и прямо перед закрывающей фигурной скобкой } добавляем строчку:
/var/run/openvswitch/* rw,
Перезапускаем службу AppArmor:
sudo systemctl restart apparmorШАГ 7: Настройка XML-конфигурации ВМ в Libvirt
Открываем конфигурацию нужной виртуалки через virsh edit <имя_вм>. Модифицируем три критических блока внутри основного тега :
- Память (Строго Shared Memory и Hugepages):xml
<memory unit='KiB'>2097152</memory> <!-- 2 ГБ выделено под ВМ -->
<currentMemory unit='KiB'>2097152</currentMemory>
<memoryBacking>
<hugepages/>
<source type='file'/>
<access mode='shared'/>
</memoryBacking>
- Топология CPU и привязка к NUMA-ноде 0:xml
<cpu mode='host-passthrough' check='none'>
<numa>
<cell id='0' cpus='0-1' memory='2097152' unit='KiB' memAccess='shared'/>
</numa>
</cpu>
- Добавление интерфейса vhostuser в секцию :xml
<devices>
...
<interface type='vhostuser'>
<mac address='52:54:00:12:34:56'/>
<source type='unix' path='/var/run/openvswitch/vhost-vm1' mode='client'/>
<model type='virtio'/>
<driver name='vhost' queues='2'>
<alloc type='anonymous'/>
</driver>
</interface>
...
</devices>
- Запускаем гостевую ОС:bash
virsh start <имя_вм>
ШАГ 8: Верификация и проверка трафика
- Проверяем, что PMD-потоки OVS успешно захватили сокеты интерфейсов:bash
sudo ovs-appctl dpif-netdev/pmd-rxq-show
В выводе вы обязаны увидеть интерфейсы vhost-vm1 и dpdk-p0, распределенные между ядрами 2 и 4. 2. Проверяем целостность бриджа OVS:
sudo ovs-vsctl showВозле созданных портов не должно быть никаких надписей вроде error или dropped. 3. Тестируем связность: Зайдите в саму ВМ, настройте статический IP 10.0.0.20/24 на сетевом интерфейсе eth0. Из хостовой системы запустите пинг из пространства имен my-ns до виртуальной машины:
sudo ip netns exec my-ns ping 10.0.0.20Если пинги всё равно не идут, пришлите вывод одной команды: • sudo ovs-ofctl dump-flows br-dpdk (проверим, нет ли дефолтных правил OpenFlow, которые блокируют коммутацию пакетов внутри самого бриджа).
Для настройки VXLAN поверх OVS-DPDK в режиме Point-to-Point (между двумя хостами) требуется использовать инкапсуляцию на уровне пользователей (userspace). Трафик, выходящий из виртуальной машины или пространства имен, попадает в Userspace-бридж, упаковывается в VXLAN-туннель потоками PMD и выстреливается напрямую через физический DPDK-порт в сторону соседа. Ниже приведен полный монолитный мануал для настройки Хоста А и Хоста Б.
Архитектура туннеля Point-to-Point (ANSI)
================================== ХОСТ А ==================================
[ VM / Netns (10.0.0.10) ]
|
v
Бридж [br-int] (Внутренние сетевые порты)
|
v
Бридж [br-phy] (datapath_type=netdev)
=========================================
| Интерфейс VXLAN (type=vxlan) |
| Local IP: 192.168.1.1 (dpdk-p0) |
| Remote IP: 192.168.1.2 |
=========================================
|
v (Инкапсулированный VXLAN трафик, UDP 4789)
Физический порт [dpdk-p0] (PCI: 0000:03:00.0)
|
|================== ФИЗИЧЕСКИЙ L2/L3 СТЫК ==================
|
Физический порт [dpdk-p0] (PCI: 0000:03:00.0)
^
| (Декапсуляция VXLAN)
=========================================
| Интерфейс VXLAN (type=vxlan) |
| Local IP: 192.168.1.2 (dpdk-p0) |
| Remote IP: 192.168.1.1 |
=========================================
Бридж [br-phy] (datapath_type=netdev)
^
|
Бридж [br-int] (Внутренние сетевые порты)
^
|
[ VM / Netns (10.0.0.20) ]
================================== ХОСТ Б ==================================
ШАГ 1: Базовая подготовка GRUB и памяти (Одинаково на обоих хостах)
Для работы VXLAN в OVS-DPDK хостам необходимы изолированные ядра и выделенные Hugepages.
- Конфигурация GRUB:
sudo nano /etc/default/grubДобавьте параметры выделения двух страниц по 1 ГБ и изоляции ядер 2,4 для PMD-потоков:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash default_hugepagesz=1G hugepagesz=1G hugepages=2 isolcpus=2,4 nohz_full=2,4 rcu_nocbs=2,4 iommu=pt intel_iommu=on"
- Применение и перезагрузка:
sudo update-grub && sudo rebootШАГ 2: Привязка физических карт к DPDK (Одинаково на обоих хостах)
Физические интерфейсы, через которые хосты связаны друг с другом, отдаются под управление драйвера vfio-pci.
# Загружаем модуль
sudo modprobe vfio-pci
# Разрешаем режим No-IOMMU при необходимости
echo 1 | sudo tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
# Привязываем сетевую карту (подставьте ваш PCI-адрес, например 0000:03:00.0)
sudo dpdk-devbind.py --bind=vfio-pci 0000:03:00.0ШАГ 3: Запуск и инициализация OVS-DPDK (Одинаково на обоих хостах)
# Установка и переключение на версию DPDK
sudo apt update && sudo apt install -y openvswitch-switch-dpdk dpdk
sudo update-alternatives --set ovs-vswitchd /usr/lib/openvswitch-switch-dpdk/ovs-vswitchd-dpdk
# Конфигурация ресурсов OVS
sudo ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true
sudo ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-socket-mem="1024,0"
sudo ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x14
# Перезапуск службы и верификация (должно быть true)
sudo systemctl restart openvswitch-switch
sudo ovs-vsctl get Open_vSwitch . dpdk_initializedШАГ 4: Настройка сетевой топологии на ХОСТЕ А
Нам понадобятся два бриджа:
- br-phy — физический бридж (в пространстве netdev/userspace), который держит карту и упаковывает VXLAN.
- br-int — сервисный бридж, куда подключаются клиенты (ВМ или контейнеры). bash
sudo ovs-vsctl add-br br-phy -- set bridge br-phy datapath_type=netdev
sudo ovs-vsctl add-port br-phy dpdk-p0 -- set Interface dpdk-p0 type=dpdk options:dpdk-devargs=0000:03:00.0
sudo ip addr add 192.168.1.1/24 dev br-phy
sudo ip link set br-phy up
sudo ovs-vsctl add-br br-int -- set bridge br-int datapath_type=netdev
# 5. Создаем туннельный VXLAN порт на br-phy, смотрящий на Хост Б (192.168.1.2)
# key=flow позволяет гибко управлять VNI через правила OpenFlow, vni=100 задает жесткий ID туннеля
sudo ovs-vsctl add-port br-int vxlan0 -- set Interface vxlan0 type=vxlan \
options:remote_ip=192.168.1.2 \
options:local_ip=192.168.1.1 \
options:key=100 \
options:dst_port=4789
ШАГ 5: Настройка сетевой топологии на ХОСТЕ Б
Выполняются зеркальные настройки. Меняются местами параметры local_ip и remote_ip.
# 1. Создаем физический бридж
sudo ovs-vsctl add-br br-phy -- set bridge br-phy datapath_type=netdev
# 2. Добавляем в него физический DPDK порт
sudo ovs-vsctl add-port br-phy dpdk-p0 -- set Interface dpdk-p0 type=dpdk options:dpdk-devargs=0000:03:00.0
# 3. Назначаем IP-адрес для OVS-интерфейса br-phy на Хосте Б
sudo ip addr add 192.168.1.2/24 dev br-phy
sudo ip link set br-phy up
# 4. Создаем внутренний бридж интеграции
sudo ovs-vsctl add-br br-int -- set bridge br-int datapath_type=netdev
# 5. Создаем туннельный VXLAN порт на br-phy, смотрящий на Хост А (192.168.1.1)
sudo ovs-vsctl add-port br-int vxlan0 -- set Interface vxlan0 type=vxlan \
options:remote_ip=192.168.1.1 \
options:local_ip=192.168.1.2 \
options:key=100 \
options:dst_port=4789ШАГ 6: Подключение тестовых эндпоинтов (Netns)
Для проверки прокинем по одному Network Namespace в бридж br-int на каждом хосте. На ХОСТЕ А:
sudo ip netns add ns-client
sudo ovs-vsctl add-port br-int tap-client -- set Interface tap-client type=internal
sudo ip link set tap-client netns ns-client
sudo ip netns exec ns-client ip link set tap-client up
sudo ip netns exec ns-client ip addr add 10.0.0.10/24 dev tap-clientНа ХОСТЕ Б:
sudo ip netns add ns-client
sudo ovs-vsctl add-port br-int tap-client -- set Interface tap-client type=internal
sudo ip link set tap-client netns ns-client
sudo ip netns exec ns-client ip link set tap-client up
sudo ip netns exec ns-client ip addr add 10.0.0.20/24 dev tap-clientШАГ 7: Настройка ARP-таблицы внутри OVS (Критически важно для Userspace VXLAN)
Так как инкапсуляция VXLAN происходит в пространстве пользователя (Userspace) силами самого OVS-DPDK, коммутатор должен заранее знать MAC-адрес соседа, соответствующий удаленному туннельному IP (remote_ip). Ядро Linux в этом процессе не участвует, поэтому OVS должен составить свою ARP-таблицу. На ХОСТЕ А (сообщаем OVS MAC-адрес Хоста Б): Выясните MAC-адрес интерфейса br-phy на Хосте Б с помощью ip link show br-phy (например, aa:bb:cc:dd:ee:22).
sudo ovs-appctl tnl/arp/set br-phy 192.168.1.2 aa:bb:cc:dd:ee:22На ХОСТЕ Б (сообщаем OVS MAC-адрес Хоста А): Выясните MAC-адрес интерфейса br-phy на Хосте А (например, aa:bb:cc:dd:ee:11).
sudo ovs-appctl tnl/arp/set br-phy 192.168.1.1 aa:bb:cc:dd:ee:11ШАГ 8: Верификация туннеля и прогон трафика
- Проверяем статус портов на обоих хостах:
sudo ovs-vsctl showВ выводе бриджа br-int вы должны увидеть порт vxlan0 без ошибок. 2. Проверяем внутреннюю ARP/Туннельную таблицу OVS:
sudo ovs-appctl tnl/arp/showЗапись соседа должна быть в статусе валидной. 3. Запуск пинга сквозь VXLAN туннель: С Хоста А отправляем пакеты из пространства имен до пространства имен на Хосте Б:
sudo ip netns exec ns-client ping 10.0.0.20Если пинг не проходит, пришлите вывод отладочной команды с любого из хостов: • sudo ovs-appctl dpif/show (покажет, видит ли ядро OVS виртуальные туннельные маршруты для инкапсуляции).