Сети
Контейнеры общаются по сети. Чтобы пайплайн нашёл базу, нужна общая сеть и имя базы. Это самая частая причина ошибки «Connection refused».
Главное правило
Заголовок раздела «Главное правило»Внутри контейнера localhost — это сам контейнер. Не ваш ноутбук и не соседний контейнер.
Если скрипт в контейнере loader ходит на localhost:5432, он ищет Postgres внутри loader. Там его нет.
Сеть с именами
Заголовок раздела «Сеть с именами»Docker умеет создавать виртуальные сети. Контейнеры в одной сети видят друг друга по имени.
docker network create shop-netdocker network ls --filter name=shop-netОтвет:
NETWORK ID NAME DRIVER SCOPE66a43eab964e shop-net bridge localДве сети — два разных поведения:
| Сеть | Имена контейнеров работают? |
|---|---|
bridge (по умолчанию) | Нет |
Своя сеть (docker network create) | Да. Docker сам отвечает на запрос имени |
Compose создаёт свою сеть автоматически. Поэтому в Compose имена работают без настройки. Об этом — в следующем уроке.
Демо: база и загрузчик
Заголовок раздела «Демо: база и загрузчик»Нужна папка ~/docker-practice и Dockerfile из урока Хороший образ (раздел «Итоговый Dockerfile»). Если вы его пропустили, создайте файл по этому уроку.
Соберите образ:
cd ~/docker-practicedocker build -t orders-app:1.0 .Запустите Postgres в сети shop-net. Имя контейнера — db:
docker run -d --name db --network shop-net \ -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=shop \ postgres:17Подождите 5–6 секунд, пока база стартует. Проверьте, что имя db работает:
docker run --rm --network shop-net alpine:3.22 ping -c 1 dbОтвет:
PING db (172.25.0.2): 56 data bytes64 bytes from 172.25.0.2: seq=0 ttl=64 time=0.064 msIP-адрес у вас будет другой. Он нам не нужен: мы пользуемся именем.
Для сравнения запустите тот же ping без --network:
docker run --rm alpine:3.22 ping -c 1 dbОтвет:
ping: bad address 'db'В сети bridge по умолчанию имена не работают.
Ловушка localhost
Заголовок раздела «Ловушка localhost»Запустите загрузчик без DB_HOST. Скрипт load_orders.py берёт localhost по умолчанию:
docker run --rm --network shop-net \ -e DB_PASSWORD=secret \ -v "$PWD/data:/data:ro" \ orders-app:1.0 python load_orders.pyОшибка:
connection failed: connection to server at "127.0.0.1", port 5432 failed: Connection refusedPostgres работает. Но скрипт ищет его в своём контейнере. Теперь укажите имя контейнера базы:
docker run --rm --network shop-net \ -e DB_HOST=db -e DB_PASSWORD=secret \ -v "$PWD/data:/data:ro" \ orders-app:1.0 python load_orders.pyОтвет:
Загружено строк: 500Здесь python load_orders.py после имени образа заменяет CMD из Dockerfile. Образ тот же, команда другая.
Проверьте данные в базе:
docker exec db psql -U postgres -d shop -c \ "SELECT channel, sum(amount) AS revenue FROM orders WHERE status='completed' GROUP BY channel ORDER BY 2 DESC"Ответ:
channel | revenue---------+---------- organic | 23715.82 ads | 23082.74 email | 15459.67(3 rows)Два порта: хоста и контейнера
Заголовок раздела «Два порта: хоста и контейнера»В уроке про порты мы писали -p 127.0.0.1:5432:5432. Этот ключ нужен только для доступа с вашего компьютера: из DBeaver, psql на ноутбуке, браузера.
| Кто подключается | Куда | Нужен -p? |
|---|---|---|
| Ваш ноутбук | localhost:5432 | Да |
| Другой контейнер в той же сети | db:5432 | Нет |
Контейнеры общаются по внутреннему порту. Если вы запустили -p 5433:5432, снаружи база на 5433, а контейнеры всё равно ходят на db:5432.
Контейнеру db в нашем демо мы не открывали ни одного порта. Загрузчик всё равно до него дошёл.
Полезные команды
Заголовок раздела «Полезные команды»docker network ls # список сетейdocker network inspect shop-net # кто подключён, какие адресаdocker network rm shop-net # удалить сеть (контейнеры должны быть остановлены)docker run --network shop-net ... # запустить контейнер в сетиПокажем, кто сидит в сети:
docker network inspect shop-net --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'Ответ:
db 172.25.0.2/16Практика
Заголовок раздела «Практика»Если контейнер db и сеть shop-net ещё есть, используйте их. Иначе повторите команды выше.
Задание 1
Заголовок раздела «Задание 1»Подключитесь к базе из второго контейнера postgres:17. Используйте psql и имя db. Сколько строк в таблице orders?
Подсказка: пароль передаётся переменной PGPASSWORD.
Проверка: 500.
Решение
docker run --rm --network shop-net -e PGPASSWORD=secret \ postgres:17 psql -h db -U postgres -d shop -tc "select count(*) from orders"-h db — имя контейнера базы. Порт не нужен: по умолчанию 5432.
Задание 2
Заголовок раздела «Задание 2»Покажите, что имя db не работает вне своей сети. Запустите ping -c 1 db в контейнере alpine:3.22 без --network.
Проверка: ответ ping: bad address 'db'.
Решение
docker run --rm alpine:3.22 ping -c 1 dbБез --network контейнер попадает в сеть bridge. Там нет имён.
Задание 3
Заголовок раздела «Задание 3»Запустите загрузчик с неверным именем базы: DB_HOST=database. Что получилось?
Проверка: последняя строка ошибки: failed to resolve host 'database': [Errno -2] Name or service not known.
Решение
docker run --rm --network shop-net \ -e DB_HOST=database -e DB_PASSWORD=secret \ -v "$PWD/data:/data:ro" \ orders-app:1.0 python load_orders.pyЭто другая ошибка, чем «Connection refused». «Refused» — имя нашлось, но там никто не слушает. «Name not known» — имени нет в сети.
Задание 4
Заголовок раздела «Задание 4»Остановите и удалите контейнер db и сеть shop-net.
Проверка: docker network ls не показывает shop-net.
Решение
docker rm -f dbdocker network rm shop-netСеть нельзя удалить, пока в ней есть контейнеры.
Дальше: Docker Compose.