Translate

неділю, 13 грудня 2015 р.

LXC Сontainers. Part II: Network Configuration

У попередній статті я описав, що таке LXC та чому ця технологія може вас зацікавити.

Перед прочитанням тексту нижче, бажано звісно розуміти базові речі про LXC чи просто про контейнеризацію, тому дуже раджу хоча б переглянути статтю, згадану вище.
У цій статті спробуємо розібратись які можливості надає LXC для конфігурації мережевого стеку та і що власне ОС Linux може запропонувати для цього.

Конфігурація з'єднання з мережею описується окремо для кожного контейнера в /var/lib/lxc/<container_name>/config. По замовчуванню, мережевий зв’язок для контейнерів LXC налаштовується за рахунок додаткової приватної мережі 10.0.3.0/24, що NAT-иться на хост машині: всі пакети, що приходять на інтерфейс lxcbr0 (10.0.3.1, gateway) за допомогою маскарадингу динамічно транслюються в публічний айпі і навпаки. Видає IP-адреси dnsmasq, що працює на LXC-хості, а діапазон можливих адрес описаний в /etc/default/lxc-net. На практиці це виглядає так:

# vim /etc/default/lxc-net
...
USE_LXC_BRIDGE="true"

LXC_BRIDGE="lxcbr0"
LXC_ADDR="10.0.3.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="10.0.3.0/24"
LXC_DHCP_RANGE="10.0.3.2,10.0.3.254"
LXC_DHCP_MAX="253"
...

Налаштування Iptables на хості, одразу після установки lxc, мають такий вигляд:

# iptables-save
...
-A POSTROUTING -s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE
...
COMMIT


Конфігурація мережі кожного наступного контейнера буде створена виходячи з параметрів описаних у цьому файлі /etc/default/lxc-net. Тобто призначатись IP будуть з цього ж діапазону, шлюзом буде та ж адреса і т.п.

Якщо мережа 10.0.3.0/24 на момент установки lxc буде чимось зайнята, ймовірно, буде обраний інший діапазон приватних IP.

Насправді для тестування чогось на localhost цього більше ніж досить. Відповідно з такою конфігурацією, контейнер матиме доступ як до приватної мережі цього ж діапазону, так і до інтернету. Якщо необхідно зробити доступним лише певний порт контейнера, то на хосту необхідно виконати щось на зразок:

# iptables -t nat -A PREROUTING -p tcp -i lxcbr0 --dport 587 -j DNAT --to-destination 10.0.3.XXX:587

10.0.3.XXX - адреса контейнеру

Таким чином запити на 587 порт хост-машини будуть переадресовані на 587 порт контейнеру. Далі поговоримо про те, які є інші варіанти організації мережі в контейнерах. До речі, ця інформація буде корисна для інших типів віртуалізацій, щось на зразок KVM, XEN, OpenVZ та ін.

LXC підтримує такі типи віртуалізації мережевих з’єднань для контейнерів (man lxc.container.conf):

  • empty
  • veth
  • macvlan
  • vlan
  • phys

Поговоримо про кожен із них по черзі.

EMPTY


Empty - це тип мережевого з’єднання для контейнера, котрий не під’єднаний до жодної мережі. У такого контейнера присутній лише loopback інтерфейс з 127.0.0.1.
Продемонструємо його створення та роботу:

# lxc-create -t ubuntu -n empty01 -- -r trusty

В результаті, за допомогою пакету debootstrap буде завантажено та встановлено Ubuntu Trusty в директорію /var/lib/lxc хост-машини та виконані додаткові інструкції, що описані в темплейт-скрипті /usr/share/lxc/templates/lxc-ubuntu.

Встановимо empty режим для контейнера:

# vim /var/lib/lxc/empty01/config
...
# Network configuration
lxc.network.type = empty
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:06:21:6d 

Тепер можна запустити контейнер:

# lxc-start -n empty01

# lxc-ls -f
NAME             STATE    IPV4  IPV6  GROUPS  AUTOSTART
-------------------------------------------------------
empty01          RUNNING  -     -     -       NO

# lxc-attach -n empty01 -- ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Контейнер працює, проте з інтерфейсів піднято лише lo інтерфейс, контейнер не підключено до жодної мережі. Переходити в нього в цьому разі можна лише за допомогою lxc-attach/lxc-console. Такий тип контейнера може знадобитись для задач, що можна виконати без мережевого доступу: запуск тестових інсансів програм і т.п.

VETH


Це віртуальне марежеве Ethernet з’єднання, одна частина якого під’єднана до контейнера, а інша ввімкнена в мережевий міст, що описаний опцією lxc.network.link.

