Erste Erfahrungen mit Open-vSwitch

Vor kurzem habe ich Debian 8 (Jessie) auf meiner Entwicklermaschine, einem HP ProLiant DL380G5, installiert. Als Basissystem habe ich mich dieses Mal für Debian (stable) entschieden, obwohl ich ja sonst fast ausschließlich unter Gentoo arbeite. Das System macht einen sehr guten Eindruck. Interessiert hat mich hierbei am meisten, wie die Integration von systemd unter Debian gelungen ist, nachdem ich mich damit auf einer Gentoo-Maschine schon seit etlichen Monaten beschäftigt habe.

Was mir dabei gut gefällt ist, dass die gewohnte Netzwerkkonfiguration unter /etc/network/interfaces noch immer so genutzt wird. Auf diese Weise konnte ich recht schnell eine Bridge konfigurieren, in der eine meiner externen Schnittstellen mit integriert ist (ich liebe diese Art, weil dann jede VM direkten Zugang zum Netzwerk hat).

Das funktionierte sehr gut. Nach einiger Zeit war klar, dass ich gerne eine Gentoo-Clusterlösung als Testsystem auf dem Server betreiben wollte (wir haben ein Produktivsystem und dieses neue Testsystem soll einfach unser Staging für Updates sicherer machen).

Jetzt ist es so, dass dieser Cluster Bonding und etliche VLANs konfiguriert hat und nun war klar, dass der Weg mit einer Bridge nicht unbedingt optimal dafür ist. Also habe ich mich entschieden, Open-vSwitch auf der Entwicklermaschine aufzubauen. Mit Erfolg.

Im Wesentlichen zeige ich einmal die Konfiguration der Debian-interfaces-Datei und ein Beispiel einer XML-Konfiguration zu einem der beiden Clusterknoten. Zum Schluss, wie das Ganze dann unter Gentoo auf dem ersten Knoten genutzt wird.

Mein Fokus liegt klar auf den VLANs. Zu Bonding bin ich noch nicht weiter tiefer eingestiegen. Wer dazu Ideen und Anregungen hat, gerne in den Kommentaren erwähnen 🙂

Debian interfaces

allow-ovs ovsbr0
iface ovsbr0 inet static
    address 172.16.2.244
    netmask 255.255.255.0
    network 172.16.2.0
    broadcast 172.16.2.255
    gateway 172.16.2.1
    ovs_type OVSBridge
    ovs_ports eth0 eth1 vlan108
    # HP Switch (Simulation)
    post-up /bin/ip addr add 192.168.2.254/24 brd + dev ${IFACE}
    pre-down /bin/ip addr del 192.168.2.254/24 brd + dev ${IFACE}

allow-ovsbr0 eth0
iface eth0 inet manual
    ovs_bridge ovsbr0
    ovs_type OVSPort

allow-ovsbr0 eth1
iface eth1 inet manual
    ovs_bridge ovsbr0
    ovs_type OVSPort
    ovs_options tag=109

allow-ovs ovsbr1
iface ovsbr1 inet manual
    ovs_type OVSBridge
    post-up /bin/ip link set dev ${IFACE} up
    pre-down /bin/ip link set dev ${IFACE} down

#
# VLANs
#

# iQom testing
allow-ovsbr0 vlan108
iface vlan108 inet static
    address 217.199.204.9
    netmask 255.255.255.248
    network 217.199.204.8
    broadcast 217.199.204.15
    ovs_bridge ovsbr0
    ovs_type OVSIntPort
    ovs_options tag=108
    ovs_extra set interface ${IFACE} external-ids:iface-id=$(hostname -s)

Das Beispiel definiert einen Switch namens ovsbr0. In diesem Switch hängen beide externen Schnittstellen des Servers so wie einVLAN-Device. Der Switch hat selbst eine IP und zusätzlich habe ich noch ein weiteres Netz hinzugefügt, was für den Testcluster selbst eine Relevanz hat.

Die beiden physischen Schnittstellen sind nicht im selben VLAN! eth1 habe ich bewusst in VLAN 109 untagged gepackt. Ich nutze das, um mein Laptop daran anzuschließen und bestimmte Kommunikation mit dem Cluster zu testen.

