Mininet — эмулятор компьютерной сети

mininet

 

 

Статья опубликована в журнале «Системный Администратор»

Освоим построение и конфигурирование виртуальных компьютерных сетей любых масштабов в рамках одной виртуальной машины.

Что это и для чего

Работая над статьей, посвященной Open vSwitch, проходя по различным ссылкам в поисках хоть какой-нибудь полезной информации, открыл для себя проект под названием mininet [1]. Будучи активным читателем технической и компьютерной литературы, журналов и новостей, думал, что все самое интересное я уже изучил и попробовал.  Но, этот проект меня искренне удивил. Я не уверен в практической ценности данного решения для большинства читателей, но мой личный интерес к нему подтолкнул к написанию статьи.
Mininet — это эмулятор компьютерной сети. Под компьютерной сетью подразумеваются простые компьютеры — хосты, коммутаторы, а так же OpenFlow-контроллеры. С помощью простейшего синтаксиса в примитивном интерпретаторе команд можно разворачивать сети из произвольного количества хостов, коммутаторов в различных топологиях и все это в рамках одной виртуальной машины(ВМ). На всех хостах можно изменять сетевую конфигурацию, пользоваться стандартными утилитами(ipconfig, ping) и даже получать доступ к терминалу. На коммутаторы можно добавлять различные правила и маршрутизировать трафик. В общем, получается довольно интересная вещь, позволяющая познакомиться с устройством и функционированием компьютерных сетей без необходимости использования какого либо сетевого оборудования.

Как это работает

Начиная с версии 2.6.24, ядром Linux поддерживаются механизмы виртуализации и изоляции – Cgroups[2], которые позволяют обеспечить сетевыми интерфейсами, таблицами маршрутизации и ARP-таблиами процессы  в рамках одной операционной системы. Это один из видов виртуализации на уровне ОС, позволяющий запустить множество однотипных процессов в изолированном и ограниченном по ресурсам окружении. Подобные техники позволяют Mininet создавать в пространстве ядра или пользователя коммутаторы, OpenFlow-контроллеры  и хосты, и взаимодействовать в рамках моделируемой сети. В качестве виртуальных коммутаторов используется адаптированная реализация Open vSwitch’a.
Основная функциональность Mininet реализована на Python, за исключением некоторых утилит написанных на Си. Практически любая произвольная топология может быть описана с помощью специального синтаксиса на Python. В интернете можно найти множество интересных лабораторных работ на базе mininet, решающих различные задачи. Например реализация простого маршрутизатора[3].

Как этим пользоваться

Вся работа с виртуальной сетью mininet, а именно развертывание сети желаемой топологии, изменение различных параметров хостов или коммутаторов и т. п., производится в простом интерпретаторе команд – mn.

$ sudo mn

Запущенный без параметров, mn перейдет в режим интерпретации команд. При этом по умолчанию будет создана минимальная сеть, состоящая из двух хостов (h1, h2), коммутатора (s1) и OpenFlow-контроллера (c1).

$ sudo mn

*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
*** Starting 1 switches
s1
*** Starting CLI:
mininet>

Интерпретатор mininet использует ряд собственных команд, позволяющих управлять виртуальной сетью практически как настоящей. Ниже приведены примеры основных возможностей.

Вывести список всех хостов, коммутаторов и контроллеров можно с помощью команды nodes

mininet> nodes

available nodes are:
h1 h2 c0 s1

посмотреть топологию сети, а именно сопоставление портов коммутатора и хостов можно с помощью команды net:

mininet> net

c0
s1 lo:  s1-eth1:h1-eth0 s1-eth2:h2-eth0
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2

вывести конфигурацию сетевого интерфейса конкретного хоста можно с помощью классической команды ifconfig перед которой необходимо указать имя конкретного узла:

mininet> h1 ifconfig

h1-eth0   Link encap:Ethernet  HWaddr 96:0d:f2:1a:e3:91
inet addr:10.0.0.1  Bcast:10.255.255.255  Mask:255.0.0.0
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:11 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:846 (846.0 B)  TX bytes:468 (468.0 B)

lo        Link encap:Local Loopback
inet addr:127.0.0.1  Mask:255.0.0.0
UP LOOPBACK RUNNING  MTU:16436  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)

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

Любой из портов коммутатора можно выключить и включить по желанию:

mininet> link s1 h1 down
mininet> link s1 h1 up

