Skip to content

Docker Networking

Default network

В Docker Engine сетью по умолчанию является простая встроенная сеть default bridge. Любой контейнер запущенный без специфических сетевых настроек, подключается к этой сети и имеет доступ в интернет через хостовую систему, используя masquerading.

Так же контейнеры, подключенный к дефолтной сети могут "видеть" друг друга по ip-адресам, но не по именам.

Можно создать "выделенную" сеть типа bridge и подключить к ней определенные контейнеры. Тогда другие контейнеры, не присоединенные к этой сети не будут их видеть. Так же, объединенные одной сетью контейнеры, могут обращаться друг к другу по именам.

create bridge network
docker network create -d bridge special_net # создаем выделенную сеть, типа bridge
docker run --network=special_net -it busybox # подключаем к ней запущенный контейнер

Подкючение к нескольким сетям

В Docker есть возможность настроить подключение отдельно взятого контейнера сразу к нескольким сетям: как физическим, так и виртуальным. Для упрощения понимания, можно воспринимать контейнер, как отдельный хост, который может иметь несколько сетевых карт, которые подключены к разным сетям.

Например, контейнер, который отвечает за frontend, подключен к двум сетям: "внешней" и "внутренней". А контейнеры, отвечающие за backend, подключены только к "внутренней" сети.

В Docker так же есть default-gateway, который назначается самим Докером, но может быть переопределен руками. Т.е., контейнер пытается "связаться" с кем-то, он шлет пакеты сначала в ту сеть к которой он подключен и, если не находит в ней адресата - шлет пакеты на шлюз по умолчанию.

Port publishing

По умолчанию, контейнеры работающие в одной сети типа "bridge" могут подключаться к портам друг друга без дополнительных манипуляций, так же они доступны хостовой машине. Контейнеры из других сетей и "внешний" мир не могут видеть эти контейнеры.

Что бы контейнер стал доступен извне, его порт должен быть "опубликован" с помощью флага --publish или -p. Публикация порта создает правило для фаервола докер-хоста, также происходит mapping порта контейнера на порт докер-хоста, для доступа "извне".

Пример Описание
-p 8080:80 Мапинг порта 8080 хоста на 80 TCP порт контейнера
-p 192.168.1.100:8080:80 Мапинг адреса хоста 192.168.1.100:8080 на 80 TCP порт контейнера
-p 8080:80/udp Мапинг порта 8080 хоста на 80 UPD порт контейнера
-p 8080:80/tcp -p 8080:80/udp Мапинг TCP/UDP порта 8080 хоста на TCP/UDP порт контейнера

Важный момент! Публикуя порт контейнера на внешний сетевой интерфейс хоста, вы делаете его подверженным риску атаки. Можно при публикации, указывать loopback-адрес, чтобы только хостовая система могла обращаться к контейнеру напрямую. Например: docker run -p 127.0.0.1:8080:80 -p '[::1]:8080:80' nginx

Режимы gateway

Для bridge network доступны несколько режимов работы шлюза по умолчанию:

  • nat. Режим по умолчанию. Для любого публикуемого порта контейнера будут установлены правила NAT и masquerading. Пакеты проходящие через хост будут использовать его адрес.

    Если на хостовом компьютере настроен ip-forwarding и порт контейнера опубликован на адрес "внутренней" сети, хосты из "внешней" сети будут иметь к нему доступ.

  • nat-unprotected. В этом режиме, к неопубликованным контейнерам есть доступ извне с помощью direct routing.

  • routed. При этом режиме, правила NAT и masquerading не устанавливаются, но правила фаервола работают таким образом, что только опубликованные порты контейнера доступны извне. При публикации портов контейнера не нужно указывать порты хоста на который происходит mapping, это ничего не изменит - будет опубликован порт контейнера. Исходящие пакеты от контейнеров будут использовать адрес контейнера.

    При таком режиме работы, "внешние" хосты получают доступ к контейнеру через "внешний" адрес docker-хоста, использую механизм direct routing. Хосты локальной сети уровня layer-2 (подключенные к одному свичу), получают доступ к контейнеру напрямую, публикация портов не требуется. Хосты вне локальной сети могут получить доступ только к опубликованным портам контейнера.

    Контейнеры из "других" подсетей могут получать доступ к неопубликованным потрам контейнеров routed сети.

  • isolated

