Урок 6 из 8 · 20 мин · обновлено

Сеть и порты: как контейнеры общаются

Как работает публикация портов и почему возникает ошибка «port is already allocated», почему localhost внутри контейнера — это сам контейнер, как создать сеть и связать контейнеры по имени на примере базы данных и веб-интерфейса Adminer.

Порты: дверь из контейнера наружу

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

Терминал
docker run -d --name web -p 8080:80 nginx:alpine

-p 8080:80 читается слева направо: порт вашего компьютера : порт внутри контейнера. Запрос на localhost:8080 Docker передаёт на порт 80 контейнера, где его ждёт nginx.

Порт внутри контейнера определяет программа: nginx слушает 80, PostgreSQL — 5432. Порт компьютера выбираете вы. Можно запустить три копии nginx на портах 8080, 8081 и 8082 — внутри у всех будет 80.

Посмотреть, какие порты открыл контейнер:

Терминал
docker port web

Ошибка «port is already allocated»

Один порт компьютера может занять только одна программа. Если запустить второй контейнер на том же порту, Docker ответит:

Результат
Bind for 0.0.0.0:8080 failed: port is already allocated

Варианты: остановить контейнер, который занял порт, или выбрать другой — -p 8081:80. Если порт занят не контейнером, а программой на компьютере — например, установленной ранее базой, — тоже выберите другой порт.

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

Частая ловушка новичков. Внутри контейнера localhost — это сам контейнер, а не ваш компьютер и не соседний контейнер. Программа, которая ищет базу по адресу localhost:5432, будет искать её у себя внутри и не найдёт.

Чтобы контейнеры видели друг друга, их подключают к общей сети.

Своя сеть: связь по имени

Создадим сеть и запустим в ней базу. Используем том pgdata из прошлого урока — данные сохранятся:

Терминал
docker network create shop
docker run -d --name db --network shop -e POSTGRES_PASSWORD=secret -v pgdata:/var/lib/postgresql/data postgres:16

Обратите внимание: у базы нет -p. Её не нужно открывать наружу — к ней будут ходить другие контейнеры по сети.

Теперь запустим Adminer — веб-интерфейс для работы с базами данных. Это официальный образ, внутри он слушает порт 8080:

Терминал
docker run -d --name adminer --network shop -p 8081:8080 adminer

Откройте http://localhost:8081 и войдите:

  • Движок — PostgreSQL;
  • Сервер — db, то есть просто имя контейнера;
  • Пользователь — postgres, пароль — secret.

Вы внутри базы. Если том pgdata из прошлого урока на месте, в базе postgres будет таблица notes. Adminer нашёл базу по имени db: в созданной вами сети Docker сам сопоставляет имена контейнеров с их адресами. В сети по умолчанию, без --network, имена так не работают.

Управление сетями

Терминал
docker network ls              # список сетей
docker network inspect shop    # какие контейнеры подключены
docker network rm shop         # удалить сеть

Сеть удаляется, только когда в ней не осталось контейнеров.

Безопасность портов

-p 5432:5432 открывает порт не только для вас, а для всех устройств в сети — в кафе, в офисе или на сервере в интернете. На серверах с Linux Docker к тому же добавляет свои сетевые правила, которые срабатывают раньше правил обычного брандмауэра. Поэтому:

  • не публикуйте порт, если к программе ходят только другие контейнеры;
  • если порт нужен только вам, привяжите его к своему компьютеру: -p 127.0.0.1:5432:5432.

Попробуйте сами. Убедитесь, что контейнеры видят друг друга по имени. Подключите к сети shop временный контейнер с клиентом PostgreSQL и выполните запрос к базе db. Затем удалите оба контейнера и сеть. Том pgdata оставьте — он понадобится в следующем уроке.

Решение

Образ postgres:16 уже содержит клиент psql, поэтому его можно запустить как отдельный временный контейнер:

Терминал
docker run --rm -it --network shop -e PGPASSWORD=secret postgres:16 psql -h db -U postgres -c "SELECT * FROM notes;"

-h db — адрес сервера: имя контейнера базы. Если запустить ту же команду без --network shop, клиент не найдёт сервер db.

Уборка:

Терминал
docker rm -f db adminer
docker network rm shop

Мини-тест: проверьте себя

Закрепите урок: несколько вопросов, разбор ответов — в конце.

  1. Контейнер запущен с -p 3000:80. По какому адресу открыть его в браузере?

  2. Программа в контейнере пытается подключиться к базе по адресу localhost:5432, а база в другом контейнере. Что не так?

  3. Зачем создавать свою сеть через docker network create?