Для демонстрації особливостей veth з’єднання створимо новий контейнер:

# lxc-create -t ubuntu -n veth01 -- -r trusty

Вкажемо контейнеру, тип мережі veth та що необхідно використовувати у якості мережевого мосту.

# vim /var/lib/lxc/veth01/config

# Network configuration
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:71:e1:cf

Мережевий інтерфейс контейнера використовує інтерфейс хост-машини (lxcbr0) для роботи в мережі, тобто використовує Ethernet протокол (2 рівень моделі OSI, L2 Switch) для обміну даними з мережевим мостом lxcbr0 та eth0 інтерфейсом контейнера. Всі пакети протоколів більш високих рівнів будуть прозоро проходити через мережевий міст. Також контейнер буде підключений в загальну мережу, в якій знаходиться і сама хост машина.

Схематично це виглядатиме так:
Стартуємо контейнер та переглянемо налаштування мережі:

# lxc-start --name veth01

# lxc-ls -f
NAME             STATE    IPV4         IPV6                                 GROUPS  AUTOSTART
---------------------------------------------------------------------------------------------
empty01          RUNNING  -            -                                    -       NO
veth01           RUNNING  10.0.3.110   fdee:cbcd:a595:0:216:3eff:fe71:e1cf  -       NO

Це власне налаштування, які надає LXC по-замовчуванню, одразу після установки. Я писав про них з самого початку статті. Як я вже згадував, IP-адресу контейнеру виділить dnsmasq хост-машини, а сама хост машина буде мати 10.0.3.1 (gateway) на lxcbr інтерфейсі:

# netstat -ntlp | grep 53
tcp        0      0 10.0.3.1:53             0.0.0.0:*               LISTEN      1275/dnsmasq

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

# ping -c 2 10.0.3.110
PING 10.0.3.110 (10.0.3.110) 56(84) bytes of data.
64 bytes from 10.0.3.110: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 10.0.3.110: icmp_seq=2 ttl=64 time=0.046 ms

--- 10.0.3.110 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.046/0.049/0.053/0.007 ms

Та пінг з контейнера:

# lxc-attach -n veth01 -- ping -c 2 10.0.3.1
PING 10.0.3.1 (10.0.3.1) 56(84) bytes of data.
64 bytes from 10.0.3.1: icmp_seq=1 ttl=64 time=0.408 ms
64 bytes from 10.0.3.1: icmp_seq=2 ttl=64 time=0.408 ms

--- 10.0.3.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.408/0.408/0.408/0.000 ms

# lxc-attach -n veth01 -- route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.3.1        0.0.0.0         UG    0      0        0 eth0
10.0.3.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0

На хост-машині можна побачити, який проміжний інтерфейс використовується для бріджингу з lxcbr0:

# brctl show
bridge name         bridge id             STP enabled     interfaces
lxcbr0              8000.f46d04156f60     no              vethZLR1Q8

При створенні кожної наступної віртуальної машини з використанням того ж інтерфейсу у якості мережевого мосту, буде створюватись подібний інтерфейс veth*, котрий буде включений в цей же бридж (в даному випадку lxcbr0).

Iptables за необхідністю може бути використаний задля маскарадингу пакетів в зовнішню мережу, якщо доступ до неї необхідний:

# iptables -t nat -A POSTROUTING -s 10.0.3.0/24 ! -d 10.0.3.0/24 -j MASQUERADE

Можна також заздалегіть указати які адреси будуть призначатись на контейнери. Для цього необхідно розкоментувати змінну LXC_DHCP_CONFILE в /etc/default/lxc-net:

# vim /etc/default/lxc-net
...
LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf

Описати файл-список доменів та їх адрес в /etc/lxc/dnsmasq.conf:

# vim /etc/lxc/dnsmasq.conf
...
dhcp-hostsfile=/etc/lxc/dnsmasq-hosts.conf
...

Ну і, нарешті, описати які саме хости які адреси мають отримувати:

# vim /etc/lxc/dnsmasq-hosts.conf

mail,10.0.3.16
web,10.0.3.17

Перевантажуємо lxc-net та dnsmasq:

# service lxc-net restart
# killall -s SIGHUP dnsmasq

При додаванні кожного нового хосту в dnsmasq-hosts.conf lxc-net перевантажувати не треба, а лише dnsmasq.

За допомогою veth-з’єднання, контейнер також можна напряму підключити в публічну мережу, в котрій працює сам хост. Тобто в такому разі, з першого погляду, контейнер буде рівноцінним учасником мережі, як і хост-машина. Для цього необхідно фізичний інтерфейс хост-машини включити в мережевий міст, який відповіно буде ділити з’єднання поміж іншими veth* інтерфейсами, котрі є проміжним мережевим інтерфейсом для кожного контейнера в цій мережі.

