### Обзор сетей Docker Сеть Docker построена на Container Network Model (CNM), которая позволяет кому угодно создать свой собственный сетевой драйвер. Таким образом, у контейнеров есть доступ к разным типам сетей и они могут подключаться к нескольким сетям одновременно. Помимо различных сторонних сетевых драйверов, у самого Docker-а есть 4 встроенных: * Bridge: в этой сети контейнеры запускаются по умолчанию. Связь устанавливается через bridge-интерфейс на хосте. У контейнеров, которые используют одинаковую сеть, есть своя собственная подсеть, и они могут передавать данные друг другу по умолчанию. * Host: этот драйвер дает контейнеру доступ к собственному пространству хоста (контейнер будет видеть и использовать тот же интерфейс, что и хост). Macvlan: этот драйвер дает контейнерам прямой доступ к интерфейсу и суб-интерфейсу (vlan) хоста. Также он разрешает транкинг. * Overlay: этот драйвер позволяет строить сети на нескольких хостах с Docker (обычно на Docker Swarm кластере). У контейнеров также есть свои адреса сети и подсети, и они могут напрямую обмениваться данными, даже если они располагаются физически на разных хостах. Сетевые драйвера Bridge и Overlay, возможно, используются чаще всего, поэтому в этой статье я буду больше уделять им внимание. #### Сети типа мост (bridge) По умолчанию для контейнеров используется bridge. При первом запуске контейнера Docker создает дефолтную bridge-сеть с одноименным названием. При запуске контейне-ра Docker создает новый экземпляр пары veth (фактически программный аналог кабеля Ethernet), соединяющий интерфейс eth0 в контейнере со шлюзом. Внешние соединения обслуживаются при помощи механизма перенаправления IP forwarding и правил сетевого экрана iptables, которые определяют конфигурацию механизма преобразования IP-адресов IP masquerading, одну из форм механизма NAT (Network Address Translation). ![Структура данных буферного кэша](https://whoisdeveloper.ru/static/img/docker0.png) ``` nick@MacBook-Pro-DNA ~ % docker network ls NETWORK ID NAME DRIVER SCOPE 3fdff1bfb8ed bridge bridge local 45178a860cc9 host host local 741de63e7bf7 none null local ``` По умолчанию все контейнеры могут обмениваться данными друг с другом, независимо от того, установлены ли соединения между ними и объявлены ли открытые или экспортируемые порты. Свободный обмен данными можно отменить, отключив эту возможность при запуске демона Docker с помощью флага --icc=false, который устанавливает соответствующее правило сетевого экрана iptables. Значения флагов --icc=false и --iptables=true позволяют обмениваться данными только контейнерам с установленными соединениями, и это тоже реализуется добавлением соответствующих правил iptables. Чтобы проинспектировать ее свойства, запустим команду docker network inspect bridge: ``` nick@MacBook-Pro-DNA ~ % docker inspect bridge [ { "Name": "bridge", "Id": "3fdff1bfb8edafcab54fbf946d820f27c31eec3233198c44cf2bb6907f1a77bd", "Created": "2022-05-16T19:22:37.390793541Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "c191fed717d007c62645919607fe94a62ba5ad526c74e1d5104184f7f6972ae1": { "Name": "admiring_agnesi", "EndpointID": "efd6ccd31013c996cb81ea017b9b468903e2a467186275c339b8aaac33628e1b", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ] ``` Вы также можете создать свои собственные bridge-сети при помощи команды docker network create, указав опцию --driver bridge. При разработке все это удобно и полезно, но проблемы производительности делают этот режим не вполне подходящим для эксплуатации. #### Bridge-интерфейсы в Linux Bridge-интерфейсы Linux похожи на свичи тем, что они присоединяют несколько интерфесов к одной подсети и перенаправляют трафик на основе MAC-адресов. Как будет видно ниже, у каждого контейнера, привязанного к bridge-сети, будет свой собственный виртуальный интерфейс на хосте, и все контейнеры в одной сети будут привязаны к одному интерфейсу, что позволит им передавать друг другу данные. Можно получить больше данных о статусе моста при помощи утилиты brctl. Как только мы запустим контейнеры и привяжем их к этой сети, интерфейс каждого из этих контейнеров будет выведен в списке в отдельной колонке. А если включить захват трафика в bridge-интерфейсе, то можно увидеть, как передаются данные между контейнерами в одной подсети. #### Виртуальные интерфейсы Linux Container Networking Model дает каждому контейнеру свое собственное сетевое пространство. Если запустить команду ifconfig внутри контейнера, то можно увидеть его интерфейсы такими, какими их видит сам контейнер. ### iptables Docker использует linux iptables, чтобы контролировать коммуникации между интерфейсами и сетями, которые он создает. Linux iptables состоят из разных таблиц, но нам в первую очередь интересны только две из них: filter и nat. Таблица filter содержит правила безопасности, которые решают, допускать ли трафик к IP-адресам или портам. С помощью таблицы nat Docker дает контейнерам в bridge-сетях связываться с адресатами, которые находятся снаружи хоста (иначе пришлось бы добавлять маршруты к контейнерным сетям в сети хоста). #### iptables:filter Таблицы в iptables состоят из различных цепочек, которые соответствуют разным состояниям или стадиям обработки пакета на хосте. По умолчанию, у таблицы filter есть 3 цепочки: Input для обработки входящих пакетов, предназначенных для того же хоста, на который они приходят; Output для пакетов, возникающих на хосте, предназначенных для внешнего адресата; Forward для обработки входящих пакетов, предназначенных для внешнего адресата. Каждая цепочка включает в себя правила, которые определяют, какие действия и при каких условиях надо применить к пакету (например, отклонить или принять его). Правила обрабатываются последовательно, пока не будет найдено соответствие, иначе применяются дефолтные правила цепочки. Также в таблице можно задать кастомные цепочки. Чтобы увидеть текущие правила цепочки и дефолтные установки в таблице filter, запустите команду iptables -t filter -L или iptables -L, если таблица filter используется по умолчанию и не указана никакая другая таблица: #### Цепочка Docker-isolation Docker-isolation содержит правила, которые ограничивают доступ между разными сетями. #### icc=false Одна из опций, которую можно передать команде 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, то мы получим следующее: #### iptables:nat С помощью nat можно поменять IP-адрес или порт пакета. В данном случае он используется, чтобы за IP-адресом хоста спрятать адреса источников пакетов, которые приходят от bridge-сетей (например, хосты в подсети 172.18.0.0/24) и направлены во внешний мир. Эта фича контролируется опцией com.docker.network.bridge.enable_ip_masquerade, которую можно передать docker network create (если не задать ничего специфического, то по умолчанию будет значение true). ### Режим host Контейнер, запущенный с флагом --net=host, использует пространство сетевых имен хоста, становясь полностью открытым для внешней сети. Это означает, что такой контейнер использует IP-адрес хоста совместно с другими контейнерами, но при этом исключается необходимость ручной работы, требуемой в режиме bridge, таким образом, сеть работает так же быстро, как и обычная сеть, состоящая из хостов. Поскольку IP-адрес используется совместно, контейнеры, обменивающиеся данными друг с другом, должны правильно распределить между собой порты на хосте, поэтому необходимо тщательно продумать и, возможно, изменить структуру приложений. Существенное улучшение производительности позволяет рассмотреть возможность реализации гибридной сетевой модели, в которой внешние контейнеры с большой сетевой нагрузкой, такие как прокси и кэши, используют сетевой режим host, а остальные контейнеры работают во внутренней сети в режиме bridge. Отметим невозможность установления прямых соединений между контейнерами, работающими в режиме bridge, с контейнерами, работающими в режиме host, но контейнеры в bridge-сети могут обмениваться данными с контейнерами host-сети, используя для этого IP-адрес шлюза docker0. ### Режим container В этом режиме используется пространство сетевых имен из другого контейнера. В некоторых ситуациях это очень удобно (например, при запуске контейнера с предварительно сконфигурированным сетевым стеком необходимо, чтобы все прочие контейнеры также использовали этот стек). Это позволяет создавать и многократно использовать эффективные конфигурации сетевых стеков, специализированных для конкретных вариантов использования или для архитектур центров данных. Недостаток заключается в том, что все контейнеры, совместно использующие сетевой стек, вынуждены пользоваться одним IP- адресом и т. д. ### Режим none В этом режиме сетевая среда становится полностью недоступной для контейнера. Это может оказаться удобным для контейнеров, которым сеть вообще не нужна, например для контейнера-компилятора, записывающего весь свой поток вывода на том. Режим отключения сетевой среды также может быть полезен в ситуациях, когда необходимо настроить собственную сетевую среду с нуля. В этом случае можно воспользоваться инструментальными средствами, такими как pipework (https:// github.com/jpetazzo/pipework), обеспечивающими удобство при работе с сетями внутри cgroups и пространств имен. ## Overlay Overlay – это реализация решения Docker «с включенным комплектом батареек» для организации сетевой среды, распределенной по многим хостам. Решение использует туннели VXLAN для соединений хостов в их внутреннем пространстве IP-адресов и NAT для установления внешних соединений. Библиотека serf (https:// serfdom.io/) используется для обмена данными между узлами. Объединение контейнеров в сеть выполняется практически тем же способом, как и для стандартной сети со шлюзами, – устанавливается шлюз Linux для сети Overlay, затем пара виртуальных интерфейсов veth используется для установления соединения с контейнером.