Un ejemplo práctico

Después de instalar KVM i Open vSwitch y ver los comandos básicos para la creación/modificación de bridges/puertos/bondings, vamos a configurar la red de nuestros hosts de KVM, los cuales tienen 53 VLANS diferentes, para este ejemplo utilizaremos 2 hosts físicos, para ver la forma de crear VLANs de forma que puedan ser vistas des del switch externo y de esta forma poder llegar a la red privada de la otra máquina. El esquema de red con los switches virtuales y con varias máquinas virtuales en funcionamiento en un momento dado podría ser:

Éste podría ser un ejemplo de proveedor de Iaas, dónde los clientes comparten la red pública y cada uno de ellos puede crearse sus propias redes privadas entre máquinas. En este ejemplo las máquinas VM1, VM2 y VM3 podrian ser de un cliente, las VM4, VM5, VM6 y VM7 de otro, y las VM8 y VM9 de otro más.
Podemos ver como tenemos varias VLAN’s, la 1 será para el management, la 2 será para el storage NFS de las máquinas físicas, y la VLAN 3 será una VLAN pública común a todos los clientes, mientras que las VLANS de la 04 a la 53 nos servirán para las redes privadas entre máquinas virtuales. Ya que éstas redes privadas deberán verse entre hosts físicos, las tendremos que tener precreadas en el switch exterior. A continuación se detalla cada una de las redes y la configuración necesaria que deberemos tener en el switch exterior.

  • Management: VLAN 01. Red para management del host físico, esta interfície no tendrá ni bridges, ni tagging ni bonding así que no intervendrá el Open vSwitch. Será una sola IP por host: 192.168.1.1 y 192.168.1.2. Así pues tendremos que configurar los puertos 1 y 6 del switch exterior con modo acceso y con la VLAN 01.
  • Storage: Red para el storage NFS de imágenes de máquinas virtuales. Debido a la criticidad del storage, tendremos un bonding sobre dos interfícies físicas. A parte aquí nos entra el tráfico taggeado con la VLAN 02 y las máquinas físicas tendrán las IPs 192.168.2.1 y 192.168.2.2. Así pues tendremos que configurar los puertos 2,3,7 y 8 del switch exterior con la VLAN 02 en modo tagged.
  • MV-pública: Red para agregar puertos a las máquinas virtuales, tanto para esta cómo para la MV-privada tendremos un bridge común sobre el que crearemos puertos con diferentes tags. Para tener mejor redundancia de puertos, vamos a hacer un bonding sobre las interfícies eth3 y eth4, en un caso real cada una de las bocas iría a un switch diferente para tener redundancia a nivel de switch. Las máquinas físicas no tendrán ninguna IP de este rango. En este caso tendremos que configurar las bocas 4,5,9 y 10 en modo tagged con las VLANS de la 3 a la 53.
  • MV-privadas: Red para agregar puertos a las máquina virtuales y crear redes privadas entre ellas, al tener varias máquinas físicas necesitaremos que salgan las VLANs del host y es por eso que tendremos que precrear las VLANs de la 4 a la 53 en el switch exterior. Tal y como se ha explicado en el apartado de MV-pública tendremos los puertos 4,5,9 y 10 del switch exterior con las VLANS 3 a la 53.

Creación de bridges

Vamos pues a crear todos los bridges y configurar la red en los hosts, en este post no entraremos a configurar los switches exteriores, supondremos que ya los tenemos configurados como se ha explicado en el apartado anterior.

Management:

Para la IP de management sólo tenemos que añadir la configuración de la IP en el fichero /etc/network/interfaces, en el host 1 quedará de esta forma:

auto eth0
iface eth0 inet static
        address 192.168.1.1
        netmask 255.255.255.0
        gateway 192.168.1.254

En el host 2 quedará:

auto eth0
iface eth0 inet static
        address 192.168.1.2
        netmask 255.255.255.0
        gateway 192.168.1.254