Розберемось з цим на прикладі. 192.168.1.41 - IP хост-машини, а контейнер спробуємо підключити в цю ж мережу (/24) з IP-адресою - 192.168.1.42. Мережу обслуговує звичайний домашній роутер - 192.168.1.1.
Створимо бридж-інтерфейс lxcbr1 до eth0. Для реалізації цього будемо користуватись стандартним мережевим свічем bridge з пакету bridge-utils.

# cat /etc/network/interfaces
...
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet manual

auto lxcbr1
iface lxcbr1 inet static
        address 192.168.1.41
        netmask 255.255.255.0
        gateway 192.168.1.1
        dns-nameservers 8.8.8.8
        bridge_ports eth0
        bridge_stp off
        bridge_fd 0
        bridge_maxwait 0

Ім’я бриджа, теоретично, можна обрати будь-яким.
Змінимо оригінальні налаштування конфігураційного файлу lxc-net та default.conf і вкажемо використання нового бриджа для нових контейнерів по-замовчуванню:

# cp /etc/default/lxc-net /etc/default/lxc-net_orig
# vim /etc/default/lxc-net
...
USE_LXC_BRIDGE="false"
...

# vim /etc/lxc/default.conf
lxc.network.type = veth
lxc.network.link = lxcbr1
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:xx:xx:xx

Або ж інакше необхідно буде вказувати після створення кожного контейнера, що необхідно буде використовувати контейнеру у якості мережевого мосту. Якщо просто необхідно указати використання інших підмереж, діапазонів та ін. USE_LXC_BRIDGE необхідно залишити в true та просто вказати свої нові параметри в lxc-net.

Всі інші параметри мають бути закоментованими.

Після перевантаження/перезапуску мережі на хості увесь трафік буде йти через lxcbr1 інтерфейс.

# ifconfig
eth0      Link encap:Ethernet  HWaddr f4:6d:04:15:6f:60
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3673758 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6417654 errors:0 dropped:0 overruns:0 carrier:2
          collisions:0 txqueuelen:1000
          RX bytes:1000149284 (1.0 GB)  TX bytes:7587683148 (7.5 GB)
...

lxcbr1    Link encap:Ethernet  HWaddr f4:6d:04:15:6f:60
          inet addr:192.168.1.41  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fdee:cbcd:a595:0:f66d:4ff:fe15:6f60/64 Scope:Global
          inet6 addr: fdee:cbcd:a595:0:a519:5d4c:aaf7:4763/64 Scope:Global
          inet6 addr: fe80::f66d:4ff:fe15:6f60/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3673521 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5607536 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:948685073 (948.6 MB)  TX bytes:7541135984 (7.5 GB)

Створимо додатковий veth02 контейнер для демонстрації з’єднання:

# lxc-create -t ubuntu -n veth02 -- -r trusty

За необхідності, відредагуємо мережеві налаштування для veth02:

# vim /var/lib/lxc/veth02/config
...
# Network configuration
lxc.network.type = veth
lxc.network.link = lxcbr1
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:ca:fd:06
lxc.network.ipv4 = 192.168.1.42/24

Якщо не принципово, яка адреса контейнера буде в мережі - можна не вказувати lxc.network.ipv4, проте обов’язково має бути описана назва мережевого мосту для з’єднання з мережею. Відповідно IP-адресу призначить dhcp-сервер (якщо він присутній в мережі). В моєму випадку, 192.168.1.1 - одночасно і шлюз, і dhcp-сервер.

Можна піти трохи іншим шляхом та указувати мережеві параметри одразу в конфігураційному файлі інтерфейсів контейнера:

# cat /var/lib/lxc/veth02/rootfs/etc/network/interfaces
...
# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
address 192.168.1.42
netmask 255.255.255.0
gateway 192.168.1.1
dns-nameservers 8.8.8.8

Проте і в цьому випадку в /var/lib/lxc/veth02/config має бути описано, що буде використовуватись як мережевий міст.

Запустимо контейнер:

# lxc-start -n veth02

Після запуску з’явиться проміжний інтерфейс veth*:

# ifconfig
...
vethLCR2W7 Link encap:Ethernet  HWaddr fe:70:5f:51:ce:d6
          inet6 addr: fe80::fc70:5fff:fe51:ced6/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:51 errors:0 dropped:0 overruns:0 frame:0
          TX packets:46744 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:5228 (5.2 KB)  TX bytes:11160232 (11.1 MB)