Посмотреть таблицу маршрутизации конкретного хост можно аналогично с использованием привычной команды route:

mininet> h1 route

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.0.0.0        *               255.0.0.0       U     0      0        0 h1-eth0

Ну и конечно же выполнить пинг, как же без него:

mininet> h1 ping h2

PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_req=1 ttl=64 time=4.11 ms
64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=0.252 ms
^C
— 10.0.0.2 ping statistics —
2 packets transmitted, 2 received, 0% packet loss, time 4999ms
rtt min/avg/max/mdev = 0.066/0.779/4.119/1.495 ms

пинг каждого с каждым:

mininet> pingall

*** Ping: testing ping reachability
h1 -> h2
h2 -> h1
*** Results: 0% dropped (0/2 lost)

В принципе, на каждом из хостов, указывая предварительно его имя, можно выполнять большинство стандартных команд linux. Например посмотреть процессы любого из хостов или коммутаторов поможет все тот же ps:

mininet> s1 ps

PID TTY          TIME CMD
1 ?        00:00:00 init
2 ?        00:00:00 kthreadd
3 ?        00:00:00 ksoftirqd/0

(вывод сокращен)

при этом любой из процессов можно завершить с помощью стандартного kill -9.

Кроме проверки доступности узлов с помощью ping можно еще протестировать пропускную способность между узлами с помощью старого доброго iperf:

mininet> iperf h1 h2

*** Iperf: testing TCP bandwidth between h1 and h2
waiting for iperf to start up…*** Results: [‘1.35 Gbits/sec’, ‘1.36 Gbits/sec’]

Забегая немного вперед, скажу, что пропускная способность интерфейсов может быть ограниченна от 10 до 1000 Mbit/s. Но об этом немного ниже.

Ну и в конце концов, можно просто получить терминал к любому из узлов:

mininet>  xterm h1

Дополнительные сервисы

На каждом из виртуальных хостов, помимо стандартных процессов есть возможность запускать сторонние сервисы. Например это может быть простой веб-сервер на Python:

mininet> h1 python -m SimpleHTTPServer 80 &

и попробовать подключиться к нему с другого узла:

mininet> h2 wget -O — h1