Storage:

Para la red de storage vamos a crear una interfície de bonding (bondst) sobre un bridge (brst), como necesitamos una IP, vamos también a crear un puerto con una interfície física (brstp1) con tagging y la IP. En los dos hosts físicos ejecutamos:
# ovs-vsctl add-br brst
# ovs-vsctl add-bond brst bondst eth1 eth2
# ovs-vsctl add-port brst brstp1 — set interface brstp1 type=internal
#ovs-vsctl set port brstp1 tag=2
Añadimos la IP a la interfície brstp1 del bridge con el openvswitch y configuramos las interfícies eth1 y eth2 para que se levanten al arrancar.
En el nodo 1 añadimos lo siguiente en el /etc/network/interfaces:

auto brstp1
iface brstp1 inet static
address 192.168.2.1
netmask 255.255.255.0
auto eth1
iface eth1 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down
auto eth2
iface eth2 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down

En el nodo 2 quedará:

auto brstp1
iface brstp1 inet static
address 192.168.2.2
netmask 255.255.255.0
auto eth1
iface eth1 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down
auto eth2
iface eth2 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down

Algunos problemas con los que me he encontrado con esta solución, son que al añadir el punto de montaje en el /etc/fstab la máquina no arranca porque se intenta montar antes de que el servicio del openvswitch se haya levantado. Una solución temporal es añadir noauto a las opciones del /etc/fstab y arrancarlo des del /etc/rc.local, si hubiera servicios que dependieran de este punto de montaje no seria una solución válida, pero como no es el caso, lo dejamos así.
La linea correspondiente al /etc/fstab podría ser como:

192.168.2.50:/vol/mvs   /mvs                  nfs     noauto 0 0

Y añadiríamos en el /etc/rc.local:

mount /mvs

MV-Pública y MV-privadas:

En este caso, cómo no tendremos ninguna IP asociada en el host, tan solo creamos los bridges y configuramos las interfícies de red eth3 y eth4 para que se levanten al arrancar la máquina.
Creamos el bridge (brmv) con el bonding (bondmv) en los dos hosts:
# ovs-vsctl add-br brmv
# ovs-vsctl add-bond brmv bondmv eth3 eth4
Añadimos en el fichero /etc/network/interfaces de los dos hosts lo siguiente para que las interfícies se levanten al arrancar:

auto eth3
iface eth3 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down
auto eth4
iface eth4 inet manual
up ifconfig $IFACE 0.0.0.0 up
down ifconfig $IFACE down

Una vez llegados a este punto ya tenemos toda la red configurada, tan sólo nos queda hacer un reboot de los hosts, ver que se ha configurado todo correctamente y empezar a crear máquinas virtuales.

Creación de máquinas virtuales

Para ver el procedimiento de creación de máquinas virtuales y para comprobar que todo esta correctamente, vamos a crear unas maquinas virtuales con la misma VLAN en diferentes hosts y vamos a comprobar que se ven entre ellas, también vamos a comprobar que no se mezcla el tráfico entre VLANs.
Vamos a suponer que tenemos dos clientes, cada uno con dos máquinas virtuales, con una IP privada cada una, cómo es lógico no queremos que las máquinas entre clientes puedan verse el tráfico, así que la VLAN privada va a tener un tag diferente para cada cliente:

  • Cliente1:
  • MV1
  • Host físico: host1
  • Redes:
    • privada: VLAN 4
  • MV2
  • Host físico: host2
  • Redes:
    • privada: VLAN 4
  • Cliente2:
  • MV3
  • Host físico: host1
  • Redes:
    • privada: VLAN 5
  • MV2
  • Host físico: host2
  • Redes:
    • privada: VLAN 5