...

Переглянемо, що підключено наразі до мережевого мосту lxcbr1:

# brctl show
bridge name         bridge id             STP enabled     interfaces
...
lxcbr1              8000.f50с07436g96     no              eth0
                                                          vethLCR2W7

Переглянемо адресу не контейнері:

# lxc-ls --fancy
NAME    STATE    IPV4          IPV6                                 GROUPS  AUTOSTART
-----------------------------------------------------------------------------------
veth02  RUNNING  192.168.1.42  fdee:cbcd:a595:0:216:3eff:feca:fd06  -       NO

І ось він - успіх! Перевіримо чи бачить контейнер інші хости в цій мережі, чи бачить хост контейнер і які маршрути встановлені на контейнері:

# lxc-attach -n veth02 -- ping -c2 192.168.1.41
PING 192.168.1.41 (192.168.1.41) 56(84) bytes of data.
64 bytes from 192.168.1.41: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 192.168.1.41: icmp_seq=2 ttl=64 time=0.047 ms

--- 192.168.1.41 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.047/0.068/0.090/0.023 ms

# lxc-attach -n veth02 -- ping -c2 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.504 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.425 ms

--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.425/0.464/0.504/0.045 ms

# ping -с2 192.168.1.42
PING 192.168.1.42 (192.168.1.42) 56(84) bytes of data.
64 bytes from 192.168.1.42: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 192.168.1.42: icmp_seq=2 ttl=64 time=0.047 ms

--- 192.168.1.42 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.425/0.464/0.504/0.045 ms

# lxc-attach -n veth02 -- route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

Отже, повторюсь, наразі всі нові контейнери будуть використовувати бридж lxcbr1 по замовчуванню і, як наслідок, знаходитись в тій же мережі, що і LXC-хост.

Для створення мережевого мосту окрім стандартного мережевого свіча можна скористатись його іншою реалізацією - Open vSwitch (OVS).

До переваг Open vSwitch над стандартним мережевим мостом Linux можна віднести більше можливостей по обліку трафіку, що проходить між віртуальними машинами з використанням SPAN / RSPAN, sFlow і Netflow, підтримка GRE-тунелів, інтеграція на рівні заліза з деякими мережевими картами та інше. Тобто якщо це вам необхідне - то можна спробувати Open vSwitch

Проінсталюємо пакет для роботи з OVS:

# apt-get install openvswitch-switch

Та створимо міст:

# ovs-vsctl add-br switch0
# ip add add 192.168.100.1/24 dev switch0
# ip link set switch0 up

Тобто switch0 буде таким собі gateway-єм для майбутньої мережі 192.168.100.0/24.

# ovs-vsctl show
1b236728-4637-42a5-8b81-53d4c93a6803
    Bridge "switch0"
        Port "switch0"
            Interface "switch0"
                type: internal
    ovs_version: "2.3.2"

Отже, з виводу бачимо, що був створений міст, порт та інтерфейс switch0.

Створимо 2 контейнера:

# lxc-create -t ubuntu -n veth03-ovs -- -r trusty
# lxc-create -t ubuntu -n veth04-ovs -- -r trusty

Та відредагуємо налаштування кожного з них:

# vim /var/lib/lxc/veth03-ovs/config
...
# Network configuration
lxc.network.type = veth
lxc.network.flags = up

lxc.network.script.up = /etc/lxc/ifup
lxc.network.script.down = /etc/lxc/ifdown
#lxc.network.veth.pair = lxc0
lxc.network.hwaddr = 00:16:3e:15:b3:62
lxc.network.ipv4 = 192.168.100.10/24

# vim /var/lib/lxc/veth04-ovs/config
...
# Network configuration
lxc.network.type = veth
lxc.network.flags = up

lxc.network.script.up = /etc/lxc/ifup
lxc.network.script.down = /etc/lxc/ifdown
lxc.network.hwaddr = 00:15:3e:15:b3:63
lxc.network.ipv4 = 192.168.100.11/24

Тобто veth03-ovs отримає адресу 192.168.100.10, а veth04-ovs - 192.168.100.11. Вони мають бачити один одного, адже увімкнені в одну мережу.

ifup та ifdown - скрипти, що запускаються після створення/вимкнення мережевого інтерфейсу контейнера відповідно.

# cat /etc/lxc/ifup
#!/bin/bash

ovsBr=switch0
ovs-vsctl --may-exist add-br $ovsBr
ovs-vsctl --if-exists del-port $ovsBr $5
ovs-vsctl --may-exist add-port $ovsBr $5

