Глава 1. Букварь построения сети Docker

Docker является технологией обладающих малым весом контейнеров, которая в последние годы привлекает пристальное внимание. Она аккуратно связывает различные свойства и функции ядра Linux, такие как имена пространств, cgroup, SELinux и профили AppAromor поверх совокупности файловых систем, таких как AUFS и BTFFS для того чтобы делать образы модулей. Такие образы обеспечивают среду виртуализации с высокой степенью настройки для приложений, которая следует в потоке написанное единожды работает везде. Приложение может состоять из одного работающего в своём собственном контейнере процесса или может быть выполнено из множества процессов, работающих в своих собственных контейнерах, которые способны реплицироваться по мере роста нагрузки. Более того, существует потребность в мощных элементах сетевой среды, которые могут поддерживать различные сложные варианты применения.

В этой главе мы изучим существенные компоненты построения сети Docker и того как строить и выполнять простые примеры контейнеров.

Данная глава охватит следующие темы:

  • Построение сетевой среды и Docker

  • Построение сетевого моста docker0

  • Построение сетевой среды Docker OVS

  • Домены сетей Unix

  • Связывание контейнеров Docker

  • Что нового в построении сетей Docker

Docker получает в промышленности мощную тягу благодаря своей разумной производительности и воспроизводимости архитектуры, обеспечивая при этом четыре краеугольных основы современного развития:

  • Автономность

  • Децентрализацию

  • Параллелизм

  • Изолированность

Более того, широкомасштабное внедрение архитектуры микрослужб ThoughtWorks, или LOSA Lots of Small Applications, продолжает привносить потенциал в технологию Docker. В результате, крупные компании наподобие Google, VMWare и Microsoft уже портировали Docker в свою инфраструктуру, а импульс продолжается запуском бесчисленных стартапов Docker, а именно Tutum, Flocker, Giantswarm и тому подобных.

Поскольку Docker повсеместно воспроизводит своё поведение в любом месте, будь то ваша машина разработки, сервер с голым железом, виртуальная машина или центр обработки данных, разработчики приложений могут сосредоточить своё внимание на развитии, в то время как семантика выполнения остаётся за DevOps. Это делает команду рабочего потока модульной, эффективной и продуктивной. Docker не следует путать с Виртуальной машиной (ВМ, VM), несмотря на то, что обе являются технологиями виртуализации. В то время как Docker совместно использует OS обеспечивая достаточный уровень изоляции и безопасности работающим в контейнере приложениям, он далее полностью абстрагируется от ОС предоставляя сильную изоляцию и гарантии безопасности. Однако, отпечатки ресурса Docker очень незначительны по сравнению с ВМ и, следовательно, предпочтительнее с точки зрения экономичности и производительности. Тем не менее, он не может полностью заменить ВМ и поэтому они являются дополняющими друг друга технологиями. Следующая схема отображает архитектуру ВМ и Docker:

 

Рисунок 1.1. Архитектура ВМ и Docker


 Построение сетей и Docker

Каждый контейнер Docker имеет свой собственный сетевой стек, причём это происходит благодаря пространству имён NET ядра Linux, в котором для каждого контейнера конретизируется новое пространство имён NET и оно не может быть доступно снаружи контейнера или из других контейнеров.

Построение сетей Docker получает свою мощность от следующих сетевых компонентов и служб.

  Мосты Linux

Это встроенный в ядро обучаемые коммутаторы L2/MAC, причём они должны применяться для переадресации.

  Open vSwitch

Это усовершенствованный мост, который является программируемым и поддерживает туннелирование.

  NAT

Конверторы сетевых адресов являются непосредственными объектами, выполняющими трансляцию IP адресов и портов (SNAT, DNAT и тому подобное).

  IPtables

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

  AppArmor/SELinux

Здесь могут быть определены политики межсетевого экранирования для каждого приложения.

Для работы с Docker могут применяться различные сетевые компоненты, предоставляющие новые способы для доступа и использования служб на основе Docker. Как результат, мы видим большое число библиотек, которые следуют различным подходам построения сетей. Некоторыми из достойных упоминания являются Docker Compose, Weave, Kubernetes, Pipework, libnetwork и тому подобные. Следующий рисунок описывает ключевые идеи построения сети Docker:

 

Рисунок 1.2. Основные элементы построения сетей Docker


 Мост docker0

Мост docker0 является сердцем построения сети по умолчанию. При запуске службы Docker, на машине хоста создаётся мост Linux. Интерфейсы контейнера общаются с этим мостом, а сам мост выступает в качестве прокси внешнего мира. Множество контейнеров на одном и том же хосте могут взаимодействовать друг с другом через этот мост Linux.