Сеть контейнера

В дополнении ко всему, существует возможность подключить один контейнер напрямую к сети другого контейнера, для этого используется следующий флаг: --network container:<name|id>

# Сначала запускаем целевой контейнер и прибиваем его к loopback-адресу с помощью --bind
docker run -d --name redis redis --bind 127.0.0.1

# Затем подлючаем другой контейнер напрямую к сети целевого контейнера
docker run --rm -it --network container:redis redis redis-cli -h 127.0.0.1

Port publishing and mapping on DocerDocs

Типы сетей в Docker

bridge

Bridge network драйвер сети докер используемый по умолчанию. Позволяет:

  • Получать неограниченный севой доступ с хоста к контейнерам, также между контейнерами подключенными к одинаковой bridge-сети.
  • Блокировать доступ между контейнерами из разных сетей, а также извне докер-хоста.
  • Предоставлять доступ к контейнерам извне, используя masquerading. При этом внешние хосты не видят контейнеры внутри докер-хоста, а видет только внешний ip хоста.
  • Делать port publishing, когда сетевой трафик пересылается между портами контейнера и портами докер-хоста с реальными ip-адресами.

В терминах Docker, сеть типа bridge использует программный "мост", который позволяет контейнерам соединятся друг с другом в одной и той же сети, одновременно с этим изолируя их от контейнеров из других сетей. Bridge network приминима только к контейнерам работающим на одном хосте, для коммуникации с контейнерами на других хостах используется overlay network.

Любой контейнер, который запускается без указания конкретной сети, подключается к default bridge network. Именнованные bridge-сети более предпочтительны:

  • В именнованых сетях присутствует DNS разрешение имен.

    Контейнеры, подключенные к default-сети, могут общаться друг с другом только по IP-адресам. В то время как, контейнеры подключенные к одной именованной bridge-сети могут обращаться по именам контейнеров

  • Именнованные сети дают лучшую изоляцию. Только контейнеры подключенные к одной и той же сети могут беспрепятственно общаться друг с другом.

Команды для управления имаенованными сетями
# Создать новую сеть
docker network create user_net

# Удалить существующую сеть
docker network rm user_net

# Подключить к сети работающий контейнер
docker network connect user_net my_container

# Отключить от сети работающий контейнер
docker network disconnect user_net my_container

Ограничения и лимиты bridge-сете

Согласно ограничениям ядра Линукс в одной сети типа "мост" могут корректно работать не более 1000 контейнеров.

host

При использовании драйвера сети типа host, сетевое пространство имен контейнера не изолированно от сетевого пространства имен хоста, на котором запущен контейнер. Потому что, контейнеры используют network namespace хоста для своей работы, соответственно: контейнеры не получают своих собственных айпишников, их порты напрямую публикуются в порты хостовой машины, фактически они просто занимают порты хоста, если они свободны.

В каких ситуациях логично применять сеть типа host:

  • Для оптимизации производительности.
  • В ситуации, когда контейнеру необходимо выделить большой диапазон портов.
  • Ускорение процеса происходит от того, что не требуется NAT, и для каждого порта не требуется создавать "userland-proxy"

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

ipvlan

IPvLAN — это новый подход к проверенной временем технологии виртуализации сети. Реализация данного подхода в Linux отличается исключительной легковесностью, в сопоставлении с традиционными "мзоляциями" на основе linux-bridge.

IPvLAN предлагает ряд уникальных функций и широкие возможности для внедрения инновационных моделей. Основным приемуществом использования данной модели является исключения "моста" линукс из сетевого взаимодействия с контейнерами.

Фактически каждому контенеру можно назначить индивидуальный сетевой адрес и слать весь трафик напрямую, изключая NAT, masquarading, forwarding. Это может заметно ускорить работу контейнеров с большой сетевой нагрузкой.