Weiterhin gibt es ein virtuelles VLAN mit der ID 108. Das nutze ich, um aus Sicht des Test-Clusters eine ansprechbare IP „extern“ zu haben. Heißt so viel wie: Ich kann vom Cluster aus eine IP auf dem VLAN 108 pingen und somit ebenfalls eine bestimmte Kommunikation simulieren.

libvirt XML Konfiguration einer der Knoten

    
      
      
      
        
        
        
        
        
        
        
        
        
        
      
      
        
      
      
      

Das sieht auf den ersten Moment schlimm aus. Zerlegen wir das mal:

Der Server hat vier Netzwerkkarten. Jeweils zwei davon bilden ein Bond-Gerät. Das eine Device wird für die interne Clusterkommunikation (corosync, pacemaker, DRBD, …) benutzt, das andere für eine Anbindung an den Switch mit all seinen VLANs. Jetzt kommt vielleicht schnell die Frage auf, wo denn „all die VLANs“ oben definiert wurden? Nirgends. Das geschieht zur Laufzeit des Gastes. Freundlicherweise fügt libvirt alles Notwendige direkt in open-vSwitch hinzu und wir brauchen uns um nichts weiter zu kümmern.

Alle hier gezeigten VLANs sind tagged Ports. D.h. dass der Cluster selbst 802.1Q sprechen muss, was gleich gezeigt wird.

Zuerst zeige ich mal kurz die Switchkonfiguration vor dem Start der MS und dann danach

Vor dem Start

root@dev  ~ # ovs-vsctl show
e114dfa1-5ac3-4d92-9590-f92965b16a4e
    Bridge "ovsbr1"
        Port "ovsbr1"
            Interface "ovsbr1"
                type: internal
    Bridge "ovsbr0"
        Port "eth0"
            Interface "eth0"
        Port "eth1"
            tag: 109
            Interface "eth1"
        Port "vlan108"
            tag: 108
            Interface "vlan108"
                type: internal
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
    ovs_version: "2.3.0"

Nach dem Start

root@dev  ~ # ovs-vsctl show
e114dfa1-5ac3-4d92-9590-f92965b16a4e
    Bridge "ovsbr1"
        Port "vnet3"
            Interface "vnet3"
        Port "ovsbr1"
            Interface "ovsbr1"
                type: internal
        Port "vnet2"
            Interface "vnet2"
        Port "vnet7"
            Interface "vnet7"
        Port "vnet6"
            Interface "vnet6"
    Bridge "ovsbr0"
        Port "vnet0"
            trunks: [0, 100, 102, 104, 105, 106, 107, 108, 109, 200]
            Interface "vnet0"
        Port "vnet4"
            trunks: [0, 100, 102, 104, 105, 106, 107, 108, 109, 200]
            Interface "vnet4"
        Port "vnet1"
            trunks: [0, 100, 102, 104, 105, 106, 107, 108, 109, 200]
            Interface "vnet1"
        Port "eth0"
            Interface "eth0"
        Port "eth1"
            tag: 109
            Interface "eth1"
        Port "vlan108"
            tag: 108
            Interface "vlan108"
                type: internal
        Port "ovsbr0"
            Interface "ovsbr0"
                type: internal
        Port "vnet5"
            trunks: [0, 100, 102, 104, 105, 106, 107, 108, 109, 200]
            Interface "vnet5"
    ovs_version: "2.3.0"

Wie gewünscht 🙂

Gentoo Netzwerk-Konfiguration unter openrc

dns_domain_lo="testing.example.com"
dns_servers="127.0.0.1"
dns_options="edns0"

rc_net_eth0_provide="!net"
rc_net_eth1_provide="!net"
rc_net_eth2_provide="!net"
rc_net_eth3_provide="!net"

# Internal bonding for Corosync and DRBD
slaves_bond0="eth2 eth3"
mode_bond0="balance-rr"
miimon_bond0="100"
downdelay_bond0="200"
updelay_bond0="100"
config_bond0="192.168.0.1/24 brd +"

# External bonding
slaves_bond1="eth0 eth1"
mode_bond1="active-backup"      # 802.3ad on prod
arp_interval="2000"
arp_ip_target="192.168.2.254"   # HP Switch
config_bond1="
        172.16.2.230/24 brd +
        192.168.2.203/24 brd +
"
metric_bond1="1000"
routes_bond1="default via 172.16.2.1"

vlans_bond1="100 102 104 105 106 107 108 109 200"