Обычно, docker0 может быть настроен флагами --net в четырёх режимах:

  • --net default

  • --net=none

  • --net=container:$container2

  • --net=host

  Режим --net default

В данном режиме мост по умолчанию применяется мостом для соединения контейнеров друг с другом.

  Режим --net=none

В этом режиме созданные контейнеры полностью изолированы и не могут соединяться с сетевой средой.

  Режим --net=container:$container2

При помощи данного флага данный контейнер создаёт в пространстве имён сети совместный ресурс с контейнером, именуемым $container2.

  Режим --net=host

При данном режиме контейнер создаёт совместные ресурсы своего сетевого пространства имён с хостом.

 

Соответствие порта в контейнере Docker

В этом разделе мы увидим как порты контейнера приводятся в соответствие портам хоста. Такое соответствие может выполняться неявно механизмом Docker или может определяться в явном виде.

Мы создадим два контейнера с именами Container1 и Container2, причём оба назначим на IP адрес из частного адресного пространства IP и к тому же соединённых с мостом docker0, как показано на следующей иллюстрации:

 

Рисунок 1.3. Соединение двух контейнеров в режиме --net=host


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

Для внешнего доступа их порт должен соответствовать порту хоста.

Как уже упоминалось в предыдущем разделе, контейнеры используют сетевые пространства имён. Когда создаётся первый контейнер, для этого контейнера создаётся новое сетевое пространство имён. Между каждым контейнером и мостом Linux создаётся соединение (link) vEthernet. Исходящий из eth0 трафик достигает моста хоста через интерфейс vEthernet и после этого выполняется коммутация. Для отображения списка мостов Linux можно применить следующий код:


# show linux bridges
$ sudo brctl show
	   

Вывод будет аналогичен приводимому ниже, причём будет приведено соответствие имени моста и интерфейсов veth ваших контейнеров:


bridge name        bridge id        STP enabled    interfaces
docker0        8000.56847afe9799        no         veth44cb727
                                                   veth98c3700
	   

Как получить доступ к контейнеру из внешнего мира?

Опять же, с помощью параметра iptables nat на машине хоста устанавливается соответствие порта.

 

Рисунок 1.4. Организация доступа к контейнеру извне


 Docker OVS

Open vSwitch является мощной абстракцией сетевой среды. Следующая иллюстрация показывает как OVS взаимодействует с ВМ, Гипервизором и Физическим Коммутатором. Каждая ВМ имеет связанную с ней vNIC. Каждый vNIC соединяется через VIF (также называемый виртуальным интерфейсом, virtual interface) со своим Виртуальным Коммутатором:

 

Рисунок 1.5. Организация доступа к контейнеру извне


OVS применяет механизм туннелирования, например GRE, VXLAN или STT для создания виртуальных оверлеев вместо применения физического построения топологий и компонентов Ethernet. Следующий рисунок отображает то, как OVS может быть настроен для того, чтобы контейнеры могли взаимодействовать между множеством хостов с применением туннелей GRE:

 

Рисунок 1.6. Организация доступа к контейнеру извне


 Сокет домена Unix

В пределах одного хоста для взаимодействия между контейнерами также можно применять UNIX IPC механизм, в особенности сокеты домена UNIX или конвейеры (pipe):


$ docker run --name c1 –v /var/run/foo:/var/run/foo –d –I –t base /bin/bash
$ docker run --name c2 –v /var/run/foo:/var/run/foo –d –I –t base /bin/bash
	   

Приложения в c1 и c2 могут взаимодействовать через следующие адреса сокетов Unix:


struct  sockaddr_un address;
address.sun_family = AF_UNIX;
snprintf(address.sun_path, UNIX_PATH_MAX, "/var/run/foo/bar" );
	   

C1: Server.c

C2: Client.c


bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un));
listen(socket_fd, 5);
while((connection_fd = accept(socket_fd, (struct sockaddr *)
&address, &address_length)) > -1)
nbytes = read(connection_fd, buffer, 256);
			


connect(socket_fd, (struct sockaddr *) &address,
sizeof(struct sockaddr_un));
write(socket_fd, buffer, nbytes);
			

 Связывание контейнеров Docker

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

Помимо переменной окружения env, Docker также добавляет запись хоста для контейнера источника в файл /etc/hosts. Далее приводится пример такого файла хостов:


$ docker run -t -i --name c2 --rm --link c1:c1alias training/webapp /bin/bash
root@<container_id>:/opt/webapp# cat /etc/hosts
172.17.0.1 aed84ee21bde
...
172.17.0.2 c1alaias 6e5cdeb2d300 c1
	   

