NUC, KVM y Open vSwitch
Configuración de un NUC con Ubuntu + Open vSwitch + VLAN’s + KVM + VM’s con netplan y Open vSwitch. El networking está basado en OVS y Netplan tanto en el Host como en las máquinasa virtuales.
El año pasado documenté aquí cómo configurar OpenvSwitch con KVM. En este apunte voy a ir más al grano, mostraré el estado final deseado y cómo configurarlo.
Introducción
Netplan
Netplan es una utilidad para configurar fácilmente la red en un sistema Linux. Basta con crear una descripción YAML de las interfaces de red necesarias y lo que deben hacer. A partir de esta descripción Netplan generará toda la configuración necesaria para que la herramienta de renderizado haga su magia y la active. Netplan lee la configuración desde /etc/netplan/*.yaml
y genera archivos de configuración específicos en /run
y se los entrega al demonio o backend particular del equipo para que “renderice” dicha configuración. Los soportados son:
- NetworkManager
- Systemd-networkd <== usado en Ubuntu 22.04.1
Open vSwitch
Open vSwitch (OVS) es un bridge virtual de código abierto. Orientado a la automatización y a mejorar el switching en entornos de máquinas virtuales. Date un paseo por la página de OVS en la Wikipedia, para una introducción.
- Permite trabajar en un único Hypervisor o de forma distribuida en varios.
- Está considerada como la implementación más popular de OpenFlow, un controlador/protocolo para manejar switches hardware (que soporten openflow), donde la red se puede programar por software, independiente de la marca del fabricante. Mi instalación va a ser standlone, sin controlador externo (no uso OpenFlow)
- Admite interfaces y protocolos de gestión estándar (p. ej. NetFlow, sFlow, IPFIX, RSPAN, CLI, LACP, 802.1ag).
- Está diseñado para poder distribuir el switching a través de múltiples servidores físicos, similar a lo que se hace con
vNetwork distributed vswitch
de VMware o elNexus 1000V
de Cisco. - Soporta bastantes cosas más, aquí tienes la lista completa.
Nota: Open vSwitch puede trabajar con interfaces TUN, TAP y Internal Ports (propias). En mi configuración NO UTILIZO las interfaces TUN ni TAP, solo voy a usar sus propios Internal Ports . |
A modo recordatorio, TUN y TAP son dispositivos virtuales de red que residen en el Kernel, TUN opera a nivel 3 (routing) y TAP a nivel 2 (bridges/switching). Trabajan usando sockets en el espacio de usuario. Tradicionalmente se han usado siempre junto con el Bridge estándar que incorpora el Kernel de Linux y con KVM/QEMU.
Con Open vSwitch utilizo solo sus propios Internal Ports
en vez de interfaces TAP
. Es más, ten cuidado con los ejemplos que encuentres por ahí documentados con tap
y Open vSwitch porque te pueden llevar a confusión. En este apunte (y en mi instalación en producción) con KVM/QEMU y OVS solo voy a usar Internal Ports
. El motivo es que Open vSwitch puede trabajar con TAP
pero los trata como si fuesen un interfaz normal, es decir, no los abre vía sockets
(modo estándar) y eso supone ciertas limitaciones e implicaciones, te recomiendo leer el artículo: Q: I created a tap device tap0, configured an IP address on it, and added it to a bridge…: en los Common Configuration Issues de OVS.
Sus principales componentes son:
ovs-vswitchd
: Es el demonio (núcleo) de OVS junto con el móduloopenvswitch_mod.ko
(para el kernel). Ambos se encargan de la conmutación, VLAN’s, bonding, monitorización. El primer paquete lo gestiona el daemon en el user-space, pero del resto de la conmutación se encarga el módulo del kernel.ovsdb-server
: Es el segundo al mando, se trata de un servidor de base de datos ligero que guarda la configuración de OVS.
Instalación
Instalación de Ubuntu
Antes de meterme con el networking voy a dejar aquí un mini resumen de la instalación del servidor host ubuntu sobre NUC de intel:
- Descargo Ubuntu Server LTS 22.04.1
- Verifico la descarga, copio la imagen a una USB (usando BalenaEtcher)
- Sigo el tutorial de instalción
- Boot desde la USB, NUC (F10) -> UEFI Bootloader -> Try to install Ubuntu Server
- Español, Spanish, Ubuntu Server, Ethernet, Mirror por defecto, actualizo instalador
- Uso el disco entero, creo el usuario, instalo openssh, ignoro los snaps
- Cuando termina la instalación hago reboot.
- Creo la clave público/privada para poder trabajar vía SSH
- Actualizo el sistema operativo
apt update && apt upgrade -y && apt full-upgrade -y apt autoremove -y --purge systemctl reboot -f
- Verifico si puedo limpiar kernel’s antiguos
uname -a dpkg --list | grep linux-image apt purge linux-image-xxxxxx
- Limpio el sistema
apt clean journalctl --vacuum-time=2d journalctl --vacuum-size=500M
Instalación de Open vSwitch
Tras la instalación de Ubuntu Server LTS 22.04.1,
- Instalo OVS
root@luna:~# apt install openvswitch-switch
- Ubuntu habilita y arranca el servicio
openvswitch-switch.service
que a su vez arranca los dos daemons antes comentados.root@luna:~# ps -ef | grep ovs root 1179 1 0 13:29 ? 00:00:00 ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach root 1238 1 0 13:29 ? 00:00:00 ovs-vswitchd unix:/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach
Instalación de herramientas de red
- Instalo el soporte de VLAN’s y de paso algunas herramientas útiles
root@luna:~# apt install net-tools nmap vlan
Configuración
Preparo luna
con la siguiente configuración.
- Interfaz
eth0
conectado a un puerto del Switch en modo trunk - El propio servidor accede a la red por la
vlan100
con una IP estática - los guest’s (VM’s) futuros de KVM instanciarán
Internal Ports
dinámicamente.
Deshabilito IPv6
- No voy a usar IPv6 para nada, así que lo quito por completo de forma permanente
root@luna:~# cat /etc/default/grub : GRUB_CMDLINE_LINUX_DEFAULT="ipv6.disable=1" : root@luna:~# update-grub root@luna:~# reboot -f
Configuración netplan
- Preparo el fichero
netplan
definitivo, pero de momento no lo activo. Asigno el nombreeth0
al interfaz ethernet, desactivo el interfaz WiFi, defino la IP en la vlan100 (usando la interfaz vnet100 que crearé más adelante).root@luna:~# cat /etc/netplan/00-red-luna.yaml root@luna:~# cat /etc/netplan/00-red-luna.yaml # # Config de red del servidor luna # By LuisPa 2023 # network: version: 2 renderer: networkd wifis: {} ethernets: eth0: optional: true # Ignorar la red para acelerar el tiempo de boot match: macaddress: "94:c6:91:1a:a6:3e" name: eno1 name: eth0 set-name: eth0 dhcp4: no lunabr: dhcp4: no vnet100: optional: true addresses: - 192.168.105.253/24 routes: - to: default via: 192.168.105.1 nameservers: addresses: [192.168.105.224] search: [parchis.org]
A partir de ahora cuidado si estás conectado vía SSH, los siguientes comandos van a cortar la comunicación, así que mejor vete a la consola y sigue desde ahí |
Configuración OVS
- Creo el bridge
lunabr
. Añado el puertoeth0
al bridgelunabr
como puerto Trunk (no hace falta hacer nada especial, los puertos físicos se añaden en modo Trunk por defecto). Creo el interfazvnet100
para queluna
pueda trabajar en la vlan100. Estos comandos deovs-vsctl
son persistentes (recuerda la base de datos).root@luna:~# ovs-vsctl add-br lunabr root@luna:~# ovs-vsctl add-port lunabr eth0 # Aquí es donde se corta la comunicación root@luna:~# ovs-vsctl add-port lunabr vnet100 tag=100 -- set Interface vnet100 type=internal
Activo la configuración
- Activo la configuración de netplan que había hecho antes, cambio el cable de red de mi servidor a un puerto TRUNK del switch y hago un reboot
root@luna:~# netplan apply : CAMBIO EL CABLE de luna a un puerto TRUNK del Switch : root@luna:~# reboot -f
- Tras el arranque, conecto con
luna
vía SSH desde un laptop en la vlan100, verifico el estado de los puertos, el bridge, etc.➜ ~ ssh luis@192.168.105.253 : root@luna:~# ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ovs-system state UP mode DEFAULT group default qlen 1000 link/ether 94:c6:91:1a:a6:3e brd ff:ff:ff:ff:ff:ff altname enp0s31f6 3: wlp58s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether a0:c5:89:aa:13:c4 brd ff:ff:ff:ff:ff:ff 4: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 82:d4:11:3d:34:37 brd ff:ff:ff:ff:ff:ff 5: lunabr: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/ether 94:c6:91:1a:a6:3e brd ff:ff:ff:ff:ff:ff 6: vnet100: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/ether 7e:14:01:8e:72:5b brd ff:ff:ff:ff:ff:ff
root@luna:~# ovs-vsctl show f283cd6c-17e8-4bf3-be1a-b50904e91fc3 Bridge lunabr Port lunabr Interface lunabr type: internal Port eth0 Interface eth0 Port vnet100 tag: 100 Interface vnet100 type: internal ovs_version: "2.17.3"
Añadimos KVM/QEMU
- Instalo
KVM/QEMU
yvirt-manager
. La instalación es relativamente sencilla, al principio del apunte sobre Vagrant con Libvirt KVM explico cómo hacerlo.root@luna:~# apt install qemu qemu-kvm libvirt-clients \ libvirt-daemon-system virtinst root@luna:~# apt install virt-manager
Nota: No instalo las bridge-utils , utilidades para configurar el Bridge Ethernet en Linux porque no las necesitamos, vamos a usar OVS |
- Añado mi usuario al grupo
libvirt
ykvm
… yreboot
para que luego funcioneqemu-system-x86_64
)root@luna:~# adduser luis libvirt root@luna:~# adduser luis kvm root@luna:~# systemctl reboot -f
Configuración dinámica OVS + Libvirt + QEMU/KVM
Aprovecho la integración entre Libvirt y Open vSwitch para no tener que hacer configuraciones estáticas. Dejaré que sea libvirt quien llame a ovs-vsctl
para crear (al levantar las VMs) y destruir (al parar las VMs) los puertos del Bridge.
Integración de Open vSwitch con Libvirt
Open vSwitch soporta las redes que gestiona libvirt
en modo bridged
(no las NAT). Encontrarás más información aquí.
- Voy a crear una de red persistente en Libvirt desde un fichero XML. Creo el fichero
/root/switch-lunabr.xml
que define la posibilidad de acceder desde las futuras máquinas virtuales al TRUNK o a las VLAN’s independientemente.
<!-- Ejemplo de fichero XML para crear una red virtual persistente dentro de Libvirt.
-->
<network>
<name>switch-lunabr</name>
<forward mode='bridge'/>
<bridge name='lunabr'/>
<virtualport type='openvswitch'/>
<portgroup name='Sin vlan'>
</portgroup>
<portgroup name='Trunk Core'>
<vlan trunk='yes'>
<tag id='2'/>
<tag id='3'/>
<tag id='6'/>
<tag id='100'/>
</vlan>
</portgroup>
<portgroup name='Vlan 2'>
<vlan>
<tag id='2'/>
</vlan>
</portgroup>
<portgroup name='Vlan 3'>
<vlan>
<tag id='3'/>
</vlan>
</portgroup>
<portgroup name='Vlan 6'>
<vlan>
<tag id='6'/>
</vlan>
</portgroup>
<portgroup name='Vlan 100' default='yes'>
<vlan>
<tag id='100'/>
</vlan>
</portgroup>
</network>
- Desactivo la red
default
que se instala por defecto
root@luna:~# virsh net-autostart --disable default
Network default unmarked as autostarted
root@luna:~# virsh net-destroy default
Network default destroyed
root@luna:~# virsh net-undefine default
Network default has been undefined
- Activo la nueva red persistente desde el fichero que acabo de crear
root@luna:~# virsh net-define switch-lunabr.xml
(Se crea el fichero /etc/libvirt/qemu/networks/switch-lunabr.xml)
root@luna:~# virsh net-start switch-lunabr
Network switch-lunabr started
root@luna:~# virsh net-autostart switch-lunabr
Network switch-lunabr marked as autostarted
root@luna:~# virsh net-list
Name State Autostart Persistent
----------------------------------------------
switch-lunabr active yes yes
- Desde mis VM’s veré las nuevas opciones cuando seleccione la Red
- Lo mejor está por llegar. Una vez que arranque la VM se va a crear dinámicamente un nuevo interfaz llamado vnetN con la configuración que establecí en el XML. En este ejemplo hemos seleccionado el llamado VLAN100, en el XML tiene
tag id='100'
, por lo tanto nos creará un virtual port interno con eltag: 100
.
Maquina virtual y puerto TRUNK dinámico
Es posible entregar el puerto TRUNK a una VM y que se cree en OVS de forma dinámica durante el arranque. Lo primero que hago es instalar mi VM y luego añado un interfaz de red. En mi caso arranco virt-manager de la siguiente forma:
luis@luna:~$ type vm
vm is aliased to `LC_ALL=C virt-manager'
luis@luna:~$ vm
- Creo una VM basada en Ubuntu y añado un interfaz de tipo Bridge
IMPORTANTE - crea una configuración incompleta. Si entramos en la sección XML vemos que no se están especificando qué VLAN’s queremos presentar a la máquina virtual. Si intento arrancar la VM devolverá el error libvirt unable to add trunk bridge port vnetXX operation not supported |
- Edito la sección XML y añado manulamente algunos parámetros. El ID en
<parameters interfaceid="">
me lo he inventado.
$ virsh edit lunafw.parchis.org
:
<interface type="bridge">
<mac address="52:54:01:aa:01:01"/>
<source bridge="lunabr"/>
<vlan trunk="yes">
<tag id="2"/>
<tag id="3"/>
<tag id="6"/>
<tag id="100"/>
</vlan>
<virtualport type="openvswitch">
<parameters interfaceid="7d072813-014c-41f9-9f0d-4ebc350b9887"/>
</virtualport>
<target dev="vnet5"/>
<model type="virtio"/>
<alias name="net0"/>
<address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x0"/>
</interface>
- A partir de aquí la VM arrancará correctamente.
Ejemplos de configuración en las VM
Para poder trabajar con este TRUNK en la Virtual Machine tendremos que configurarlo de forma adecuada. Cada sistema operativo tiene su forma de hacerlo.
- Ubuntu LTS 22.04 con
netplan
# Netplan by LuisPa
network:
ethernets:
eth0:
optional: true
match:
macaddress: '52:54:01:aa:01:01'
name: enp1s0
name: eth0
set-name: eth0
dhcp4: no
vlans:
vlan6:
optional: true
id: 6
link: eth0
macaddress: "52:54:01:aa:00:06"
dhcp4: yes
vlan100:
optional: true
id: 100
link: eth0
macaddress: "52:54:01:aa:01:00"
addresses:
- 192.168.105.1/22
nameservers:
addresses:
- 192.168.105.224
search:
- parchis.org
version: 2
- Gento linux con
systemd-networkd.service
Hay que crear múltiples ficheros en el directorio /etc/systemd/network
gentoo-lunafw /etc/systemd/network # ls -al
total 76
drwxr-xr-x 2 root root 4096 feb 14 10:33 .
drwxr-xr-x 9 root root 4096 feb 14 09:53 ..
-rw-r--r-- 1 root root 110 feb 14 08:54 eth0.network
-rw-r--r-- 1 root root 0 feb 14 09:52 .keep_sys-apps_systemd-0
-rw-r--r-- 1 root root 115 feb 14 10:26 vlan100.netdev
-rw-r--r-- 1 root root 98 feb 14 10:26 vlan100.network
-rw-r--r-- 1 root root 108 feb 14 10:33 vlan2.netdev
-rw-r--r-- 1 root root 90 feb 14 10:23 vlan2.network
-rw-r--r-- 1 root root 108 feb 14 10:33 vlan3.netdev
-rw-r--r-- 1 root root 90 feb 14 10:28 vlan3.network
-rw-r--r-- 1 root root 108 feb 14 10:32 vlan6.netdev
-rw-r--r-- 1 root root 90 feb 14 10:31 vlan6.network
-rw-r--r-- 1 root root 38 feb 14 08:50 vSwitch100.netdev
-rw-r--r-- 1 root root 454 feb 14 10:25 vSwitch100.network
-rw-r--r-- 1 root root 159 feb 14 10:23 vSwitch2.netdev
-rw-r--r-- 1 root root 184 feb 14 10:29 vSwitch2.network
-rw-r--r-- 1 root root 159 feb 14 10:31 vSwitch3.netdev
-rw-r--r-- 1 root root 184 feb 14 10:29 vSwitch3.network
-rw-r--r-- 1 root root 163 feb 14 10:30 vSwitch6.netdev
-rw-r--r-- 1 root root 184 feb 14 10:30 vSwitch6.network
------------ eth0.network ---------------
#
# Interfaz principal de la VM
#
[Match]
Name=eth0
[Network]
VLAN=vlan2
VLAN=vlan3
VLAN=vlan6
VLAN=vlan100
------------ vlan100.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 100
#
[NetDev]
Name=vlan100
Kind=vlan
[VLAN]
Id=100
------------ vlan100.network ---------------
#
# Conecto la vlan100 al bridge "vSwitch100"
#
[Match]
Name=vlan100
[Network]
Bridge=vSwitch100
------------ vlan2.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 2
#
[NetDev]
Name=vlan2
Kind=vlan
[VLAN]
Id=2
------------ vlan2.network ---------------
#
# Conecto la vlan2 al bridge "vSwitch2"
#
[Match]
Name=vlan2
[Network]
Bridge=vSwitch2
------------ vlan3.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 3
#
[NetDev]
Name=vlan3
Kind=vlan
[VLAN]
Id=3
------------ vlan3.network ---------------
#
# Conecto la vlan2 al bridge "vSwitch3"
#
[Match]
Name=vlan3
[Network]
Bridge=vSwitch3
------------ vlan6.netdev ---------------
#
# Dispositivo de Red Virtual (netdev) para definir la VLAN 6
#
[NetDev]
Name=vlan6
Kind=vlan
[VLAN]
Id=6
------------ vlan6.network ---------------
#
# Conecto la vlan2 al bridge "vSwitch6"
#
[Match]
Name=vlan6
[Network]
Bridge=vSwitch6
------------ vSwitch100.netdev ---------------
[NetDev]
Name=vSwitch100
Kind=bridge
------------ vSwitch100.network ---------------
#
# Para poder conmutar trafico en el Switch100 es necesario crear
# el fichero vSwitch100.network (ademas de vSwitch100.netdev)
# y hacer un Match con el vSwitch100
#
[Match]
Name=vSwitch100
# En el caso del Switch100 necesito tener una dirección IP. Este equipo
# va a tener una IP fija en el bridge virtual "vSwitch100" (al que añadiré
# la VLAN100).
#
[Network]
Address=192.168.105.222/24
DNS=192.168.105.224
Gateway=192.168.105.1
IPForward=yes
------------ vSwitch2.netdev ---------------
#
# Bridge Virtual al que asigno el nombre "vSwitch2" y que utilizaré
# para tener acceso al servicio IPTV de Movistar
#
[NetDev]
Name=vSwitch2
Kind=bridge
------------ vSwitch2.network ---------------
#
# Para poder conmutar trafico en el Switch2 es necesario crear
# el fichero vSwitch2.network (ademas de vSwitch2.netdev)
# y hacer un Match con el vSwitch2
#
[Match]
Name=vSwitch2
------------ vSwitch3.netdev ---------------
#
# Bridge Virtual al que asigno el nombre "vSwitch3" y que utilizaré
# para tener acceso al servicio VoIP de Movistar
#
[NetDev]
Name=vSwitch3
Kind=bridge
------------ vSwitch3.network ---------------
#
# Para poder conmutar trafico en el Switch3 es necesario crear
# el fichero vSwitch3.network (ademas de vSwitch3.netdev)
# y hacer un Match con el vSwitch3
#
[Match]
Name=vSwitch3
------------ vSwitch6.netdev ---------------
#
# Bridge Virtual al que asigno el nombre "vSwitch6" y que utilizaré
# para tener acceso al servicio de datos de Movistar
#
[NetDev]
Name=vSwitch6
Kind=bridge
------------ vSwitch6.network ---------------
#
# Para poder conmutar trafico en el Switch6 es necesario crear
# el fichero vSwitch6.network (ademas de vSwitch6.netdev)
# y hacer un Match con el vSwitch6
#
[Match]
Name=vSwitch6