Задача — соединить два контейнера в одну сеть. Допустим это будет веб сервер nginx and php-fpm. Естественно они должны обмениваться сообщениями без проблем, ip адреса должна быть из одной подсети. Также должны находить друг друга не просто по ip адресу, но и по имени сервиса. Намного удобнее работать с именем php-service, вместо 172.21.0.2, который еще может измениться при повторном запуске.
Для начала немного теории.
При установке Docker-а автоматически создается три сети под название none, host, bridge. Список можно посмотреть через команду docker network ls
none — у контейнера нет сети, он изолирован, ему сломали ethernet port, host — использовать сеть хоста. Чтобы присоединиться к ним, придется вручную прописать имя сети при запуске docker run —network=host|none.
bridge — По умолчанию при создании контейнера, докер присоединяет контейнер к уже существующей сети под названием bridge.
Настройка двух контейнеров в одной сети
Например, командами
1 2 3 4 |
docker run -tid --name=busybox1 busybox docker run -tid --name=busybox2 busybox |
создали два контейнера. Посмотрим состояние сети bridge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
docker network inspect bridge [ { "Name": "bridge", "Id": "88f248c24cd885bf8e9900ede6e186bfaa5b861f019c2964994f28e2c233be8b", "Created": "2019-11-16T01:54:34.4956389Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "22d70743261979ed664bbc5158171f6ca4341337cccb90025e6505f5032b2c86": { "Name": "busybox1", "EndpointID": "fb9ecdf11926e0ad32575973667272208c94262f6d982baf23864b1d01fc226f", "MacAddress": "02:42:ac:11:00:03", "IPv4Address": "172.17.0.3/16", "IPv6Address": "" }, "41e28729b8b022ae6d0b9ddca21e607afe924ab0ee40def5c007048200991b30": { "Name": "busybox2", "EndpointID": "25f56d16cb87cea5f86556a461f9115b3146a41fd240b8a51ff9d277374be353", "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": {} } ] |
два контейнера есть. Зайдя в первый контейнер 172.17.0.3 можно передать сообщение по ИП 172.17.0.2 второму,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
docker attach busybox1 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=64 time=0.065 ms 64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.129 ms 64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.139 ms 64 bytes from 172.17.0.2: seq=3 ttl=64 time=0.137 ms 64 bytes from 172.17.0.2: seq=4 ttl=64 time=0.137 ms ^C --- 172.17.0.2 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max = 0.065/0.121/0.139 ms |
но нельзя найти по имени контейнера, будет ошибка
1 2 3 4 |
// Ошибка, сервиса с именем busybox2 нет ping: bad address 'busybox2' |
Как найти контейнер в сети по имени?
Docker умеет автоматически находить ип адреса контейнеров по именам (auto service discover), но для этого нужно использовать пользовательское сетевое окружение или именованное сетевое окружение (user-defined-network).
Создать новую сеть можно командой:
1 2 3 |
docker network create busybox-network |
Проверка что все работает:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Удалить предыдущие контейнеры docker rm -f busybox1 busybox2 # Создать два новых, прикрепив к сети busybox-network docker run -tid --network=busybox-network --name busybox1 busybox docker run -tid --network=busybox-network --name busybox2 busybox # Зайти в первый контейнер docker attach busybox1 / # ping -c4 busybox2 PING busybox2 (172.24.0.2): 56 data bytes 64 bytes from 172.24.0.2: seq=0 ttl=64 time=0.037 ms 64 bytes from 172.24.0.2: seq=1 ttl=64 time=0.097 ms 64 bytes from 172.24.0.2: seq=2 ttl=64 time=0.106 ms 64 bytes from 172.24.0.2: seq=3 ttl=64 time=0.093 ms --- busybox2 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.037/0.083/0.106 ms |
Сервис успешно найден по имени
Как настроить сеть в docker-compose?
Для удобства создадим два конфига для docker-compose в одной папке. Пример первого конфига busybox-1.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
version: '3.5' services: box: image: busybox tty: true networks: default: name: "busybox-network" external: true |
Пример второго busybox-2.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
version: '3.5' services: box2: image: busybox tty: true networks: default: name: "busybox-network" external: true |
networks — описывается используемая сеть, их может быть несколько
default — означает что сеть будет использоваться для всех сервисов. Вместо default можно написать «my-awesome-network», тогда эту сеть нужно будет указывать явно сервиса. Удобно если к сети нужно подключить только один из сервисов из пяти.
name — имя сети
external — сеть внешняя, то есть при старте docker-compose будет использовать существующую сеть, а не пытаться создать новую
tty — включить терминал при старте. Нужно чтобы запустить процесс в фоне, иначе процесс сразу же завершить выполнение. Аналог команды docker run -ti
Запустить и протестировать можно командами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# Запуск контейнеров docker-compose -f .\busybox-1.yml up -d docker-compose -f .\busybox-2.yml up -d ..... Creating box_box2_1 ... done # Подключение ко второму контейнеру PS D:\Docker\Box> docker exec -ti box_box2_1 /bin/sh / # cat /etc/hostname e34b23e3a88d / # cat /etc/hosts 127.0.0.1 localhost ..... 172.24.0.3 e34b23e3a88d / # ping 172.24.0.2 PING 172.24.0.2 (172.24.0.2): 56 data bytes 64 bytes from 172.24.0.2: seq=0 ttl=64 time=0.086 ms 64 bytes from 172.24.0.2: seq=1 ttl=64 time=0.141 ms ^C --- 172.24.0.2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.086/0.113/0.141 ms # проверка - первый контейнер найден по имени / # ping box_box_1 PING box_box_1 (172.24.0.2): 56 data bytes 64 bytes from 172.24.0.2: seq=0 ttl=64 time=0.096 ms 64 bytes from 172.24.0.2: seq=1 ttl=64 time=0.096 ms 64 bytes from 172.24.0.2: seq=2 ttl=64 time=0.059 ms |
Сеть в порядке
Теперь становится понятной магия docker-compose. Как несколько сервисов, объявленных в docker-compose.yml, находили другу друга? Все дело в том, что при запуске(up) docker-compose неявно создавал user-defined-network, который обычно назывался название_папки_default, и добавлял к этой сети каждый сервис. И как раз при помощи этого сервисы могли общаться с друг другом по именам.