Настройка VLAN для сетевых интерфейсов контейнеров LXD и виртуальных машин KVM


Продолжаем погружаться в бездны сетевой подсистемы Ubuntu Linux на примере моего распределенного тестового стенда и сегодня мы будем строить дополнительный уровень абстракции над физической сетью (распределенной и связанной мостами OpenVPN) с несколькими нодами. В идеале я хотел бы получить несколько VLAN-ов образующих плоские изолированные сети под каждый из тестовых проектов и я бы просто назначал интерфейсы тестовых машин определенным тестовым же сетям (изолированных VLAN-ами) и спокойно прорабатывал различные клиентские сценарии без каких-либо опасений устроить дополнительный DHCP в прод-сети или какой-нибудь сетевой шторм.

Собственно как вы можете видеть по предыдущим статьям в блоге:

Технически у меня все готово кроме нескольких интересных нюансов, но и на самом деле, там все сделано и я просто это еще как-то красиво в виде статьи не изложил, а остается у меня теперь только вопрос с проталкиванием виртуальных машин и контейнеров в определенные VLAN-ы.

Назначение VLAN для виртуальной машины в Proxmox

Сначала я подумал, что работа с VLAN происходит где-то на уровне гипервизора KVM и гипервизора контейнеров LXD и на эту мысль меня натолкнуло, то, что в тож же самом Proxmox при создании как полноценной виртуальной машины мы задаем номер VLAN в который эта машина будет включена (интерфейсом создаваемым по умолчанию).

Назначение VLAN для контейнера в Proxmox

Покопавшись немного глубже в этом вопросе я понял, что все эти настройки к гипервизору имеют довольно опосредованное отношение и являются чем то типа административного сахара если мыслить критериями из программирования где есть синтаксический сахар и выполнив на хосте Proxmox команду ifconfig мы увидим тот самый VLAN и виртуальные машин слинкованные мостом или к VLAN-интерфейсу или к чистому интерфейсу без тегов.

Вот например интерфейсы чистые и тегированные, как я уже демонстрировал в предыдущей статье про VLAN-ы:

# ifconfig | grep vm
vmbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
vmbr0v10: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
vmbr0v234: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
vmbr0v238: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

Назначение сетевых мостов интерфейсам:

# brctl show
bridge name     bridge id               STP enabled     interfaces
vmbr0           8000.ac1f6b60f30e       no              eno1
vmbr0v10                8000.ac1f6b60f30e       no              eno1.10
                                                        tap162i0
                                                        tap500i0
                                                        tap777i0
vmbr0v234               8000.ac1f6b60f30e       no              eno1.234
                                                        tap138i0
                                                        tap139i0
                                                        tap140i0
                                                        tap141i0
vmbr0v238               8000.ac1f6b60f30e       no              eno1.238
                                                        tap145i0

И вот после того как я все это понял, вся интрига пропала и остается нам только создать виртуальную сеть для LibVirt и шаблон для LXD под наш VLAN и в дальнейшем автоматизированно множить необходимое количество уровней сетевой абстракции автоматически при помощи Ansible.

Шаблон виртуальной сети для LibVirt

Как оказалось нельзя просто так взять и добавить виртуальный интерфейс средствами LibVirt в VLAN-интерфейс на хосте и для VLAN-интерфейса моста необходимо сделать еще один мост из которого создать виртуальную сеть для libVirt. Вот тут я честно говоря не уверен, что это прям отличная идея так делать и возможно есть способ проще и элегантнее, но с LibVirt у меня получилось только так выкрутиться и при этом схема работает как мне и требовалось. В гостевой операционной системе никаких правок не требуется и весь ее трафик мы получаем тегированным для виртуальной сети.

Параметры настроек интерфейсов для Ubuntu Linux (/etc/network/interfaces):

auto lo
iface lo inet loopback

auto enp3s0
iface enp3s0 inet static
address 10.1.1.102
netmask 255.255.255.0
gateway 10.1.1.254
nameservers 10.1.1.200 10.1.1.1
post-up /etc/firewall/firewall.sh

auto master-bridge
iface master-bridge inet static
address 10.212.11.201
netmask 255.255.0.0
bridge_ports none
bridge_stp off
bridge_fd 0
bridge_maxwait 0
post-up /etc/firewall/firewall.sh

auto master-bridge.2
iface master-bridge.2 inet static
address 0.0.0.0
netmask 0.0.0.0
vlan_raw_device master-bridge
post-up ifup mbvlan2

iface mbvlan2 inet static
address 0.0.0.0
netmask 0.0.0.0
bridge_ports master-bridge.2
bridge_stp on
bridge_fd 0.0

XML настройки виртуальной сети для LibVirt (совершенно не отличается от рассмотренной ранее):

<network>
  <name>vlan-2</name>
  <forward mode="bridge"/>
  <bridge name="mbvlan2"/>
</network>

Добавляем, запускаем:

# virsh net-define ./vlan2.xml
# virsh net-autostart vlan-2
# virsh net-start vlan-2

Проверяем что у нас получилось как мы и хотели.

Проверяем что у нас получился ангалог Proxmox

Создание тегированного VLAN-интерфейса контейнера LXD

Честно говоря, я ожидал, что с LXC/LXD будет больше мороки, а на самом деле все оказалось просто и я бы даже сказал элементарно в одну команду (ну в две если старый интерфейс удаляем). Итак, если у созданного из типового шаблона надо назначить тегирование на интерфейсе, то мы во первых удаляем существующий интерфейс:

# lxc config device remove vlan2 eth0
# lxc config device add vlan2 eth0 nic nictype=macvlan name=eth0 parent=master-bridge vlan=2

Можно кстати слинковать шаблоны машины с мостом mbvlan2 и эффект будет тем-же. Вот на этом мы пожалуй и закончим, а чуть позже я расскажу еще несколько нюансов с которыми я столкнулся при разработке этого внутреннего проекта.