# cat /etc/lxc/ifdown
#!/bin/bash
ovsBr=switch0
ovs-vsctl --if-exists del-port ${ovsBr} $5

switch0 - мережевий міст, що був попередньо створений.

У ifup спочатку створюється інтерфейс мосту switch0, якщо його не було створено попередньо. Якщо проміжний інтерфейс (порт) вже було додано до switch0 - його буде видалено і додано знову. У ifdown відповідно просто видаляється проміжний інтерфейс veth* з мосту switch0.

Стартуємо контейнери та перевіряємо чи правильно були призначені адреси:

# lxc-start -n veth04-ovs
# lxc-start -n veth03-ovs

# lxc-ls -f
NAME             STATE    IPV4            IPV6  GROUPS  AUTOSTART
-----------------------------------------------------------------
...
veth03-ovs       RUNNING  192.168.100.10  -     -       NO
veth04-ovs       RUNNING  192.168.100.11  -     -       NO

Таки справді бачать один одного:

# lxc-attach -n veth03-ovs -- ping -c2 192.168.100.11
PING 192.168.100.11 (192.168.100.11) 56(84) bytes of data.
64 bytes from 192.168.100.11: icmp_seq=1 ttl=64 time=0.268 ms
64 bytes from 192.168.100.11: icmp_seq=2 ttl=64 time=0.055 ms

--- 192.168.100.11 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.055/0.161/0.268/0.107 ms

# lxc-attach -n veth04-ovs -- ping -c2 192.168.100.10
PING 192.168.100.10 (192.168.100.10) 56(84) bytes of data.
64 bytes from 192.168.100.10: icmp_seq=1 ttl=64 time=0.327 ms
64 bytes from 192.168.100.10: icmp_seq=2 ttl=64 time=0.062 ms

--- 192.168.100.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.062/0.194/0.327/0.133 ms

Хост також видимий для контенерів та навпаки:

# ping -c2 192.168.100.11
PING 192.168.100.11 (192.168.100.11) 56(84) bytes of data.
64 bytes from 192.168.100.11: icmp_seq=1 ttl=64 time=0.483 ms
64 bytes from 192.168.100.11: icmp_seq=2 ttl=64 time=0.057 ms

--- 192.168.100.11 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.057/0.270/0.483/0.213 ms

# lxc-attach -n veth04-ovs -- ping -c2 192.168.100.1
PING 192.168.100.1 (192.168.100.1) 56(84) bytes of data.
64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=0.305 ms
64 bytes from 192.168.100.1: icmp_seq=2 ttl=64 time=0.062 ms

--- 192.168.100.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.062/0.183/0.305/0.122 ms

Звісно можна також реалізувати пряме підключення контейнерів в ту ж мережу, в якій знаходиться і сам хост. Тобто варіант підключення, що я описав вище для стандартного мосту.

Open vSwitch - програмний мережевий світч, що має функціональні переваги над стандартною реалізацією мережевого мосту в ядрі. Але якщо функціональність звичайного свіча достатня - то, як на мене, можна не заморочуватись. До речі, модулі підтримки Open vSwitch вже інтегровані в основну вітку ядра з версії 3.4.

MACVLAN


Як я вже говорив, є декілька програмних свічів, за допомогою яких можна об’єднати контейнери у мережу. Наступний варіант, що ми спробуємо, - macvlan.

Клієнти macvlan свіча можуть працювати в декількох режимах:

  • private - macvlan інтерфейси бачать мережевий міст на хост-машині, проте один одного бачити не будуть. Тобто контейнери LXC не зможуть обмінюватимь пакетами напряму.
  • vepa - обмін пакетами можливий лише через зовнішній світч, котрий налаштований як reflective relay.
  • bridge - інтерфейси бачать один одного, але не сам брідж-інтерфейс на хост-машині. Таким чином підвищується ізоляційність віртуальних машин чи контенерів.
  • passthru - всі пакети отримані фізичним інтерфейсом  будуть перенаправдені до macvlan інтерфейсу. Лише один macvlan інтерфейс в режимі passthru можна підключити до фізичного інтерфейсу.

Розглянемо детальніше macvlan bridge рехим. В цьому режимі контейнери зможуть "бачити" один одного, але не lxc-хост. Продемонструю це на прикладі:

# brctl addbr lxcbr2
# ip link set lxcbr2 up

# brctl show
bridge name       bridge id            STP enabled      interfaces
...
lxcbr2            8000.000000000000    no

Створимо два контейнери для демонстрації роботи мережі між ними:

# lxc-create -t ubuntu -n macvlanbridge01 -- -r trusty
# lxc-create -t ubuntu -n macvlanbridge02 -- -r trusty

