Перед прочитанням тексту нижче, бажано звісно розуміти базові речі про 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* інтерфейсами, котрі є проміжним мережевим інтерфейсом для кожного контейнера в цій мережі.
Створимо бридж-інтерфейс 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
Немає коментарів:
Дописати коментар