Урок 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
Мини-тест: проверьте себя
Закрепите урок: несколько вопросов, разбор ответов — в конце.
-
Контейнер запущен с -p 3000:80. По какому адресу открыть его в браузере?
Слева — порт вашего компьютера, справа — порт программы внутри контейнера. В браузере открывают порт компьютера.
-
Программа в контейнере пытается подключиться к базе по адресу localhost:5432, а база в другом контейнере. Что не так?
У каждого контейнера свой localhost. В общей сети контейнер базы доступен по имени, например db:5432.
-
Зачем создавать свою сеть через docker network create?
В сети по умолчанию имена контейнеров не работают как адреса. В созданной вами сети Docker сам сопоставляет имя контейнера с его адресом.