- PVSM.RU - https://www.pvsm.ru -
Я познакомился с Docker довольно давно и, как и большинство его пользователей, был мгновенно очарован его мощью и простотой использования. Простота является основным столпом, на котором основывается Docker, чья сила кроется в легких CLI-командах. Когда я изучал Docker, я захотел выяснить, что происходит у него в бэкграунде, как вообще все происходит, особенно что касается работы с сетью (для меня это одна из самых интересных областей).
Я нашел много разной документации о том, как создавать контейнерные сети и управлять ими, но в отношении того, как именно они работают, материалов намного меньше. Docker широко использует Linux iptables и bridge-интерфейсы для создания контейнерных сетей, и в этой статье я хочу подробно рассмотреть именно этот аспект. Информацию я почерпнул, в основном, из комментариев на github-е, разных презентаций, ну и из моего собственного опыта. В конце статьи можно найти список полезных ресурсов.
Я использовал Docker версии 1.12.3 для примеров в этой статье. Я не ставил своей целью дать исчерпывающее описание Docker-сетей или написать полное введение в эту тему. Я надеюсь, что этот материал будет полезен для пользователей, и я буду рад, если вы в комментариях оставите обратную связь, укажете на ошибки или скажете, чего недостает.
Сеть Docker построена на Container Network Model (CNM), которая позволяет кому угодно создать свой собственный сетевой драйвер. Таким образом, у контейнеров есть доступ к разным типам сетей и они могут подключаться к нескольким сетям одновременно. Помимо различных сторонних сетевых драйверов, у самого Docker-а есть 4 встроенных:
Сетевые драйвера Bridge и Overlay, возможно, используются чаще всего, поэтому в этой статье я буду больше уделять им внимание.
По умолчанию для контейнеров используется bridge. При первом запуске контейнера Docker создает дефолтную bridge-сеть с одноименным названием. Эту сеть можно увидеть в общем списке по команде docker network ls
:
Чтобы проинспектировать ее свойства, запустим команду docker network inspect bridge
:
Вы также можете создать свои собственные bridge-сети при помощи команды docker network create
, указав опцию --driver bridge
.
Например, команда docker network create --driver bridge --subnet 192.168.100.0/24 --ip-range 192.168.100.0/24 my-bridge-network
создает еще одну bridge-сеть с именем “my-bridge-network” и подсетью 192.168.100.0/24.
Каждая bridge-сеть имеет свое представление в виде интерфейса на хосте. С сетью “bridge”, которая стоит по умолчанию, обычно ассоциируется интерфейс docker0, и с каждой новой сетью, которая создается при помощи команды docker network create
, будет ассоциироваться свой собственный новый интерфейс.
Чтобы найти интерфейс, который ассоциируется с сетью, которую вы создали, введите команду ifconfig
, чтобы вывести все интерфейсы, а затем найти тот интерфейс, который относится к созданной вами подсети. Например, если нам надо найти интерфейс для сети my-bridge-network, которую мы только что создали, то можно запустить такую команду:
Bridge-интерфейсы Linux похожи на свичи тем, что они присоединяют несколько интерфесов к одной подсети и перенаправляют трафик на основе MAC-адресов. Как будет видно ниже, у каждого контейнера, привязанного к bridge-сети, будет свой собственный виртуальный интерфейс на хосте, и все контейнеры в одной сети будут привязаны к одному интерфейсу, что позволит им передавать друг другу данные. Можно получить больше данных о статусе моста при помощи утилиты brctl
:
Как только мы запустим контейнеры и привяжем их к этой сети, интерфейс каждого из этих контейнеров будет выведен в списке в отдельной колонке. А если включить захват трафика в bridge-интерфейсе, то можно увидеть, как передаются данные между контейнерами в одной подсети.
Container Networking Model дает каждому контейнеру свое собственное сетевое пространство. Если запустить команду ifconfig
внутри контейнера, то можно увидеть его интерфейсы такими, какими их видит сам контейнер:
Впрочем, eth0, который представлен в этом примере, можно увидеть только изнутри контейнера, а снаружи, на хосте, Docker создает соответствующую копию виртуального интерфейса, которая служит связью с внешним миром. Затем эти виртуальные интерфейсы соединяются с bridge-интерфейсами, о которых мы говорили выше, чтобы легче установить связь между разными контейнерами в одной подсети.
Чтобы рассмотреть этот процесс, для начала запустим два контейнера, связанных с дефолтной bridge-сетью, а затем посмотрим на конфигурацию интерфейса хоста.
До запуска каких-либо контейнеров у bridge-интерфейса docker0 нет никаких других присоединенных интерфейсов:
Затем я запустил два контейнера на образе ubuntu:14.04:
Сразу стало видно, что два интерфейса присоединены к bridge-интерфейсу docker0 (по одному на каждый контейнер):
Если начать пинговать Google с одного из контейнеров, то захват трафика с хоста на виртуальном интерфейсе контейнера покажет нам трафик контейнеров:
Аналогично можно выполнить пинг от одного контейнера к другому.
Во-первых, надо получить IP-адрес контейнера. Это можно сделать либо при помощи команды ifconfig
, либо при помощи docker inspect
, что позволяет проинспектировать контейнер:
Затем начинаем пинг от одного контейнера к другому:
Чтобы увидеть трафик с хоста, мы можем сделать захват на любом из виртуальных интерфейсов, которые соотносятся с контейнерами, либо на bridge-интерфейсе (в данном случае docker0), что покажет нам все коммуникации внутри контейнеров данной подсети:
Если вы хотите узнать, какой veth-интерфейс хоста привязан к интерфейсу внутри контейнера, то простого способа вы не найдете. Однако, есть несколько методов, которые можно найти на разных форумах и в обсуждениях на github. Самый простой, на мой взгляд, способ я почерпнул из этого обсуждения на github [13], немного его изменив. Он зависит от того, присутствует ли ethtool
в контейнере.
Например, у меня в системе запущены 3 контейнера:
Для начала, я выполняю следующую команду в контейнере и получаю номер peer_ifindex:
Затем на хосте я использую peer_ifindex, чтобы узнать имя интерфейса:
В данном случае интерфейс называется veth7bd3604.
Docker использует linux iptables, чтобы контролировать коммуникации между интерфейсами и сетями, которые он создает. Linux iptables состоят из разных таблиц, но нам в первую очередь интересны только две из них: filter и nat. Таблица filter содержит правила безопасности, которые решают, допускать ли трафик к IP-адресам или портам. С помощью таблицы nat Docker дает контейнерам в bridge-сетях связываться с адресатами, которые находятся снаружи хоста (иначе пришлось бы добавлять маршруты к контейнерным сетям в сети хоста).
Таблицы в iptables состоят из различных цепочек, которые соответствуют разным состояниям или стадиям обработки пакета на хосте. По умолчанию, у таблицы filter есть 3 цепочки:
Input
для обработки входящих пакетов, предназначенных для того же хоста, на который они приходят;
Output
для пакетов, возникающих на хосте, предназначенных для внешнего адресата;
Forward
для обработки входящих пакетов, предназначенных для внешнего адресата.
Каждая цепочка включает в себя правила, которые определяют, какие действия и при каких условиях надо применить к пакету (например, отклонить или принять его). Правила обрабатываются последовательно, пока не будет найдено соответствие, иначе применяются дефолтные правила цепочки. Также в таблице можно задать кастомные цепочки.
Чтобы увидеть текущие правила цепочки и дефолтные установки в таблице filter, запустите команду iptables -t filter -L
или iptables -L
, если таблица filter используется по умолчанию и не указана никакая другая таблица:
Жирным выделены разные цепочки и дефолтные установки для каждой из них (у кастомных цепочек дефолтных установок нет). Также видно, что Docker добавил две кастомные цепочки: Docker и Docker-Isolation, также добавил правила в цепочку Forward, целью которых являются эти две новые цепочки.
Docker-isolation содержит правила, которые ограничивают доступ между разными сетями. Чтобы узнать подробности, добавляйте -v
, когда запускаете iptables:
Можно увидеть несколько правил DROP, которые блокируют трафик между всеми bridge-интерфейсами, которые создал Docker, и таким образом не дают сетям обмениваться данными.
Одна из опций, которую можно передать команде docker network create
, — это опция, которая отвечает за передачу данных внутри контейнера: com.docker.network.bridge.enable_icc
. Если задать ей значение false, то передача данных между контейнерами внутри одной сети будет заблокирована. Для этого нужно добавить DROP-правило в цепочку forward, которое соответствует пакетам, приходящим от bridge-интерфейса, связанного с сетью для данного интерфейса.
Например, если создать новую сеть при помощи команды docker network create --driver bridge --subnet 192.168.200.0/24 --ip-range 192.168.200.0/24 -o "com.docker.network.bridge.enable_icc"="false" no-icc-network
, то мы получим следующее:
С помощью nat можно поменять IP-адрес или порт пакета. В данном случае он используется, чтобы за IP-адресом хоста спрятать адреса источников пакетов, которые приходят от bridge-сетей (например, хосты в подсети 172.18.0.0/24) и направлены во внешний мир. Эта фича контролируется опцией com.docker.network.bridge.enable_ip_masquerade
, которую можно передать docker network create
(если не задать ничего специфического, то по умолчанию будет значение true).
Результат этой команды можно увидеть в таблице nat:
В этой цепочке postrouting можно увидеть все сети, которые созданы под действием masquerade, которое применяется, когда они передают данные любому хосту вне своей собственной сети.
Автор: Роман Моисеев
Источник [18]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/iptables/260967
Ссылки в тексте:
[1] Обзор сетей Docker: #obzor-setey-docker
[2] Сети типа мост (bridge): #seti-tipa-most-bridge
[3] Bridge-интерфейсы в Linux: #bridge-interfeysy-v-linux
[4] Виртуальные интерфейсы Linux: #virtualnye-interfeysy-linux
[5] Находим Veth-интерфейс в контейнере: #nahodim-veth-interfeys-v-konteynere
[6] iptables: #iptables
[7] Iptables:filter: #iptablesfilter
[8] Цепочка Docker-isolation: #cepochka-docker-isolation
[9] icc=false: #iccfalse
[10] iptables:nat: #iptablesnat
[11] Итог: #itog
[12] Ссылки/ресурсы: #ssylkiresursy
[13] этого обсуждения на github: https://github.com/moby/moby/issues/20224
[14] Docker networking concepts: https://github.com/docker/labs/tree/master/networking/concepts
[15] Deep dive into Docker 1.12 Networking: https://www.youtube.com/watch?v=nXaF2d97JnE
[16] Docker container networking user guide: https://docs.docker.com/engine/userguide/networking/
[17] Linux iptables overview: https://wiki.archlinux.org/index.php/Iptables#Tables
[18] Источник: https://habrahabr.ru/post/333874/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.