В самом простом примере, контейнерам назначаются ip-адреса из подсети docker-хоста. Теперь у каждого контейнера есть свой ip-адрес и к нему можно обращаться напрямую, все кнтейнеры используют mac-адрес хоста.

IPvlan network
# Создание сети типа IPvlan
docker network create -d ipvlan \
    --subnet=192.168.1.0/24 \
    --gateway=192.168.1.1 \
    -o ipvlan_mode=l2 \
    -o parent=eth0 my_ipvlan_net

# Подключение контейнера к созданной сети
docker run --net=my_ipvlan_net -it --rm alpine /bin/sh

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

--internal

Флаг --internal дает возможность изолировать контейнеры внутри выделенной сети, доступ к контейнерам извне будет закрыт. Поскольку "сетевая изоляция" тесно связана с родительским сетевым интерфейсом хоста, простое опускание флага -o parent= при создании сети, создаст такую же изолированную сеть, как и при использовании флага --internal

Изолированная сеть IPvlan
# Если не применять флаг '-o parent=' созданная сеть будет изолированна
docker network create -d ipvlan \
    --subnet=192.168.10.0/24 isolated1

# Применение флага '--internal' дает тот же результат:
docker network create -d ipvlan \
    --subnet=192.168.11.0/24 --internal isolated2

# Можно даже опустить флаг '--subnet=' - сеть будет выдана автоматически
# IPAM subnet of 172.18.0.0/16
docker network create -d ipvlan isolated3

Пордродно про IPvlan network driver

macvlan

Сетевой драйвер macvlan дает возможность назначить реальный MAC-адрес виртуальному сетевому интерфейсу контейнера, как-будто контейнер имеет физический сетевой интерфейс, который подключен к физической сети. Чтобы использовать данную модель подключения к сети, необходимо для каждого контейнера иметь выделенную сетевую карту на docker-хосте.

Данный сетевой драйвер используется редко.

Пордродно про Macvlan network driver

none

Использование драйвера none дает возможность полностью изолировать сетевое пространство имен контейнера. Внутри контейнера будет создан только его собственный loopback интерфейс.

--network none
docker run --rm --network none alpine:latest ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

overlay

Драйвер overlay-сети дает возможность создать распределенную сеть между несколькими докер-хостами. Данная сеть располагается "поверх" сети каждого отдельного хоста, позволяет подключенным к ней контейнерам безопасно коммуницировать. Докер обеспечивает прозрачную маршрутизацию всех пакетов между докер-хостами и работающими на них контейнерами. Оверлейную сеть можно рассматривать как единую сеть, охватывающую несколько хостов. Все контейнеры, находящиеся в этой сети, могут взаимодействовать друг с другом, как если бы они находились на одном хосте.

Контейнер, находящийся в оверлейной сети, не сможет взаимодействовать с другими контейнерами, даже выполняющимися на том же хосте, если не включить их в эту сеть. Для туннелирования Docker использует технологию VXLAN (Virtual eXtensible LAN – виртуальную расширяемую локальную сеть).

Хотя сеть типа overlay зачастую используется в режиме docker-Swarm, её также можно создать вручную, использую команду docker network create, затем подключить к ней отдельные контейнеры, работающие на разных хостах.

Как и для чего использовать Overlay сеть

host.docker.internal

По умолчанию, Docker-контейнер запущенный в Linux не имеет прямого доступа к localhost docker-хоста на котором запущен Docker Daemon. Но, доступ к нему можно обеспечить, пробросив разрешение имени host.docker.internal внутрь контейнера в файл /etc/hosts, с помощью опции --add-host и extra_hosts.

--add-host в docker run
docker run --add-host=host.docker.internal:host-gateway -p 8080:80 grafana/grafana:latest-ubuntu
extra_hosts в Docker compose
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    extra_hosts:
      - "host.docker.internal:host-gateway"
# Теперь к localhost можно обращаться изнутри контейнера
# доменному имени host.docker.internal

host-gateway - это magic-строка docker engine, которая автоматически резолвит на ip-адрес шлюза docker-моста.