Відредагуємо конфігураційні файли цих контейнерів на використання macvlan свіча:

# vim /var/lib/lxc/macvlanbridge01/config
...
# Network configuration
lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.link = lxcbr2
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:8d:09:fc
lxc.network.ipv4 = 10.0.5.3/24

# vim /var/lib/lxc/macvlanbridge02/config
...
# Network configuration
lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.link = lxcbr2
lxc.network.flags = up
lxc.network.hwaddr = 00:17:3e:8d:09:fc
lxc.network.ipv4 = 10.0.5.4/24

Запустимо контейнери, та перевіримо їх роботу:

# lxc-start -n macvlanbridge01
# lxc-start -n macvlanbridge02

# lxc-ls -f
NAME             STATE    IPV4          IPV6        GROUPS  AUTOSTART
----------------------------------------------------------------------
macvlanbridge01  RUNNING  10.0.5.3      -            -       NO
macvlanbridge02  RUNNING  10.0.5.4      -            -       NO
...

І ось так працює мережа:

# ping 10.0.5.4
PING 10.0.5.4 (10.0.5.4) 56(84) bytes of data.
^C
--- 10.0.5.4 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2016ms

# ping 10.0.5.3
PING 10.0.5.3 (10.0.5.3) 56(84) bytes of data.
^C
--- 10.0.5.3 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1007ms

# lxc-attach -n macvlanbridge01 -- ping 10.0.5.4
PING 10.0.5.4 (10.0.5.4) 56(84) bytes of data.
64 bytes from 10.0.5.4: icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from 10.0.5.4: icmp_seq=2 ttl=64 time=0.049 ms
^C
--- 10.0.5.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.049/0.064/0.079/0.015 ms

# lxc-attach -n macvlanbridge02 -- ping 10.0.5.3
PING 10.0.5.3 (10.0.5.3) 56(84) bytes of data.
64 bytes from 10.0.5.3: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 10.0.5.3: icmp_seq=2 ttl=64 time=0.049 ms
^C
--- 10.0.5.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.049/0.052/0.055/0.003 ms

Тобто контейнери не доступні для хосту по IP, проте доступні один для одного. Цей режим може бути корисним при бажанні якомога більше ізолювати інфраструктуру контейнерів.
Наприклад, доступ для групи macvlan bridge контейнерів надавати лише через один сервер-gateway, у котрого буде два підключення: macvlan, щоб знаходитись у тій же мережі, та venet, що буде мати зв’язок з публічною мережею чи з хост машиною. Така собі DMZ-зона. Спробую і це продемонструвати.

# lxc-create -t ubuntu -n dmzmaster01 -- -r trusty

Для цього нам очевидно необхідно 2 мережевих інтерфейсів в контейнері:

# vim /var/lib/lxc/dmzmaster01/config
...
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr1

# MAC VLAN network
lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.flags = up
lxc.network.link = lxcbr2
lxc.network.ipv4 = 10.0.5.1/24

У якості мережевого мосту оберемо раніше створений міст lxcbr1, що з’єднає нас з публічною мережею та lxcbr2 - задля підключення до macvlan bridge контейнерів.

Запускаємо контейнер та перевіряємо ситуацію:

# lxc-start --name dmzmaster01
# lxc-ls -f
NAME             STATE    IPV4                   IPV6                                 GROUPS  AUTOSTART
-------------------------------------------------------------------------------------------------------
dmzmaster01      RUNNING  10.0.5.1, 192.168.1.2  fdee:cbcd:a595:0:ecf6:6ff:fedd:6fde  -       NO              
macvlanbridge01  RUNNING  10.0.5.3               -                                    -       NO      
macvlanbridge02  RUNNING  10.0.5.4               -                                    -       NO
...
   
# lxc-attach -n dmzmaster01
root@dmzmaster01:/#

root@dmzmaster01:/# ping -c2 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.504 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.392 ms

--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.392/0.448/0.504/0.056 ms

root@dmzmaster01:/# ping -c2 google.com
PING google.com (216.58.209.174) 56(84) bytes of data.
64 bytes from bud02s21-in-f174.1e100.net (216.58.209.174): icmp_seq=1 ttl=59 time=15.7 ms
64 bytes from bud02s21-in-f174.1e100.net (216.58.209.174): icmp_seq=2 ttl=59 time=15.6 ms

--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 15.673/15.697/15.721/0.024 ms

root@dmzmaster01:/# ping -c2 10.0.5.3
PING 10.0.5.3 (10.0.5.3) 56(84) bytes of data.
64 bytes from 10.0.5.3: icmp_seq=1 ttl=64 time=0.082 ms
64 bytes from 10.0.5.3: icmp_seq=2 ttl=64 time=0.049 ms

