Глиф Учебники

Урок 7. Сети контейнеров

Курс «Docker» · урок 7 из 10 · 5 минут чтения

Реальные сервисы редко живут поодиночке: сайту нужна база данных, базе — панель управления, всем вместе — обратный прокси (веб-сервер, который принимает запросы снаружи и передаёт их нужному сервису). В этом уроке — как контейнеры находят друг друга и почему базе данных не нужен опубликованный порт.

Сети по умолчанию

Docker сразу создаёт три сети:

Bash
docker network ls

Контейнеры в сети bridge могут связаться друг с другом только по IP-адресу, а адрес меняется при каждом пересоздании контейнера. По имени они друг друга в этой сети не находят. Поэтому для своих сервисов создают свою сеть.

Своя сеть

Если контейнеры web и db из прошлых уроков ещё есть, удалите их, чтобы имена не мешали: docker rm -f web db (тома при этом останутся).

Bash
docker network create appnet
docker run -d --name web --network appnet nginx:alpine
docker run --rm --network appnet alpine wget -qO- http://web | grep title

Последняя команда запускает одноразовый контейнер в той же сети и запрашивает страницу у контейнера web — по имени:

Текст
<title>Welcome to nginx!</title>

В своей сети у Docker работает встроенный DNS (служба, которая по имени находит адрес): имя контейнера — это его адрес в сети. Адрес может меняться, имя — нет. Контейнеры в разных сетях друг друга не видят.

bridge — сеть по умолчанию контейнер 172.17.0.3 http://web 172.17.0.2 контейнер web 172.17.0.2 по имени не найти, а IP меняется appnet — своя сеть контейнер alpine wget http://web встроенный DNS web → адрес контейнер web nginx имя контейнера служит адресом
В сети bridge по умолчанию контейнер находит соседа только по IP, а в своей сети appnet встроенный DNS находит его по имени web.

Подключить к сети уже работающий контейнер — например, site из урока о томах — и отключить его:

Bash
docker network connect appnet site
docker network disconnect appnet site

Контейнер может состоять в нескольких сетях сразу — так, например, обратный прокси видит и сайты, и внешний мир, а базы данных сидят в отдельной сети только с теми, кому они нужны.

Порт внутри сети и порт снаружи

Внутри общей сети контейнеры обращаются друг к другу на порт программы, а не на опубликованный порт хоста. Пример — PostgreSQL и сайт в одной сети:

Bash
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=Smena-Parol-2026 -v pgdata:/var/lib/postgresql postgres:18

Сайт в сети appnet подключается к базе по адресу db:5432. Ключ -p для этого не нужен: он открывает порт для хоста и внешнего мира. Сайту порт публикуют, базе — нет.

хост сеть appnet сайт web порт 80 db:5432 база db порт 5432 -p 8080:80 8080 5432 не опубликован другой компьютер
Внутри сети appnet сайт обращается к базе по адресу db:5432, а снаружи открыт только опубликованный порт сайта 8080.

Опубликованный порт базы (-p 5432:5432) виден всем, кто может достучаться до сервера, и Docker открывает его в обход ufw. Если базе не нужны подключения снаружи, не публикуйте её порт: внутри сети Docker она доступна и так. Если нужны с самого хоста — -p 127.0.0.1:5432:5432.

localhost внутри контейнера

Внутри контейнера localhost и 127.0.0.1 — это сам контейнер, а не хост и не соседний контейнер. Частая ошибка: в настройках сайта написано «база на localhost», а база — в другом контейнере. Нужно имя контейнера базы: db.

Если программе в контейнере нужна служба, запущенная прямо на хосте, есть особое имя host.docker.internal. В Docker Desktop оно работает само, на Linux его включают при запуске:

Bash
docker run --rm --add-host host.docker.internal:host-gateway alpine ping -c 2 host.docker.internal

Служба на хосте при этом должна слушать не только 127.0.0.1, иначе из контейнера до неё не достучаться.

хост контейнер сайта localhost здесь — сам этот контейнер localhost:5432 контейнер базы db:5432 служба на хосте host.docker.internal слушает не только 127.0.0.1
Из контейнера сайта localhost ведёт в сам этот контейнер, база доступна по имени db, а служба хоста — по имени host.docker.internal.

Сеть host

С ключом --network host контейнер работает прямо в сети хоста: программа слушает порты хоста без всяких -p, а -p в этом режиме не действует. Так запускают программы, которым нужен прямой доступ к сетевым интерфейсам, — например, системы мониторинга сети.

В режиме host нет сетевой изоляции: контейнер видит все сетевые интерфейсы хоста, и каждый порт, который слушает программа, сразу открыт на хосте. Используйте его только когда без него нельзя.

Посмотреть, что в сети

Bash
docker network inspect appnet
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' web

Первая команда показывает настройки сети и список подключённых контейнеров с адресами. Вторая — только адреса одного контейнера. Удалить сеть можно, когда в ней не осталось контейнеров: docker network rm appnet.

Сети в Docker Compose

Docker Compose создаёт отдельную сеть для каждого проекта и подключает к ней все его сервисы. Имена сервисов из файла становятся адресами: сервис db доступен остальным как db. Поэтому в Compose сети обычно не настраивают вручную — об этом следующий урок.

Шпаргалка

ЗадачаКоманда
Список сетейdocker network ls
Создать сетьdocker network create имя
Запустить контейнер в сетиdocker run --network имя …
Подключить работающий контейнерdocker network connect сеть контейнер
Подробности о сетиdocker network inspect имя
Удалить сетьdocker network rm имя

Коротко

Проверьте себя

1. Сайт и база запущены в своей сети appnet, база — в контейнере db. Какой адрес базы указать в настройках сайта?
2. Нужно ли публиковать порт базы через -p, чтобы сайт из той же сети Docker к ней подключился?
3. Чем своя сеть (docker network create) удобнее сети bridge по умолчанию?
4. Что происходит с ключом -p при запуске с --network host?