Supondremos que ya tenemos creadas imágenes de disco con una Ubuntu instalada. Así creamos el xml de creación de la MV1 para libvirt (no se pone el xml de todas las máquina ya que serán muy parecidos adaptando el nombre, la imágen y la mac. Lógicamente el bridge donde se conectaran será para todos el mismo, cambiando posteriormente el tag de cada puerto):

# cd /mvs
# cat mv1.xml
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
        <name>mv1</name>
        <cputune>
                <shares>1024</shares>
        </cputune>
        <memory>524288</memory>
        <os>
                <type arch='x86_64'>hvm</type>
                <boot dev='hd'/>
        </os>
        <devices>
                <emulator>/usr/bin/kvm</emulator>
                <disk type='file' device='disk'>
                        <source file='/mvs/disk.mv1'/>
                        <target dev='hda'/>
                        <driver name='qemu' type='qcow2' cache='default'/>
                </disk>
                <interface type='bridge'>
                        <source bridge='br32'/>
                        <mac address='00:00:00:00:00:01'/>
                         <model type='virtio'/>
                </interface>
         </devices>
        <features>
                <acpi/>
        </features>
</domain>

Creamos las mv’s en el host1:

# virsh create mv1.xml
# virsh create mv3.xml

Creamos las mv’s en el host2:

# virsh create mv2.xml
# virsh create mv4.xml

una vez creadas las máquinas vamos a ver que puertos les ha asignado a cada uno, en el host1 ejecutamos:

# virsh dumpxml mv1 | grep vnet
<target dev='vnet0'/>
# virsh dumpxml mv3 | grep vnet
<target dev='vnet1'/>

Vamos al host 2:

# virsh dumpxml mv2 | grep vnet
<target dev='vnet0'/>
# virsh dumpxml mv4 | grep vnet
<target dev='vnet1'/>

Visto esto vamos a asignar la VLAN correspondiente a cada uno de los puertos, empezamos con el host 1:

# ovs-vsctl set port vnet0 tag=4
# ovs-vsctl set port vnet1 tag=5

Lo mismo en el host 2:

# ovs-vsctl set port vnet0 tag=4
# ovs-vsctl set port vnet1 tag=5

Comprobaciones finales

Vamos a comprobar que se ven entre las máquinas de cliente y que no se ven las máquinas de diferentes clientes.
Para empezar asignamos una dirección IP a cada una de ellas:
MV 1: 192.168.1.1
MV 2: 192.168.1.2
MV 3: 192.168.1.3
MV 4: 192.168.1.4
Hemos puesto IPs del mismo rango, pero no tendrían que verse entre las máquinas de diferentes clientes, ya que les hemos asignado diferente VLAN. Vamos a comprobarlo, entramos en la MV1:

mv1:~# ping -c4 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_req=1 ttl=64 time=0.187 ms
64 bytes from 192.168.1.2: icmp_req=2 ttl=64 time=0.193 ms
64 bytes from 192.168.1.2: icmp_req=3 ttl=64 time=0.191 ms
64 bytes from 192.168.1.2: icmp_req=4 ttl=64 time=0.205 ms
--- 192.168.1.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2997ms
rtt min/avg/max/mdev = 0.187/0.194/0.205/0.006 ms

Vemos como si que se ven las máquinas mv1 y la mv2, vamos ahora a comprobar que no se ven la mv1 con la mv3

mv1:~# ping -c4 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
From 192.168.1.1 icmp_seq=1 Destination Host Unreachable
From 192.168.1.1 icmp_seq=2 Destination Host Unreachable
From 192.168.1.1 icmp_seq=3 Destination Host Unreachable
From 192.168.1.1 icmp_seq=4 Destination Host Unreachable
--- 192.168.1.3 ping statistics ---
4 packets transmitted, 0 received, +4 errors, 100% packet loss, time 3011ms

Eso es todo por hoy. En posts futuros veremos cómo fijar las MACs de las máquinas virtuales, aplicar reglas de firewalling, QoS,…
Un saludo post navideño! 😉