config_bond1_100="null"
config_bond1_102="null"
config_bond1_104="null"
config_bond1_105="null"
config_bond1_106="null"
config_bond1_107="null"
config_bond1_108="null"
config_bond1_109="10.1.100.1/16 brd +"
config_bond1_200="null"

Viel lässt sich dazu jetzt nicht sagen, denn das ist Cluster-spezifisch. Ich zeige nur kurz, was die Ausgabe von IP so zeigt (der Cluster ist im Beispiel auf beiden Knoten im Standby)

root@node0  /etc/conf.d # ip -4 a l
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default
    inet 127.0.0.1/8 brd 127.255.255.255 scope host lo
       valid_lft forever preferred_lft forever
7: bond0:  mtu 1500 qdisc noqueue state UP group default
    inet 192.168.0.1/24 brd 192.168.0.255 scope global bond0
       valid_lft forever preferred_lft forever
8: bond1:  mtu 1500 qdisc noqueue state UP group default
    inet 172.16.2.230/24 brd 172.16.2.255 scope global bond1
       valid_lft forever preferred_lft forever
    inet 192.168.2.203/24 brd 192.168.2.255 scope global bond1
       valid_lft forever preferred_lft forever
16: bond1.109@bond1:  mtu 1500 qdisc noqueue state UP group default
    inet 10.1.100.1/16 brd 10.1.255.255 scope global bond1.109
       valid_lft forever preferred_lft forever

Die Linkliste

 root@node0  /etc/conf.d # ip l l
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: sit0@NONE:  mtu 1480 qdisc noop state DOWN mode DEFAULT group default
    link/sit 0.0.0.0 brd 0.0.0.0
3: eth0:  mtu 1500 qdisc pfifo_fast master bond1 state UP mode DEFAULT group default qlen 1000
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
4: eth1:  mtu 1500 qdisc pfifo_fast master bond1 state UP mode DEFAULT group default qlen 1000
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
5: eth2:  mtu 1500 qdisc pfifo_fast master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether 54:52:00:47:08:bf brd ff:ff:ff:ff:ff:ff
6: eth3:  mtu 1500 qdisc pfifo_fast master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether 54:52:00:47:08:bf brd ff:ff:ff:ff:ff:ff
7: bond0:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:47:08:bf brd ff:ff:ff:ff:ff:ff
8: bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
9: bond1.100@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
10: bond1.102@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
11: bond1.104@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
12: bond1.105@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
13: bond1.106@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
14: bond1.107@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
15: bond1.108@bond1:  mtu 1500 qdisc hfsc state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
16: bond1.109@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
17: bond1.200@bond1:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 54:52:00:40:e6:04 brd ff:ff:ff:ff:ff:ff
18: ifb0:  mtu 1500 qdisc hfsc state UNKNOWN mode DEFAULT group default qlen 32
    link/ether 4e:52:af:b2:b3:e3 brd ff:ff:ff:ff:ff:ff

Fazit

Mit Hilfe von Open-vSwitch kann ich sehr realistische Setups virtuell nachbauen, was Kosten und Ressourcen an Hardware spart.

Eine kleine Anmerkung habe ich noch:

Sowohl unter Debian (zum Zeitpunkt dieses Artikels) als auch unter Gentoo (mit systemd!) kann es vorkommen, dass der Switch während des Bodens noch nicht hundert prozentig verfügbar ist. Das ist noch ein bisschen nervig, aber unproblematisch. Ich habe einfach alle VMs auf der Testmaschine aus dem Autostart genommen. Bei Gentoo klappt das, aber gelegentlich kommt das ovsbr0 selbst nicht hoch. Dann sind zwar alle Gäste voll funktional vorhanden und erreichbar, aber die Kommunikation zwischen der physischen Maschine und den Gästen klappt nicht. Ich nutze hier netctl, um mein Netzwerk zu konfigurieren und ein


netctl start ovsbr0

nach einem Neustart behebt dann umgehend das Problem. Wer eine Firewall wie Shorewall auf einer physischen Maschine nutzt, kann hier auch einen Trick anwenden, damit der Server dann nicht unerreichbar ist, wenn das ovsbr0 nicht gleich hoch kam:

wan		ovsbr0			tcpflags,nosmurfs,routeback,optional

Das Schlüsselwort „optional“ erlaubt das Nichtvorhandensein einer Netzwerkschnittstelle zur Startzeit von Shorewall.

Have fun…