--- 10.0.5.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.049/0.065/0.082/0.018 ms
root@dmzmaster01:/#

Можна навіть піти далі - та налаштувати маскарадинг на dmzmaster01. Таким чином на macvlanbridge0[1,2] можна буде, скажімо, оновлювати пакети

# lxc-attach -n dmzmaster01
root@dmzmaster01:/#
root@dmzmaster01:/# apt-get install iptables
root@dmzmaster01:/# iptables -t nat -A POSTROUTING -s 10.0.5.0/24 ! -d 10.0.5.0/24 -j MASQUERADE

# lxc-attach -n macvlanbridge01
root@macvlanbridge01:/#
root@macvlanbridge01:/# route add default gw 10.0.5.1
root@macvlanbridge01:/# ping -c2 google.com
PING google.com (216.58.209.174) 56(84) bytes of data.
64 bytes from bud02s21-in-f174.1e100.net (216.58.209.174): icmp_seq=1 ttl=58 time=15.7 ms
64 bytes from bud02s21-in-f174.1e100.net (216.58.209.174): icmp_seq=2 ttl=58 time=15.7 ms

--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 15.741/15.751/15.761/0.010 ms
root@macvlanbridge01:/#

Варто зауважити, що LXC-хост все таки можна зробити видимим для контейнерів з macvlan bridge підключенням. Для цього необхідно створити додатковий інтерфейс в просторі macvlan-мосту lxcbr2:

# ip link add link lxcbr2 macvlancont01 type macvlan mode bridge
# ip link list macvlancont01
10: macvlancont01@lxcbr2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default
    link/ether 76:46:d4:77:6b:fb brd ff:ff:ff:ff:ff:ff

Призначимо інтерфейсу IP-адресу з того ж діапазону, що і macvlan контейнерам:

# ip address add 10.0.5.5/24 dev macvlancont01
# ip link set dev macvlancont01 up
# ip addr list macvlancont01

Тепер спробуємо попінгувати контейнери з хосту:

$ ping -c 2 10.0.5.3
PING 10.0.5.3 (10.0.5.3) 56(84) bytes of data.
64 bytes from 10.0.5.3: icmp_req=1 ttl=64 time=0.092 ms
64 bytes from 10.0.5.3: icmp_req=2 ttl=64 time=0.066 ms

--- 10.0.5.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.066/0.079/0.092/0.013 ms

$ ping -c 2 10.0.5.4
PING 10.0.5.4 (10.0.5.4) 56(84) bytes of data.
64 bytes from 10.0.5.4: icmp_req=1 ttl=64 time=0.097 ms
64 bytes from 10.0.5.4: icmp_req=2 ttl=64 time=0.056 ms

--- 10.0.5.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.056/0.076/0.097/0.022 ms

Boom! Все як і було заплановано.

VLAN


VLAN (Virtual Local Area Network) - група пристроїв, що мають можливість взаємодіяти між собою безпосередньо на канальному рівні, хоча фізично при цьому вони можуть бути підключені до різних мережевих коммутаторів. І навпаки, пристрої, що знаходяться в різних VLAN'ах, невидимі один для одного на канальному рівні, навіть якщо вони підключені до одного комутатора, і зв'язок між цими пристроями можливий тільки на мережевому і більш високих рівнях.

У сучасних мережах VLAN - головний механізм для створення логічної топології мережі, не залежної від її фізичної топології. VLAN'и використовуються для скорочення широкомовного трафіку в мережі. Мають велике значення з точки зору безпеки, зокрема як засіб боротьби з ARP-spoofing'ом.

Створимо контейнер для демонстрації цього режиму:

# lxc-create -t ubuntu -n vlan01 -- -r trusty

Конфігурація інтерфейса для роботи в VLAN режимі відбувається а допомогою того ж конфігураційного файлу config:

# vim /var/lib/lxc/vlan01/config
...
# Network configuration
lxc.network.type = vlan
lxc.network.link = eth0
lxc.network.vlan.id = 10
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:24:ab:d3

lxc.network.vlan.id - це так би мовити тег для Ethernet пакетів. Запустимо контейнер та перевіримо, що з мережею:

# lxc-start --name vlan01
root@vlan01:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
11: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 00:16:3e:24:ab:d3 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::216:3eff:fe24:abd3/64 scope link
       valid_lft forever preferred_lft forever

Отже, в контейнері з'явився інтерфейс eth0@if2 (2 тому, що саме під таким номером він знаходиться в хост системі). У межах одного фізичного інтерфейсу можна створити лише один VLAN інтерфейс з певним id.