Присутствуют две записи:

  • первая является записью для вашего контейнера c2, которая применяет идентификатор контейнера Docker в качестве имени хоста.

  • Вторая записиь, 172.17.0.2 c1alaias 6e5cdeb2d300 c1 использует псевдоним связи для ссылки на IP адрес вашего контейнера c1.

Следующий рисунок показывает два контейнера, Container1 и Container2, соединённые с использованием пар veth с мостом docker0 с --icc=true. Это означает, что два данные контейнера могут осуществлять доступ друг к другу через этот мост:

 

Рисунок 1.7.Взаимодействие контейнеров через мост хоста


  Соединения

Соединения (link) обеспечивают службу обнаружения для Docker. Они позволяют контейнерам обнаруживать друг друга и безопасно взаимодействовать с применением флага -link name:alias. При помощи этого флага, установленного в значение false, Container1 не может получить доступ к Container2 пока он не будет дозволен в явном виде через соединение. Это громадное преимущество для безопасности ваших контейнеров. Когда два контейнера соединены вместе, Docker создаёт между ними взаимосвязь родитель- ребёнок, как продемонстрировано на следующей схеме:

 

Рисунок 1.8. Организация безопасного взаимодействия между контейнерами


Извне это выглядит следующим образом:


# start the database
$ sudo docker run -dp 3306:3306 --name todomvcdb \
-v /data/mysql:/var/lib/mysql cpswan/todomvc.mysql

# start the app server
$ sudo docker run -dp 4567:4567 --name todomvcapp \
--link todomvcdb:db cpswan/todomvc.sinatra
	   

Изнутри это выглядит так:


$ dburl = ''mysql://root:pa55Word@'' + \ ENV[''DB_PORT_3306_TCP_ADDR''] +
''/todomvc''
$ DataMapper.setup(:default, dburl)
	   

 Что нового в построении сетей Docker?

Построение сетей Docker находится на ранней стадии зарождения и существует множество интересных вкладов из сообщества разработчиков, например, Pipework, Weave, Clocker и Kubernetes. Каждый из них отражает отличный аспект построения сетей Docker. Мы познакомимся с ними в последующих главах. Помимо этого, Docker, Inc. объявило новый проект, в котором построение сетей приводится к стандарту. Он называется libnetwork

libnetwork реализует модель сетевого контейнера (CNM, container network model), которая формализует этапы, необходимые для обеспечения построения сетей контейнеров путём предоставления абстракций, которые могут применяться для поддержки многих сетевых драйверов. CNM строится из трёх основных компонентов - песочницы (sandbox), терминала (endpoint) и сетевой среды (network).

  Песочница

Песочница (sandbox) содержит настройки сетевого стека контейнера. Они содержат управление интерфейсами контейнера, таблицу маршрутизации и установки DNS. Реализацией песочницы может быть сетевое пространство имён Linux, клета (jail) FreeBSD, либо другое аналогичное понятие. Песочница может содержать множество терминалов из многих сетевых сред.

  Терминал

Терминал (endpoint) соединяет песочницу с сетевой средой. Реализацией терминала может быть пара veth или внутренний порт Open vSwitch, или нечто подобное. Терминал может относиться только к одной сетевой среде, и при этом может относиться только к одной песочнице.

  Сетевая среда

Сетевая среда является группой терминалов, которые способны напрямую взаимодействовать друг с другом. Реализацией сетевой среды может быть мост Linux, VLAN и тому подобное. Сетевая среда состоит из множества терминалов, как показано на следующей схеме:

 

Рисунок 1.9. Пример сетевой среды


 Модель Docker CNM

CNM обеспечивает следующее соглашение между сетевыми средами и контейнерами:

  • Все контейнеры в одной и той же сетевой среде могут свободно взаимодействовать друг с другом

  • Множество сетевых сред является способом сегментации обмена между контейнерами и оно должно поддерживаться всеми драйверами

  • Множество терминалов на контейнер является способом объединения контейнеров во множество сетевых сред

  • Для обеспечения песочниц сетевой среды сетевой связностью добавляются терминалы

Мы обсудим подробности того, как реализуется CNM в Главе 6. ледующее поколение сетевого стека для Docker: libnetwork.

 Заключение

В этой главе мы изучили существенные компоненты построения сетей Docker, которые развиваются из сочетания простых абстракций Docker и мощных сетевых компонентов, таких как мосты Linux и Open vSwitch.

Мы узнали как контейнеры Docker могут быть созданы в различных режимах. В режиме по умолчанию соответствие портов осуществляется при помощи применения правил NAT iptable, делая возможным возникающий в хосте обмен доставлять контейнерам. Позже в этой главе мы охватили основы связывания контейнеров. Мы также обсудили следующее поколение построения сетей Docker, называемое libnetwork.