Как работают сети, часть 2: отказоустойчивость с teaming, режем соединения с Traffic Control, а так же tap-интерфейсы и Linux Bridge
В статье Как работают сети: что такое свитч, роутер, DNS, DHCP, NAT, VPN и ещё с десяток необходимых вещей я рассказал про основные компоненты компьютерных сетей. Её прочтение необходимо перед тем, как браться за текст ниже.
За основу статьи взята настройка teaming в Linux. Настраивать его мы будем в виртуальном окружении, а значит, нам понадобится libvirt, который мы изучили в статье Основы виртуализации и введение в KVM.
На этот раз мы не будем вслепую полагаться на работу libvirt. Вместо этого мы попытаемся понять, что кроется за парой строчек простого XML-файлика, определяющего виртуалку или сети. В результате мы узнаем, как работают и для чего нужны Linux bridge, tap-интерфейсы и Linux Traffic Control, и как они используются для виртуализации.
Начнём мы с того, что такое teaming.
Зачем нужны teaming, bonding, link aggregation и port trunking?
Если не погружаться в полемику, рассмотр конкретной технической реализации и набора фич, то термины teaming, bonding, link aggreation и port trunking означают одно и то же: объединение нескольких физических сетевых интерфейсов в один логический, с целью увеличить пропускную способность и/или обеспечить отказоустойчивость.
Мы будем использовать термин "teaming", потому что именно он используется в современных весиях Linux.
Помимо teaming, в Linux есть bonding, на замену которому и пришёл teaming. Чем в контексте Linux teaming отличается от bonding можно почитать в документации от Red Hat — согласно табличке по ссылке, использовать teaming в новых окружениях особого смысла нет, но здесь я могу ошибаться. Максимально любопытному читателю предлагается прочитать документацию по bonding — мало ли, вдруг придётся работать именно с ним.
Я не буду погружаться в миллион деталей касательно того, как именно работает teaming. Хочешь ли ты уже сейчас знать, что teaming реализован в виде маленького шустрого драйвера для ядра Linux и набора пользовательских API для пущей гибкости и расширяемости? Наверное, нет. Но когда такое желание появится, в конце статьи будет восхитительный список ссылок, в том числе на подробнейшее описание внутреннего строения teaming в Linux.
Теперь разберёмся, что значит "увеличить пропускную способность" и "обеспечить отказоустойчивость".
Допустим, у сервера один NIC. Если он сгорит, то сервер останется без сети. А если у сервера два NIC, то сервер будет видеть их как два разных NIC, с двумя разными IP-адресами. В результате, одно и тоже соединение знает только об одном интерфейсе. Если сгорит один из двух, то все соединения, проходящие через сгоревший NIC, пропадут без шанса на реабилитацию.
А вот если использовать teaming, оба NIC будут спрятаны за одним программным интерфейсом, за который и отвечает teaming. Благодаря этому, если один из NIC сгорит или, например, его вырвут из сервера, то никакого перебоя в связи не будет, так как трафик автоматически пойдёт через оставшийся в живых интерфейс. С отказоустойчивостью разобрались.
Теперь допустим, что у нас есть сервер на котором хранится куча файлов, которые постоянно скачивает кто угодно из локальной сети. Если в этот файловый сервер воткнут один NIC и два других сервера будут пытаться скачивать файлы одновременно, они будут делить пропускную способность этого NIC пополам. А если в файловом сервере будет два NIC, объединённых при помощи teaming, то каждый из скачивающих серверов будет наслаждаться максимальной (для одного NIC) скоростью скачивания.
Спускаемся с небес на землю
Очень важно сделать отступление и разъяснить пару моментов.
Во-первых, если сервер с одним NIC будет пытаться скачать файл с сервера, в котором два NIC, то скорость скачивания не удвоится — потому что скорость будет ограничена пропускной способностью NIC скачивающего сервера. Другое дело, если в оба сервера воткнуть по два NIC.
Во-вторых, если в сервер воткнуть два NIC и попытаться скачать большой файл, то от наличия второго NIC скорость скачивания не удвоится (но слегка увеличится, возможно). Преимущество будет во время параллельного скачивания нескольких файлов — тогда трафик пойдёт как надо, и оба файла будут качаться, теоретически, с максимальной скоростью, доступной каждому из NIC.
В-третьих, наличие teaming не решает все проблемы с отказоустойчивостью. Не забываем, что NIC подсоединены к роутеру (или свитчу, или к мосту, или к хабу). И если сгорит роутер (или свитч, или мост, или хаб), то даже пять NIC на сервере не спасут от падения сети. Поэтому нужно думать ещё и об отказоустойчивости роутеров (а так же свитчей, мостов и хабов).
Проверяем работу teaming
Теперь мы знаем, зачем нужен teaming. Проверять его работу в реальных условиях проблематично — нужны железки и провода. У меня под рукой есть только ноутбук с одним NIC, а значит, чтобы провести интересные тесты, придётся воспользоваться виртуализацией. Напомню, что максимально желательно заранее прочитать нашу статью про виртуализацию, чтобы не только разобраться в теме, но и настроить локальное окружение для работы с libvirt + KVM.
Запускаем виртуальную машину с CentOS через libvirt
Скачиваем к себе Kickstart файлик
В файлике заменяем в самом конце PUBLIC_KEY на свой публичный SSH ключик.
Запускаем новую виртуалку этой командой (не забудь обновить путь до Kickstart файла и до образа с Centos):
sudo virt-install —name teaming —ram 2048 —disk size=8,format=qcow2 —vcpus 2 \
—location /tmp/CentOS-7-x86_64-Minimal-1708.iso \
—initrd-inject /path/to/ centos-cfg-minimal.ks \
—virt-type kvm \
—extra-args ks=file:/ centos-cfg-minimal.ks
Размер диска и ОЗУ на твоё усмотрение.
Ищем IP адрес при помощи sudo virsh net-dhcp-leases default
и пытаемся зайти по SSH root пользователем. Получилось? Идём дальше.
Изучаем Linux Bridge
Все ресурсы, управляемые libvirt — виртуалки, сети, хранилища — описаны в XML файлах. Внося изменения в эти файлы, можно менять состояние ресурсов. Например, можно изменить настройки сетевого интерфейса нашей виртуалки.
Откроем XML виртуалки:
virsh edit teaming
И найдём там определение сетевого интерфейса:
...
<interface type='network'>
<mac address='52:54:00:95:3f:7b'/>
<source network='default'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
...
Здесь мы видим, в какую сеть "воткнут" этот интерфейс (а
правильнее: в свитч какой сети) — <source network='default'/>
, видим
(и можем поменять) MAC-адрес этого интерфейса и остальные его параметры.
Убедимся в том, что этот интерфейс и правда воткнут в виртуальный свитч, из которого и состоит сеть libvirt. Чтобы узнать, какой именно свитч используется для сети мы можем посмотреть XML определение самой сети:
virsh net-dumpxml default
Получаем:
<network connections='1'>
<name>default</name>
<uuid>c6d5c8b9-3dbe-4be5-9ef9-25afce83e699</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:7e:33:23'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
Вот определение свитча: <bridge name='virbr0' stp='on' delay='0'/>
.
Теперь мы знаем, что он называется virbr0
. При помощи команды brctl
посмотрим, какие интерфейсы к нему подключены:
brctl show virbr0
bridge name bridge id STP enabled interfaces
virbr0 8000.5254007e3323 yes virbr0-nic
vnet0
Два интерфейса — один принадлежит хосту, а другой нашей виртуалке
(vnet0). У интерфейса хоста есть IP адрес внутри сети libvirt
(192.168.122.1/24), и через этот интерфейс
идёт весь трафик виртуалок во внешний мир. Ты можешь сам убедиться в этом, поигравшись с командами ip r
и ip addr
на хосте и внутри виртуалки, а также
посмотрев результат traceroute
до соседней виртуалки и, например, до
mkdev.me.
Теперь посмотрим какие MAC-адреса у подсоединённых интерфейсов:
brctl showmacs virbr0
port no mac addr is local? ageing timer
1 52:54:00:7e:33:23 yes 0.00
1 52:54:00:7e:33:23 yes 0.00
2 52:54:00:95:3f:7b no 126.91
2 fe:54:00:95:3f:7b yes 0.00
2 fe:54:00:95:3f:7b yes 0.00
Результат, конечно, сбивает с толку. Почему адрес NIC хоста выводится дважды, а NIC виртуалки — трижды? И почему в одном случае NIC виртуалки начинается с "52", а в двух других — с "fe"? И почему в первом случае "is local?" равно "no" и выводится какой-то ageing timer? Сейчас попробуем разобраться.
Чтобы понять, почему MAC-адреса дублируются, нам понадобится другая
утилита для работы с Linux Bridge — bridge
. С её помощью мы сможем
посмотреть Forwarding Database (fdb). fdb — это, грубо говоря, таблица
маршрутизации на втором уровне сети. Обычно мы имеем дело с
маршрутизацией на третьем уровне, или, если так понятней, на уровне IP-адресов. Но свитч чаще всего относится ко второму уровню и оперирует MAC
адресами. Посмотрим fdb для моста virbr0:
bridge fdb show br virbr0
01:00:5e:00:00:01 dev virbr0 self permanent
01:00:5e:00:00:fb dev virbr0 self permanent
52:54:00:7e:33:23 dev virbr0-nic vlan 1 master virbr0 permanent
52:54:00:7e:33:23 dev virbr0-nic master virbr0 permanent
fe:54:00:95:3f:7b dev vnet3 vlan 1 master virbr0 permanent
52:54:00:95:3f:7b dev vnet3 master virbr0
fe:54:00:95:3f:7b dev vnet3 master virbr0 permanent
33:33:00:00:00:01 dev vnet3 self permanent
01:00:5e:00:00:01 dev vnet3 self permanent
33:33:ff:95:3f:7b dev vnet3 self permanent
33:33:00:00:00:fb dev vnet3 self permanent
Мы проигнорируем 01:00:5e:00:00:01
, 01:00:5e:00:00:fb
, а также все
строки, начинающиеся с 33:33
. В отдельной статье я расскажу про IPv6 и
чем он отличается от IPv4, после чего станет понятно, зачем нужны эти
бесконечно дублирующиеся записи. Пока что сделаем вид, что их вовсе нет:
52:54:00:7e:33:23 dev virbr0-nic vlan 1 master virbr0 permanent
52:54:00:7e:33:23 dev virbr0-nic master virbr0 permanent
fe:54:00:95:3f:7b dev vnet3 vlan 1 master virbr0 permanent
52:54:00:95:3f:7b dev vnet3 master virbr0
fe:54:00:95:3f:7b dev vnet3 master virbr0 permanent
Так же выкинем строчки, ответственные за сам свитч, оставив только записи, релевантные для нашей виртуалки:
52:54:00:95:3f:7b dev vnet3 master virbr0
fe:54:00:95:3f:7b dev vnet3 vlan 1 master virbr0 permanent
fe:54:00:95:3f:7b dev vnet3 master virbr0 permanent
У Linux Bridge (да и не только у него) есть способность добавлять записи в fdb динамически, "обучаясь" на проходящем трафике. Пришли фреймы от MAC-адреса 52:54:00:95:3f:7b? Добавим его автоматом, судя по всему он подключён к свитчу. А если от него долго не будет вестей, то так же автоматом и удалим. Именно это и означают колонки "is local?" и "ageing" — первая показывает, настроен ли интерфейс в свитче статически (~= воткнут в него напрямую), а вторая показывает сколько прошло времени с последнего контакта с этим интерфейсом.
Команда bridge fdb
не показывает ageing, но мы видим маршрут, который был автоматически добавлен:
52:54:00:95:3f:7b dev vnet3 master virbr0
В случае статически настроенных интерфейсов ageing не имеет смысла, потому что свитч всегда знает, что они подключены. За подключение интерфейсов виртуалки к свитчу отвечает libvirt (а на самом деле — qemu), поэтому особого смысла в динамическом добавлении маршрутов нет — свитч и так знает всё, что нужно про соединение с виртуалкой. Поэтому мы можем вырубить learning полностью:
brctl setageing virbr0 0
Теперь у нас всего две записи для интерфейса виртуалки:
port no mac addr is local? ageing timer
1 52:54:00:7e:33:23 yes 0.00
1 52:54:00:7e:33:23 yes 0.00
2 fe:54:00:95:3f:7b yes 0.00
2 fe:54:00:95:3f:7b yes 0.00
Внимательный читатель уже давно кричит в экран: "почему MAC-адрес виртуалки и MAC-адрес в свитче разные???". Это отличный вопрос.
Как убедиться, что libvirt и правда использует QEMU? Заходим в
/var/log/libvirt/qemu
, ищем лог с названием виртуалки и видим примерно такое.
tap-интерфейсы
И правда, если зайти в виртуалку и посмотреть MAC-адрес её NIC, мы
увидим 52:54:00:95:3f:7b
, в то время как brctl showmacs
показывает
fe:54:00:95:3f:7b
. Разница лишь в первых двух символах — fe
вместо
52
. Дело в том, что для каждой виртуалки libvirt создаёт на хосте
специальный интерфейс — tap-интерфейс.
tap-интерфейс — это такой виртуальный интерфейс, работающий на уровне ядра Linux. Вместо того, чтобы принимать и передавать данные на физическом уровне, tap-интерфейс получает данные программно и программно же их передаёт.
Если сильно упрощать, то tap-интерфейс — это файловый дескриптор, в который, в нашем случае, Linux bridge отправляет Ethernet фреймы. QEMU считывает их из этого дескриптора и отправляет в виртуалку.
Не знаешь, что такое файловый дескриптор? Не беда. Мы об этом расскажем в отдельной статье. Подпишись на нашу рассылку (форма внизу), чтобы узнать о её появлении первым.
Для виртуалки это выглядит так, будто бы фреймы пришли на физический
интерфейс — тот самый, у которого MAC-адрес 52:54:00:95:3f:7b
. Это
очень важно: tap-интерфейс — это реализация виртуализации сетевого
интерфейса, и виртуалка ничего не знает об этой реализации. Виртуалка
считает, что у неё полноценный физический интерфейс.
Нам сейчас не очень важно, как именно и когда создаётся tap-интерфейс. libvirt на то и существует, чтобы спрятать от нас множество мелких деталей до тех пор, пока у нас не появится необходимость в них разбираться. Тем не менее, давай выполним на хосте пару команд, просто чтобы знать, куда смотреть в будущем.
Во-первых, давай глянем полный конфиг виртуалки. Когда мы смотрели
интерфейс виртуалки через virsh edit
, мы наблюдали статичный
постоянный конфиг. Но конфиг уже запущенной виртуалки слегка отличается,
так как в процессе запуска libvirt делает разные полезные штуки —
например, создаёт tap-интерфейс и связь между ним и виртуалкой. Давай
посмотрим, как выглядит конфиг интерфейса уже запущенной виртуалки:
virsh dumpxml teaming
...
<interface type='network'>
<mac address='52:54:00:95:3f:7b'/>
<source network='default' bridge='virbr0'/>
<target dev='vnet0'/>
<model type='virtio'/>
<alias name='net0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
...
Здесь уже виден наш tap-интерфейс: <target dev='vnet0' />
.
tap-интерфейсы существуют на хосте, и мы можем посмотреть их список
командой ip tuntap
:
virbr0-nic: tap UNKNOWN_FLAGS:800
vnet0: tap vnet_hdr
И так как это полноценный (хоть и виртуальный) интерфейс, мы увидим его в списке интерфейсов хоста:
ip link
11: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:7e:33:23 brd ff:ff:ff:ff:ff:ff
12: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master virbr0 state DOWN mode DEFAULT group default qlen 1000
link/ether 52:54:00:7e:33:23 brd ff:ff:ff:ff:ff:ff
62: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master virbr0 state UNKNOWN mode DEFAULT group default qlen 1000
link/ether fe:54:00:95:3f:7b brd ff:ff:ff:ff:ff:ff
В конце будет пара ссылок на статьи, в которых можно узнать больше про tap и tun. tun — это как tap, только не на втором уровне сети, а на третьем.
Как ограничить скорость интернета в libvirt?
Продемонстрировать отказоустойчивость сервера с настроенным teaming будет весьма несложно. Но вот как показать повышенную пропускную способность?
Откроем редактирование XML-определения виртуалки: virsh edit teaming
, найдём
там определение интерфейса виртуалки и добавим блок <bandwidth>
:
<interface type='network'>
<mac address='52:54:00:95:3f:7b'/>
<source network='default'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
<bandwidth>
<inbound average='128'/>
<outbound average='128'/>
</bandwidth>
</interface>
Теперь скорость соединения будет ограничена 128kbs. Мы можем задать скорость скачивания и прочие детали при помощи различных атрибутов как для входящих, так и для исходящих соединений. О всех доступных опциях лучше всего читать в документации libvirt. А пока проверим, что ограничение и правда действует. Сначала перезапустим виртуалку:
virsh destroy teaming && virsh start teaming
Зайдём туда по SSH и попробуем скачать какой-нибудь файл через,
например, wget
:
wget http://centos.schlundtech.de/7/isos/x86_64/CentOS-7-x86_64-Minimal-1708.iso
Смотрим на процесс скачивания и видим, что скорость и правда ограничена. Но как это работает?
Что такое Linux Traffic Control?
Traffic control позволяет детально управлять пакетами, которые проходят через сетевое устройство. Можно указать скорость обработки очереди пакетов, можно фильтровать и отмечать пакеты и выкидывать их, если они не соответствуют нашим требования. При желании можно сделать так, чтобы, например, у SSH соединения сохранялась всегда одна и та же скорость, даже если через тот же интерфейс идёт скачивание крупных файлов. В общем, полный набор инструментов для работы с сетевыми пакетами — была б фантазия для чего их применить.
Quality of Service (QoS) — синоним Traffic Control.
Как и все хорошие вещи в этой жизни, управление сетевыми пакетами
проходит в ядре Linux — через packet scheduler. А чтобы настроить его
работу, существует утилита tc
. Traffic Control в Linux — штука очень
гибкая, комплексная, мощная. Постараемся рассмотреть основные компоненты
контроля трафика, а затем посмотрим как их настроил libvirt.
Контроль трафика основан на старых добрых очередях, обработка которых контролируется через Queueing Disciplines (qdisc). У каждого сетевого интерфейса есть одна очередь по-умолчанию, к которой не применяется никаких особенных правил — пакеты входят в очередь, а затем достаются оттуда как можно быстрее. Если в сети есть неполадки, то пакеты могут какое-то время проваляться в очереди — таким образом избежав утраты данных при передаче.
Является ли qdisc очередью? После изучения многих источников, можно сказать, что термины "queue" и "qdisc" взаимозаменяют друг друга. В основе всего лежит очередь как структура данных, а qdisc — это некие правила обработки этой очереди.
Но от одной FIFO очереди толку мало. Поэтому Linux позволяет строить сложные деревья из очередей, с разными правилами обработки пакетов от разных источников, для разных портов.
qdisc's бывают двух типов — без классов (classless qdisc) и с классами (classful qdisc). qdisc's без классов просто пропускают через себя трафик (с какой-нибудь обработкой, типа добавления задержки выпуска пакета из очереди). qdisc с классами содержат в себе другие очереди и классы, и как правило фильтруют пакеты и распихивают их по нескольким очередям сразу.
Не волнуйся, если не до конца поймёшь всё, что мы увидим дальше. Внимательно изучи материалы по Linux Traffic Control в конце статьи, а затем вернись к примерам выше — тогда всё станет ясно.
Что нас интересует сейчас, так это взаимосвязь между <bandwith>
в
определении интерфейса виртуалки и настройками контроля траффика на
хосте. Как я уже сказал, за настройку отвечает утилита tc
, при помощи
которой мы можем легко увидеть, что именно libvirt нам настроил:
tc qdisc show dev vnet0
qdisc htb 1: root refcnt 2 r2q 10 default 1 direct_packets_stat 0 direct_qlen 1000
qdisc sfq 2: parent 1:1 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec
qdisc ingress ffff: parent ffff:fff1 ————————
У нашего tap-интерфейса три qdisc. У каждого интерфейса есть две "ненастоящие" qdisc — root и ingress. Ненастоящие они из-за того, что сами по себе они ничего не делают, а являются точками к которым подключаются другие qdisc.
При этом, к ingress нельзя прикрепить ничего, кроме фильтрации пакетов и опционального отказа от пакета. Реализовано это так, потому что у нас нет настоящего контроля за входящими данными — мы не можем влиять на уровне OS на скорость входящего трафика. В том числе поэтому не существует настоящей защиты от DDoS. Всё, что мы можем — это как можно быстрее и раньше блокировать трафик или помечать его для последующей обработки — именно этим и занимается ingress qdisc, которая стоит в самом начале обработки входящего трафика. Мы не добавляли никакой фильтрации для входящего трафика, поэтому ingress qdisc выглядит скучно.
Две остальные qdisc связаны друг с другом. Во главе стоит qdisc htb
1:
, родителем которого является root. Ниже неё по дереву находится
qdisc sfq 2:
— это видно по parent 1:1
.
htb и
sfq — это отдельные
типы очередей, встроенные в Linux. По ссылкам объяснение их работы
вместе с картинками. Если вкратце, то htb позволяет ограничивать
скорость трафика, а sfq пытается равномерно распределить выходящие
пакеты. При этом htb — это classful qdisc, а sfq — classless (man tc
докажет).
Теперь глянем классы:
tc -g class show dev vnet0
+—-(1:1) htb prio 0 rate 1024Kbit ceil 1024Kbit burst 1599b cburst 1599b
Вот он, наш ограничитель скорости — мы видим те самые заветные
1024Kbit
.
И снова — libvirt спрятал от нас сложную и дико полезную часть Linux, позволив несколькими человекопонятными строчками XML настроить Linux packet scheduler для tap-интерфейса виртуальной машины.
Добавляем второй интерфейс
Пора перейти к teaming. Но для этого нам нужен второй интерфейс, который очень легко добавить при помощи virsh:
virsh attach-interface teaming network default —persistent
Ты можешь убедиться в наличии нового интерфейса либо посмотрев XML
виртуалки, либо зайдя в неё и выполнив ip addr
, либо выполнив ту же
самую команду на хосте, на котором появился новый tap-интерфейс.
DHCP-сервер даже любезно выдал новому интерфейсу IP-адрес — хоть он нам и не нужен.
Время настроить teaming!
Устанавливаем и настраиваем teaming
Заходим по SSH (или при помощи Spice) в виртуалку и отключаем одно из соединений:
nmcli con
NAME UUID TYPE DEVICE
Wired connection 1 31e82585-4c0c-4a30-ba4c-622d66a48e68 802-3-ethernet ens9
eth0 b21fbb15-ae30-4a65-9ec3-37a6b202ab5d 802-3-ethernet eth0
nmcli con delete 'Wired connection 1'
Если ты зашёл в виртуалку по SSH через IP-адрес интерфейса, соединение которого выключил, то тебе придётся зайти заново — ведь teaming ещё не настроен.
Ставим teamd: yum install teamd NetworkManager-team -y
. Подгружаем модуль: modprobe
team
. Убеждаемся, что всё в порядке: lsmod | grep team
.
У меня после установки NetworkManager-team вылеза ошибка
Error: Could not create NMClient object: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: Method "GetManagedObjects" with signature "" on interface "org.freedesktop.DBus.ObjectManager" doesn't exist
. Ребут помог.
Дальше у нас есть на выбор несколько вариантов настройки teaming:
- Путём ручного редактирования network-scripts
- При помощи
nmcli
- здесь нам пригодитсяman nmcli-examples
- При помощи
nmtui
Первые два способа вынуждают нас запоминать разные последовательности
команд, в то время как текстовый интерфейс nmtui
позволит сделать всё
быстро и просто. Но быстро и просто можно сделать и самому, поэтому
посмотрим, как сделать всё через nmcli
:
$> nmcli con add type team ifname team0 con-name team0
Connection 'team0' (e6d71407-0c9f-4871-b6d4-60f62124809e) successfully added.
$> nmcli con add type team-slave ifname ens9 con-name team0-slave0 master team0
Connection 'team0-slave0' (b8dbe2f1-7d93-4c86-9c62-8470d44e1caf) successfully added.
Теперь у нас есть team интерфейс с одним настоящим интерфейсом и IP-адресом. Теперь, если ты зашёл в виртуалку по SSH, выйди и зайди снова используя IP team интерфейса — иначе потерешь соединения, выполняя следующую команду:
nmcli con delete eth0
Нам нужно убрать любые настроенные соединения у используемых для team интерфейсов. Добавляем второй интерфейс:
nmcli con add type team-slave ifname eth0 con-name team0-slave1 master team0
Connection 'team0-slave1' (d11ba15b-d13d-4605-b958-fe6d093d4417) successfully added.
Посмотрим на наш новый интерфейс и его адрес командой ip addr
:
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
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
2: ens9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master team0 state UP qlen 1000
link/ether 52:54:00:54:f4:75 brd ff:ff:ff:ff:ff:ff
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master team0 state UP qlen 1000
link/ether 52:54:00:54:f4:75 brd ff:ff:ff:ff:ff:ff
4: team0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 52:54:00:54:f4:75 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.71/16 brd 192.168.255.255 scope global dynamic team0
valid_lft 2859sec preferred_lft 2859sec
inet6 fe80::d865:f90b:50c3:e3d0/64 scope link
valid_lft forever preferred_lft forever
team0 выводится точно так же, как любой другой интерфейс, у него есть
IP-адрес, MAC-адрес и даже свой qdisc. Мы уже немного знаем про Traffic
Control, поэтому строчки типа qdisc pfifo_fast
и qlen 1000
нас не
удивляют. Ещё обратим внимание на то, что у все трёх интерфейсов теперь
одинаковые MAC-адреса — это поведение по-умолчанию, но его можно
изменить при желании.
Теперь немного поиграем с настройками нашего team. Teaming поддерживает
разные режимы работы, называемые runners
. Самый простой — это roundrobin
. Из названия
понятно, что интерфейсы, в которые будут идти пакеты, будут обрабатываться
по кругу. Есть ещё, например, activebackup
— в этом случае
используется только один интерфейс, и если он упадёт, трафик
автоматически пойдёт на следующий доступный. man teamd.conf
описывает
остальные доступные режимы. Примеры определения runners можно глянуть /usr/share/doc/teamd/example_configs/
(если такой папки нет, то попробуй locate example_configs
). Мы же попробуем activebackup
.
Цель упражнения такая: настроить activebackup
, в котором новый интерфейс будет основным, а медленный интерфейс (ограниченный при
помощи tc
) — бэкапом. А затем мы запустим в виртуалке скачку крупного
файла и "выдернем" новый, быстрый интерфейс из сервера.
nmcli con edit team0
nmcli> describe team.config
Последняя команда покажет примеры использования опции team.config:
Examples: set team.config { "device": "team0", "runner": {"name": "roundrobin"}, "ports": {"eth1": {}, "eth2": {}} }
set team.config /etc/my-team.conf
При грамотном использовании man
и прочих доступных в системе
информационных средств нам не нужно будет хаотично гуглить что попало.
Мы можем просто внимательно изучить локальную документацию и найти
подробнейшие описания с кучей примеров! Ещё один пример конфигурации мы
может обнаружить в man teamd.conf
:
{
"device": "team0",
"runner": {"name": "activebackup"},
"link_watch": {"name": "ethtool"},
"ports": {
"eth1": {
"prio": -10,
"sticky": true
},
"eth2": {
"prio": 100
}
}
}
Создадим файлик teaming-demo.conf с похожим содержимым, но обновленным для наших интерфейсов:
{
"device": "team0",
"runner": {"name": "activebackup"},
"link_watch": {"name": "ethtool"},
"ports": {
"ens9": {
"prio": 100
},
"eth0": {
"prio": 10
}
}
}
Здесь вы задали режим activebackup
и выдали интерфейсу ens9 (быстрому)
больший приоритет. Подгрузим эти настройки:
nmcli con mod team0 team.config ./teaming-demo.conf
При помощи teamdctl team0 state
убедимся, что настройки применились:
teamdctl team0 state
setup:
runner: roundrobin
ports:
ens9
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0
eth0
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0
Упс, ещё не применились! Дело в том, что конфиг загружается при создании
team-интерфейса. Воспользуемся этим как возможностью проверить, выдержат
ли наши настройки ребут, и перезапустим виртуалку. Если всё прошло
успешно, то после перезапуска мы сможем зайти в виртуалку по SSH,
выполнить teamdctl team0 state
и увидеть такое:
setup:
runner: activebackup
ports:
ens9
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0
eth0
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0
runner:
active port: ens9
Время вырвать интерфейс!
Проверяем работу teaming
В видео ниже я сначала зайду в виртуалку и стартану скачивание файла в отдельной сессии, а затем отцеплю основной интерфейс при помощи virsh. Вернувшись в сессию со скачкой файла, можно увидеть, что скорость упала до 128кбс, но само скачивание не прервалось, несмотря на то, что одного из интерфейсов не стало.
Пара слов о правильной автоматизации
Прежде чем перейти к итогам, снова поговорим о реальной жизни. Пара вещей, которые нужно держать в голове:
- Руками teaming настраивать каждый раз необязательно. Его можно настроить, например, при помощи Kickstart
nmcli
,tc
и другие утилиты очень полезны для быстрой настройки и дебага, но в реальном окружении нужно всё автоматизировать при помощи подходящих инструментов — например, Chef/Puppet/Ansible.
Подводим итоги
Изначально я думал, что это будет небольшая статья про teaming в Linux. Но как только я начал её писать, сразу понял, что придётся рассказать о многих других вещах.
Мы только что вживую потрогали свитчи, погрузившись в такие детали, как fdb.
Мы узнали, что такое tap-интерфейсы, и какую роль они играют в виртуализации сетей в Linux.
Мы посмотрели Linux Traffic Control, и теперь мы знаем чуть больше о
Linux networking stack. Теоретически, вооружившись tc
мы теперь
сможем выстраивать очень хитрые цепочки обработки сетевого трафика.
Наконец, мы пощупали teaming, убедившись в его эффективности. Возможно, однажды это пригодится нам в работе. А если нет, то мы, по крайней мере, будем знать о том, что так можно и как это работает.
Всю дорогу мы использовали libvirt и теперь знаем, насколько сильно он облегчает работу. За парой строчек XML-конфигурации скрывается сложная и гибкая настройка множества встроенных в Linux инструментов, работающих на самых разных уровнях. И в сумме эти инструменты и предоставляют виртуализацию, на которой, напомню, построены все современные облачные платформы.
Надеюсь, теперь сети стали для тебя чуточку понятней, виртуализация кажется не такой волшебной штукой, а зацепки и примеры использования дюжины разных инструментов помогут тебе более эффективно разбираться в любых связанных с инфраструктурой проблемах.
Дополнительное чтение
В ходе подготовки к этой статьи были прочитаны сотни статей, страниц документации и отрывков книг. Мы покрыли множество разных тем, но ни в одну не копнули по-настоящему глубоко. Ниже как раз ссылки для углублённого изучения материала:
man tc
,man teamd.config
,man bridge
,man brctl
- в первую очередь лучше смотреть документацию, которая уже доступна на машине.- teamd infrastructure specification расскажет о внутреннем устройстве teaming в Linux простыми словами создателей teamd
- Configure Network Teaming — документация по teaming от RedHat
- Linux Bridge and Virtual Networking — в этой статье автор показывает, как создать Linux bridge и подключить к нему пару интерфейсов самостоятельно, без помощи libvirt.
- Tap Interfaces and Linux Bridge — тот же автор, но на этот раз про tap-интерфейсы, в картинках и с очень хорошими объяснениями
- Tun/Tap interface tutorial — статья 2010 года, в которой показано создание новых интерфейсов кодом на C
- QEMU Networking — хорошие объяснения различных видов сетей в QEMU
- VLAN filter support on bridge — анонс поддержки VLAN для Linux Bridge
- Proper isolation of Linux bridge — о практическом применение VLAN в Linux Bridge, очень доступно и в картинках
- Linux Advanced Routing & Traffic ControlHOWTO — огромная мини-книжка про Traffic Control , с очень доступными объяснениями и иллюстрациями (в виде ASCII,правда)
- Differentiated Service on Linux HOWTO — иллюстрированное объеснение почти всех нужных типов qdisc. Посмотри там про HTB и sfq, чтобы мгновенно понять, как они работают.