Для чого власне може знадобитись VLAN? Для простих тестувань чогось на власній системі, бріджинга більш ніж достатньо. VLAN може бути корисним для побудови інфраструктури контейнерів, яка поділена на канальному рівні, незалежно від топології фактичного ввімкнення контейнерів в фізичну мережу. Це може підвищити рівень безпеки мережі, продуктивності та ін.

Якщо говорити про практичний приклад, то уявімо, що ми маємо комутатор на певну кількість портів. Кожний порт представлений окремим інтерфейсом на хост-машині. З кожного такого фізичного інтерфейсу можна створити VLAN-інтерфейс з відповідним тегом, таким чином об'єднати контейнери і відповідні мережі. А на хост-машині приміром лишити лише trunk-порт (порт, що об'єднує VLAN-и).

Тобто тут все залежить від уяви та уміння. Як і всюди насправді...

PHYS


Це можливість перебросу фізичного інтерфейсу хост-машини прямо в контейнер. Як це зробити? Це зовсім не складно.

# lxc-create -t ubuntu -n phys01 -- -r trusty

# cat /var/lib/lxc/phys01/config
...
# Network configuration
lxc.network.type = phys
lxc.network.link = eth0
lxc.network.flags = up
lxc.network.ipv4 = 192.168.1.135/24
lxc.network.ipv4.gateway = 192.168.1.1
lxc.network.hwaddr = 00:16:3e:46:8e:f9

За наявності в мережі 192.168.1.0/24 dhcp-сервера, шлюз та IP-адресу вказувати не обов'язково.

Після старту контейнера, фізичний інтерфейс eth0 на хост-машині зникне і перейде в контейнер:

# lxc-ls -f
NAME             STATE    IPV4           IPV6                                 GROUPS  AUTOSTART
------------------------------------------------------------------------------------------------
...  
phys01           RUNNING  192.168.1.135  fdee:cbcd:a595:0:216:3eff:fe46:8ef9  -       NO      

# lxc-attach -n phys01
root@phys01:~# ip address list eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:16:3e:46:8e:f9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.135/24 brd 192.168.1.255 scope global eth0

root@phys01:~# ping -c2 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.43 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.431 ms

--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.431/0.931/1.431/0.500 ms
root@phys01:~#


Performance


Загалом програмні мости є достатньо продуктивними в порівнянні з мережевими картами, що мають підтримку SR-IOV. Перший графік демонструє швидкість передачі данних при використанні конкретного програмного свіча:


Тобто, як не дивно, SR-IOV (PCI-passthrough) карта має найбільшу продуктивність, що на 6%~14% вища за програмні варіанти.

Другий графік показує завантаження CPU на хості при використанні кожного із варіантів підключення віртуальних машин, при максимальному завантаженні трафіком:


SR-IOV (PCI-passthrough) знову має найкращі показники: використання центрального процесора системою у цьому разі наближається до 0% (графік вище). Завантаженість CPU в режимі macvtap (macvlan) на 24~29% нижче за використання стандартного мосту чи Open vSwitch.

У якості тестового середовища було взято:

• kernel 3.14.4 (2014/5/13 Release)
• Host: Xeon E5-2407 4 core * 2 socket
• NIC: 10GbE, Intel 82599 chip (ixgbe)
• Guest: 2 core
• HW Switch: BLADE G8124
• Benchmark tool: netperf-2.6

Вся інформація щодо тестування - звідси.

На цій ноті буду завершувати. Стаття вийшла довгою і, мабуть з певними неточностями. Тому буду дуже вдячний за виправлення, про які можна згадати в коментарях.

Посилання:
http://containerops.org/2013/11/19/lxc-networking/
http://xgu.ru/wiki/LXC
http://xgu.ru/wiki/Linux_Bridge
http://tacchen.logdown.com/posts/2014/09/03/230738
http://blog.scottlowe.org/2014/01/23/automatically-connecting-lxc-to-open-vswitch/
https://infologs.wordpress.com/2015/06/19/how-to-attach-lxc-container-to-ovs-openvswitch/
http://blog.scottlowe.org/2013/11/26/lxc-open-vswitch-and-gre-tunnels/
http://www.opencloudblog.com/?p=66
http://www.opencloudblog.com/?p=130
https://www.flockport.com/guides/
https://www.linux.com/learn/tutorials/804646-lxc-container-networking-deep-dive
http://events.linuxfoundation.org/sites/events/files/slides/LinuxConJapan2014_makita_0.pdf

Немає коментарів:

Дописати коментар