OpenVSwitch

Набор утилит для построения виртуальных сетей поверх уже существующих L3 сетей. В целом удобен, в частностях кое-где непонятен и пугающ, но идеальных вещей не бывает, так что будем есть, что дают.

Общая информация

Состоит из двух частей - OVS-сервера и OVS-базы данных. Так же для корректной работы требует наличие в ядре определённого модуля. Для CentOS есть уже готовый пакет со всеми свистелками, который позволяет не задумываться о бэкграунде сборки своего собственного пакета и ядра.

Работа с бриджами

Перед тем, как начать работать с бриджами, нужно их конечно же создать (естественно перед тем, как работать с openvswitch его для начала надо запустить):

# ovs-vsctl add-br br-int

После этого всё достаточно просто. Добавляем порты и получаем L2 связность внутри бриджа

# ovs-vsctl add-port br-int eth0
# ovs-vsctl add-port br-int eth1

Как простой пример - хорошо, однако для нас это не интересно и теперь мы попробуем создать два бриджа и соединить их кроссом:

# ovs-vsctl add-br br-int
# ovs-vsctl add-br br-tun
# ovs-vsctl add-port br-int patch-tun -- set interface patch-tun type=patch options:peer=patch-int
# ovs-vsctl add-port br-tun patch-int -- set interface patch-int type=patch options:peer=patch-tun

В результате выполнения команд мы получаем два бриджа, связанных между собой по L2 портами patch-tun и patch-int.

# ovs-vsctl show

Bridge: br-int
  Port: patch-tun
    Interface: patch-tun
      type: patch
      options: {peer=patch-int}
Bridge: br-tun
  Port: patch-int
    Interface: patch-int
      type: patch
      options: {peer=patch-tun}

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

Работа с портами

Как выше мы успели познакомиться в OpenVSwitch существует некоторое количество типов портов, которые служат для тех или иных целей.

  • Порт без типа - обычный порт в системе
  • type=patch - тип порта, использующийся для связывания бриджей
  • type=vxlan - тип порта, предназначенный для построения тоннелей между серверами по vxlan
  • type=gre - тип порта, предназначенный для построения тоннелей между серверами по gre

Для создания туннеля между двумя серверами требуется создать специальные интерфейсы и указать адрес соседнего узла.

(nsw1)# ovs-vsctl add-port br-tun vxlan-nsw2 -- set interface vxlan-nsw2 type=vxlan options:remote_ip=10.0.1.2
(nsw2)# ovs-vsctl add-port br-tun vxlan-nsw1 -- set interface vxlan-nsw1 type=vxlan options:remote_ip=10.0.1.1

После этого в списке интерфейсов br-tun должен появиться интерфейс с типом vxlan:

(nsw1)# ovs-vsctl show
...
Bridge br-tun
  Port "vxlan-nsw2"
    type: "vxlan"
    options: {remote_ip="10.0.1.2"}
...

(nsw2)# ovs-vsctl show
...
Bridge br-tun
  Port "vxlan-nsw1"
    type: "vxlan"
    options: {remote_ip="10.0.1.1"}
...

Связываем два сервера через OpenVSwitch

Комбинируя описанные в предыдущих двух частях знания получаем вполне рабочий L2 поверх L3. Мы будем использовать вместо реально существующих интерфейсов network namespaces

(nsw1)# ovs-vsctl add-br br-int
(nsw1)# ovs-vsctl add-br br-tun
(nsw1)# ovs-vsctl add-port br-int patch-tun -- set interface patch-tun type=patch options:peer=patch-int
(nsw1)# ovs-vsctl add-port br-tun patch-int -- set interface patch-int type=patch options:peer=patch-tun
(nsw1)# ip link add name veth1 type veth peer name int-veth1
(nsw1)# ip netns add nsw1-ns
(nsw1)# ip link set dev veth1 netns nsw1-ns
(nsw1)# ovs-vsctl add-port br-int int-veth1
(nsw1)# ip link set up int-veth1
(nsw1)# ip link set up br-int
(nsw1)# ip netns exec nsw1-ns ip link set up veth1
(nsw1)# ip netns exec nsw1-ns ip a add 192.168.0.1/24 dev veth1
(nsw1)# ovs-vsctl add-port br-tun tun-nsw2 -- set interface tun-nsw2 type=vxlan options:remote_ip=10.0.1.2

(nsw2)# ovs-vsctl add-br br-int
(nsw2)# ovs-vsctl add-br br-tun
(nsw2)# ovs-vsctl add-port br-int patch-tun -- set interface patch-tun type=patch options:peer=patch-int
(nsw2)# ovs-vsctl add-port br-tun patch-int -- set interface patch-int type=patch options:peer=patch-tun
(nsw2)# ip link add name veth1 type veth peer name int-veth1
(nsw2)# ip netns add nsw2-ns
(nsw2)# ip link set dev veth1 netns nsw2-ns
(nsw2)# ovs-vsctl add-port br-int int-veth1
(nsw2)# ip link set up int-veth1
(nsw2)# ip link set up br-int
(nsw2)# ip netns exec nsw2-ns ip link set up veth1
(nsw2)# ip netns exec nsw2-ns ip a add 192.168.0.2/24 dev veth1
(nsw2)# ovs-vsctl add-port br-tun tun-nsw1 -- set interface tun-nsw1 type=vxlan options:remote_ip=10.0.1.1

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

(nsw1)# ip netns exec nsw1-ns ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.978 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.320 ms

(nsw2)# ip netns exec nsw2-ns ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.632 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.303 ms

В итоге мы получили изолированную сеть.

Разграничение трафика внутри OVS

Для разграничения трафика можно использовать обычные VLAN'ы. Для этого при добавлении порта в бридж надо так же указать его tag с номером vlan'a. Соответственно этот tag должен быть одинаков на всех гипервизорах для машин, которые должны находиться в одном L2 домене.

(nsw1)# ovs-vsctl del-port br-int int-veth1
(nsw1)# ovs-vsctl add-port br-int int-veth1 tag=10
(nsw1)# ovs-vsctl add-port br-int int-veth2 tag=20

(nsw2)# ovs-vsctl del-port br-int int-veth1
(nsw2)# ovs-vsctl add-port br-int int-veth1 tag=10
(nsw2)# ovs-vsctl add-port br-int int-veth2 tag=20

Теперь порты veth1 будут видеть друг друга в 10-ом vlan'е, а порты veth2 в 20-ом vlan'e, однако связности между ними не будет никакой. Всё как в самой настоящей железной сети. По сути тэги можно считать как указание native vlan для порта.