Подключение виртуальной машины к внешнему VLAN
На этой странице описано, как подключить виртуальную машину Cozystack (приложение
vm-instance) напрямую к внешнему, физически маршрутизируемому VLAN. Такой L2-сегмент нужен ВМ, когда она должна оказаться в том же широковещательном домене, что и внешнее оборудование (сервер лицензирования, СХД, шлюз, управляемый вне кластера), и получить адрес из подсети этого VLAN, а не из оверлея кластера.
Сеть для ВМ в Cozystack по умолчанию работает только через оверлей (сеть подов плюс опциональные
подсети VPC KubeOVN). Подключение ВМ мостом в реальный VLAN — это другой сценарий, и у него есть одно неочевидное ограничение: он работает с Linux-мостом и CNI-плагином bridge и не работает с macvlan. Дальше в этом руководстве объясняется, почему, и приводится работающий рецепт.
Почему bridge, а не macvlan
KubeVirt по умолчанию подключает интерфейс ВМ к вторичной сети через bridge binding (именно это чарт vm-instance формирует для каждой сети в .spec.networks). При bridge binding в сеть уходит собственный MAC-адрес гостя — под-лончер не подменяет и не транслирует его.
Подключение через macvlan с этой моделью несовместимо. macvlan демультиплексирует входящие кадры строго по MAC-адресу дочернего macvlan-интерфейса. А поскольку KubeVirt выпускает в сеть MAC гостя, а не MAC дочернего macvlan-интерфейса, ответы от шлюза или других хостов приходят на родительский интерфейс с адресом получателя, равным MAC гостя, не совпадают ни с одним дочерним macvlan-интерфейсом и молча отбрасываются, так и не дойдя до ВМ. Симптом: гость может передавать (ARP-запросы и пинги уходят, это видно в tcpdump на родительском интерфейсе), но никогда не получает ответа (запись о шлюзе в его neighbor-таблице остаётся в состоянии FAILED). Побочное следствие: хост тоже не может достучаться до дочерних macvlan-интерфейсов через родительский интерфейс, поэтому хостовый сервис на IP-адресе родительского интерфейса недоступен из ВМ.
У Linux-моста такого ограничения нет: он пересылает трафик по выученным MAC-адресам на всех подключённых к нему портах, поэтому MAC гостя достижим, а хост может держать адрес на самом мосте, чтобы общаться с ВМ. Подключите VLAN-подынтерфейс к мосту и направьте на этот мост NetworkAttachmentDefinition типа bridge.
Обзор
Совместно работают три части:
- Linux-мост на каждом узле, в который включён тегированный VLAN-подынтерфейс. Это сеть уровня узла — она настраивается вашими средствами провижининга узлов (netplan / machine config Talos / systemd-networkd), а не чартом Cozystack.
NetworkAttachmentDefinitionтипаbridge, ссылающийся на этот мост, созданный в пространстве имён тенанта, где живёт ВМ.- Приложение
vm-instance, которое ссылается на NetworkAttachmentDefinition по имени в.networks, при этом статический адрес гостя задаётся через cloud-init.
Предварительные требования
- Включён пакет
multus(он предоставляет CRDNetworkAttachmentDefinitionи всю обвязку вторичных сетей). - CNI-плагин
bridgeприсутствует в/opt/cni/binна каждом узле. Пакетmultusсам размещает его там на всех платформах; о том, что именно он устанавливает, и о способе отказаться от этого см. README пакета multus — прочтите его перед обновлением кластера, если/opt/cni/binвы наполняете самостоятельно. Проверить можно командойls /opt/cni/bin/bridge; при отсутствии бинарника NetworkAttachmentDefinition падает с ошибкойfailed to find plugin "bridge" in path [/opt/cni/bin]. - В этой схеме нет IPAM-плагина: адреса назначаются внутри гостя, а не средствами CNI. Планируйте статические адреса для каждой ВМ.
1. Linux-мост на узле
Создайте мост, в который включён тегированный VLAN-подынтерфейс. Сам VLAN-подынтерфейс адреса не несёт; присутствие хоста в этом VLAN обеспечивает мост (это необязательно, но полезно для быстрой проверки доступности шлюза и для любого хостового сервиса, к которому должны обращаться ВМ).
В примере используется netplan на узле с Ubuntu/Debian; идентификатор VLAN и подсеть даны для иллюстрации (203.0.113.0/24, VLAN 100, шлюз 203.0.113.1). Адаптируйте пример под свои имена интерфейсов, а также под Talos или systemd-networkd, если провижинингом занимаются они:
network:
version: 2
vlans:
# Тегированный VLAN-подынтерфейс без собственного адреса —
# включён в мост.
uplink.100:
id: 100
link: uplink
bridges:
br100:
interfaces:
- uplink.100
# Необязательное присутствие хоста в VLAN. Маршрут по умолчанию
# у узла должен оставаться на management-интерфейсе — не
# добавляйте здесь второй маршрут по умолчанию.
addresses:
- 203.0.113.2/24
Замечания:
- Маршрут по умолчанию узла должен оставаться на management-интерфейсе. Адрес на мосте (если он есть) нужен только для доступности внутри VLAN, а не как второй шлюз по умолчанию.
netplan applyне может перевести интерфейс в мост, пока интерфейс держит потребитель (например, подvirt-launcher, использующий прежнее подключение черезmacvlan). Сначала уберите потребителя (удалите VMI, чтобы лончер отпустил интерфейс), и только потом меняйте конфигурацию.- После перезагрузки
systemd-networkdможет некоторое время сообщать, что мост «routable», хотя линк фактически ещё не поднят. Если вашим ВМ нужен VLAN сразу при загрузке, поставьте их запуск в зависимость от проверки доступности либо повторяйтеnetplan apply, пока шлюз не начнёт отвечать.
2. NetworkAttachmentDefinition
Создайте NetworkAttachmentDefinition типа bridge в том пространстве имён тенанта, где будет работать ВМ. Чарт vm-instance ищет сеть по имени в собственном пространстве имён ВМ, поэтому по одной копии должно существовать в каждом пространстве имён тенанта, где запускаются ВМ в этом VLAN.
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: vlan100
namespace: tenant-example
spec:
config: |
{
"cniVersion": "0.3.1",
"type": "bridge",
"bridge": "br100",
"ipam": {}
}
- Значение
bridgeдолжно совпадать с именем моста из шага 1 (здесь —br100). ipam: {}— со стороны кластера адреса не назначаются, гость настраивает адрес сам (шаг 3).
3. Подключение ВМ и назначение статического адреса
Сошлитесь на NetworkAttachmentDefinition по имени в values приложения vm-instance. Так как чарт не поддерживает networkData, статический адрес задаётся в userData cloud-init (параметр cloudInit) и применяется гостем при первой загрузке:
# values приложения vm-instance
instanceType: u1.medium
instanceProfile: ubuntu
disks:
- name: example-system
networks:
- name: vlan100
cloudInit: |
#cloud-config
write_files:
- path: /etc/netplan/60-vlan100.yaml
permissions: "0600"
content: |
network:
version: 2
ethernets:
# Совпадение по второй сетевой карте (первая — карта в сети
# подов). Берите интерфейс, который поднимается без аренды
# DHCP.
enp2s0:
addresses:
- 203.0.113.10/24
runcmd:
- netplan apply
В итоге у ВМ будет два интерфейса: всегда присутствующая сетевая карта в сети подов (default, используется для трафика внутри кластера и для механизмов внешнего доступа vm-instance) и сетевая карта в VLAN. Адрес /24 из примера поднимает только connected-маршрут в подсеть VLAN и не добавляет маршрут по умолчанию, поэтому исходящий трафик гостя остаётся там, где вам нужно (обычно — на карте в сети подов). Если же VLAN должен стать для гостя шлюзом по умолчанию, добавьте маршрут по умолчанию на карте VLAN и уберите его с карты в сети подов.
Подводные камни
- У ВМ два подключения (dual-homed). Чарт
vm-instanceвсегда добавляет сетевую карту в сеть подов вдобавок к любым сетям, объявленным вnetworks; варианта с одним подключением (только VLAN) сегодня нет. Настраивайте адрес карты VLAN внутри гостя, а карту в сети подов оставьте кластеру. networkDataне поддерживается. Чарт передаёт cloud-init только черезuserData, поэтому статическая настройка внутри гостя (netplan черезwrite_filesплюсnetplan apply, как выше) — это и есть способ назначить адрес в VLAN.- MAC меняется при пересоздании ВМ. KubeVirt генерирует новый MAC гостя при каждом пересоздании объекта VM, а
vm-instanceне даёт способа его закрепить, поэтому после пересоздания ВМ её MAC меняется. Вышестоящий шлюз при этом ещё несколько минут держит устаревшую ARP-запись со старым MAC, так что «шлюз недоступен» сразу после пересоздания ВМ — это ожидаемо: дождитесь устаревания ARP-записи (примерно пять минут), а не считайте это неисправностью. - Трафик от хоста к ВМ. Если хост должен общаться с ВМ (прокси, health check), дайте мосту хостовый адрес в VLAN (шаг 1): трафик через «голый» VLAN-подынтерфейс к гостям, подключённым через мост, не будет работать так, как этого ожидают пользователи
macvlan.
См. также
- Подключение GPU к виртуальным машинам — проброс GPU NVIDIA и профилей vGPU в те же самые ВМ.
- Сетевая архитектура — как устроена плоскость данных оверлея по умолчанию.
- Руководство пользователя KubeVirt, Interfaces and Networks — bridge binding в сравнении с другими способами подключения.