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

Для начала я создам контейнер с nginx (если не понятно что происходит).

docker container run -d --name some-nginx -p 80:80 nginx:latest

Теперь выведу ip-адрес контейнера и ip-адрес хоста где установлен Docker.

docker inspect -f '{{ .NetworkSettings.IPAddress }}' some-nginx
172.17.0.3
ip a
172.31.144.9

Вывести список всех сетей docker в Docker (docker network ls)

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

Для того, чтобы проверить какие вообще виртуальные сети есть в системе используется команда docker network ls, которая выведет список всех виртуальных сетей.

docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
6d18af512d4d   bridge    bridge    local
8dd3c0368276   host      host      local
e4ee21b03ff4   hyper     bridge    local
581357de291b   none      null      local

Просмотр информации о сети в Docker (docker network inspect)

Отлично команда выше выдала список всех сетей, но как понять какой контейнер использует ту или иную сеть. Для этого используется команда docker network inspect, где в поле Containers перечисляются контейнеры, которые используют эту сеть.

docker network inspect 6d18af512d4d
			"Containers": {
            "15efdb23bd4ca96be08cd6e4845a941508b231392bc2f3d0ebc57f0facd1e8f0": {
                "Name": "some-nginx",
                "EndpointID": "07b21011ab8e497beabf228c3208c8a77f684ee4725d8ee734fdb9a92806c53a",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            }
        }

Как создать сеть в Docker (docker network create)

И вот пришло время создать свою собственную виртуальную сеть в Docker используя команду docker network create.

docker network create my-network1
docker network ls
3dd65f38f672   my-network1   bridge    local

При создании сети я не указывал никаких настроек кроме как название этой сети. Подсеть для этой новой сети Docker задал сам автоматически. Т.е. контейнеры использующие эту сеть (my-network1) будут иметь ip-адреса вида 172.19.0.2.

docker network inspect my-network1
				"IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        }

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

  • --driver - Тип сети (bridge, overlay, macvlan, host, none)
  • --subnet - Указать подсеть вручную (--subnet 172.28.0.0/16)
  • --gateway - Указать шлюз вручную (--gateway 172.28.0.1)
  • --ip-range - Задать диапазон IP для контейнеров (--ip-range 172.28.5.0/24)

Если вдруг необходимо запретить контейнерам выход в интернет, то необходимо задать опцию --internal при создании новой сети.

Назначить сеть контейнеру

Для того чтобы назначить только что создаю сеть новому контейнеру при выполнении команды создания docker container run необходимо указать сеть при помощи параметра --network.

docker container run -d --name my-network1-nginx1 --network my-network1 nginx:alpine

Для того чтобы подключить уже запущенный контейнер к другой сети используется docker network connect.

docker network connect my-network1 some-nginx
docker network inspect my-network1
					"Containers": {
            "05186ff32ca1fa4f9dd01eede5293f0e7aa8676700155ffc05b7cf3b577bda54": {
                "Name": "my-network1-nginx1",
                "EndpointID": "4eec0b82203370114a7e3d6724f9cedf5e3629996849459b5e8e0801d331df59",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "15efdb23bd4ca96be08cd6e4845a941508b231392bc2f3d0ebc57f0facd1e8f0": {
                "Name": "some-nginx",
                "EndpointID": "08702a00bff2d1b6381720216cd365434e97160e002677dbd91b9f6b579309d9",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            }
        },

Теперь, когда в сети my-network1 два контейнера они должны обращаться друг к другу по сети без проблем, но не иметь возможность обратиться к контейнерам из в другой сети.

docker exec -it my-network1-nginx1 sh
ping some-nginx
PING some-nginx (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.105 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.110 ms
64 bytes from 172.19.0.3: seq=2 ttl=64 time=0.131 ms
ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=63 time=0.112 ms
64 bytes from 172.17.0.2: seq=1 ttl=63 time=0.101 ms
64 bytes from 172.17.0.2: seq=2 ttl=63 time=0.125 ms

Почему контейнеры из разных сетей видят друг друга

Но как видно из вывода контейнер some-nginx-2, который в сети bridge тоже пингуется. Произошло это потому что я использую ОС CentOS, в которой по умолчанию включен firewalld, который перехватывает управление. А точнее две сети Docker находятся в одной зоне (docker) в firewalld. Поэтому трафик между всеми сетями в одной зоне разрешён. Даже если Docker добавил правила, изолирующие обе сети в iptables приоритет выше у firewalld, который пропускает весь трафик.

Почему контейнеры из разных сетей видят друг друга

Самым простым решением конечно будет просто отключить firewalld в самой системе (не рекомендуется в проде).

sudo systemctl stop firewalld
sudo systemctl disable --now firewalld

И другой вариант если вы хотите сохранить работающий firewalld это исключить Docker-интерфейсы из управления firewall.

  1. Получаем активные зоны
    sudo firewall-cmd --get-active-zones
    
    docker
      interfaces: docker0 br-e4ee21b03ff4 br-3dd65f38f672
    
  2. Удалить все Docker-интерфейсы
    sudo firewall-cmd --permanent --remove-interface=docker0
    sudo firewall-cmd --permanent --remove-interface=br-e4ee21b03ff4
    sudo firewall-cmd --permanent --remove-interface=br-3dd65f38f672
    
  3. Перечитываем правила
    sudo firewall-cmd --reload
    
  4. Перезапускаем Docker
    sudo systemctl restart docker
    

Отключить сеть контейнера

Если нужно отключить сеть у контейнера выполняем команду docker network disconnect.

docker network disconnect bridge some-nginx