—2013-11-06 03:44:17—  http://10.0.0.1/
Connecting to 10.0.0.1:80… connected.
HTTP request sent, awaiting response… 200 OK
Length: 248 [text/html]
Saving to: `STDOUT’
<!DOCTYPE html PUBLIC «-//W3C//DTD HTML 3.2 Final//EN»><html>
<title>Directory listing for /</title>

(вывод сокращен)

можно завершить работу веб-сервера, если необходимо:

mininet> h1 kill %python

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

Корректно свернуть всю виртуальную сеть и выйти в оболочку ОС можно командой quit.
В случае если работа интерпретатора была завершена некорректно, избавиться от повисших процессов и прочих служебных данных можно командой

$ sudo mn -c

Усложним топологию

Как и в реальном, физическом мире, построение сети в эмуляторе Mininet начинается с топологии. Топология определяет сколько будет хостов, коммутаторов а так же каким образом они будут объедены в сеть.

Существует четыре базовых топологи, которые можно использовать, «не заморачиваясь» с синтаксисом Python — они уже описаны и реализованы в виде параметров mn. Ниже приведены подробности про каждую из них.

mininal. Используется по умолчанию при запуске mn без параметров. В этом случае создаются два хоста, подключенные к одному коммутатору, который, в свою очередь управляется OpenFlow-контроллером. В данной топологии нельзя задать произвольное число хостов или коммутаторов.

single. Как и в случае с minimal, все хосты подключаются к одному коммутатору. Единственное отличие — это возможность указать их количество.

$ sudo mn —topo single,24

*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12 h13 h14 h15 h16 h17 h18 h19 h20 h21 h22 h23 h24
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1) (h3, s1) (h4, s1) (h5, s1) (h6, s1) (h7, s1) (h8, s1) (h9, s1) (h10, s1) (h11, s1) (h12, s1) (h13, s1) (h14, s1) (h15, s1) (h16, s1) (h17, s1) (h18, s1) (h19, s1) (h20, s1) (h21, s1) (h22, s1) (h23, s1) (h24, s1)
*** Configuring hosts
h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12 h13 h14 h15 h16 h17 h18 h19 h20 h21 h22 h23 h24
*** Starting controller
*** Starting 1 switches
s1
*** Starting CLI:
mininet>

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

$ sudo mn —topo linear,6

*** Creating network
*** Adding controller
*** Adding hosts:
h1 h2 h3 h4 h5 h6
*** Adding switches:
s1 s2 s3 s4 s5 s6
*** Adding links:
(h1, s1) (h2, s2) (h3, s3) (h4, s4) (h5, s5) (h6, s6) (s1, s2) (s2, s3) (s3, s4) (s4, s5) (s5, s6)
*** Configuring hosts
h1 h2 h3 h4 h5 h6
*** Starting controller
*** Starting 6 switches
s1 s2 s3 s4 s5 s6
*** Starting CLI:
mininet>

tree. Древовидная топология, наиболее сложная из перечисленных. Здесь в качестве параметров можно указать глубину иерархии коммутаторов (depth) а так же число подключенных к ним хостов (fanout).

$ sudo mn —topo tree,depth=3,fanout=4

Команда, представленная выше построит сеть с классической трехуровневой моделью. Когда к каждому коммутатору уровня доступа(access) будут подключены по четыре хоста. Коммутаторы доступа в свою очередь будут включены в распределение(distribution) из четырех коммутаторов которые в свою очередь заводятся в единое ядро(core). К сожалению, такие возможности как стекирование коммутаторов или агрегация каналов а так же стандартные VLAN’ы не реализованы в mininet, что делает данную модель не совсем реалистичной. Но в общих чертах она довольно схожа.

В случае если есть базовые познания Python и необходима особенная топология, ее можно описать самостоятельно, аналогично примеру указанному в ссылках[4].

Построить сеть используя собственную топологию, можно с помощью ключа —custom:

$ sudo mn —custom /<путь к скрипту>/topo-2sw-2host.py —topo mytopo

Параметры сети

По умолчанию, все объекты сети mininet соединяются виртуальными гигабитными каналами.
В этом можно убедиться выполнив команду iperf в интерпретаторе mininet, которая проведет тестирование пропускной способности между двумя объектами сети.

mininet> iperf

*** Iperf: testing TCP bandwidth between h1 and h2
waiting for iperf to start up…*** Results: [‘1.26 Gbits/sec’, ‘1.28 Gbits/sec’]
mininet>

Правда, не совсем понятно, почему тест показывает немного завышенные результаты. Думаю это как то связанно с особенностями виртуальной среды. Возможно, при использовании каналов по умолчанию (1 Gb), ограничения не применяются вовсе.

Следует иметь в виду, что коммутатор, работающий в пользовательском пространстве, а не в пространстве ядра, выполняется значительно медленнее.

$ sudo mn —switch user —test iperf


*** Iperf: testing TCP bandwidth between h1 and h2
*** Results: [‘360 Mbits/sec’, ‘360 Mbits/sec’]

(вывод сокращен)

При желании, можно ограничить пропускную способность каналов до произвольных значений. А в добавок к этому есть возможность указать задержки в канале(latency).

$ sudo mn —link tc,bw=10,delay=10ms

Команда выше, разворачивает сеть по умолчанию, с пропускной способностью между узлами ограниченной в 10 Mbit/s и минимальными задержками в 10 ms.

Теперь пинг между узлами будет идти с задержкой в 10 ms.

Выводы

Виртуализация, везде и всюду. Начиная от уже классической серверной и заканчивая виртуальными Ethernet-портами в современных блейд-системмах. Виртуализируется все, и mininet — еще один хороший тому пример. Возможно, именно этим проект меня и зацепил. Даже сейчас, познакомившись поближе, честно говоря не вижу в нем какой то практической ценности. Особенно, для уже состоявшегося технического специалиста. Но сама реализация интересна и возможно, это решение сможет послужить для кого то инструментом моделирования а различные эксперименты помогут разобраться в принципах работы вычислительных сетей. В Интеренете доступны различные практические задачи и лабораторные работы построенные на базе mininet. Они широко используются в таких небезызвестных учебных заведениях, как Стэнфорд, Принстон и возможно, будут полезны вам.

Ссылки

[1] Пожалуй единственная информация о mininet на русском — http://www.xakep.ru/post/60886/
[2] Технология Cgroups —  http://ru.wikipedia.org/wiki/Cgroups
[3] Реализация маршрутизатора в mininet — https://github.com/mininet/mininet/wiki/Simple-Router
[4] Пример скрипта на Python описывающего произвольную топологию — http://pastebin.com/YS6aguDR