Два вопроса, на которых спотыкаются чаще всего: «почему контейнер не видит базу» и «куда делись данные после пересоздания контейнера». Оба про то, как Docker устраивает сеть и хранение.
Сетевые драйверы
- bridge (по умолчанию): у контейнера свой network namespace, виртуальная пара
vethсоединяет его с мостом на хосте (docker0для сети по умолчанию). Наружу контейнер выходит через NAT (masquerade). - host: контейнер использует сеть хоста напрямую, без изоляции и без публикации портов. Быстро, но порты конфликтуют с хостом. Полноценно работает только на Linux.
- none: только loopback, сети нет.
- overlay: сеть между несколькими хостами (Swarm).
- macvlan/ipvlan: контейнер получает адрес в физической сети.
Пользовательская сеть и DNS
docker network create app-net
docker run -d --name db --network app-net postgres:16
docker run -d --name api --network app-net -e DB_HOST=db my/api
В пользовательской bridge-сети работает встроенный DNS Docker (адрес 127.0.0.11 внутри контейнера): контейнеры находят друг друга по имени и по алиасам. В сети по умолчанию bridge такого DNS нет, только IP. Отсюда классическая ошибка: два контейнера, запущенных без --network, не видят друг друга по имени.
IP контейнера меняется при пересоздании, поэтому ходить надо по имени, а не по IP.
Публикация портов
docker run -p 8080:80 nginx # хост 0.0.0.0:8080 → контейнер :80
docker run -p 127.0.0.1:5432:5432 db # только с localhost хоста
docker run -P nginx # все EXPOSE-порты на случайные порты хоста
docker port <container>
Docker реализует публикацию правилами iptables (DNAT в таблице nat). Важное следствие: опубликованный порт обходит правила ufw, потому что пакеты идут через цепочку FORWARD и правила Docker, а не через INPUT, которую настраивает ufw. Базу, опубликованную как -p 5432:5432, будет видно из интернета, даже если ufw «закрыт». Для внутренних сервисов либо не публикуют порт вовсе (контейнеры общаются по внутренней сети), либо привязывают к 127.0.0.1.
Ещё ловушка: приложение внутри контейнера слушает 127.0.0.1. Тогда публикация не поможет: трафик приходит на интерфейс контейнера eth0. Внутри контейнера надо слушать 0.0.0.0.
Из контейнера к сервису на хосте: host.docker.internal в Docker Desktop, на Linux добавляют --add-host=host.docker.internal:host-gateway.
Хранение данных
Записываемый слой контейнера живёт, пока жив контейнер, и медленнее обычной ФС. Для данных три механизма:
| Где лежит | Для чего | |
|---|---|---|
| volume | управляет Docker (/var/lib/docker/volumes/...) | данные баз и сервисов |
| bind mount | любой путь хоста | конфиги, код в разработке |
| tmpfs | память | временное и секретное, не пишем на диск |
docker volume create pgdata
docker run -v pgdata:/var/lib/postgresql/data postgres:16
docker run -v "$PWD/nginx.conf:/etc/nginx/nginx.conf:ro" nginx
docker run --mount type=tmpfs,target=/tmp app
docker volume ls; docker volume inspect pgdata
Синтаксис --mount длиннее, но явный; -v короче. Если в -v указан путь (начинается с / или ./), это bind mount, если имя, то том.
Особенности:
- Пустой именованный том при первом подключении заполняется содержимым образа по этому пути. Bind mount просто перекрывает каталог образа содержимым хоста, даже пустым.
- Тома переживают
docker rm. Удаляются явно:docker volume rm,docker rm -vдля анонимных,docker volume prune. - Права: процесс в контейнере работает со своим UID, и на bind mount он может не иметь доступа к файлам хоста (или наоборот, создавать файлы root на хосте).
- Флаг
:roмонтирует только для чтения.
Бэкап тома
docker run --rm -v pgdata:/data -v "$PWD":/backup alpine \
tar czf /backup/pgdata.tgz -C /data .
Для баз правильнее логический дамп (pg_dump) или средства самой СУБД: копия файлов работающей базы может оказаться неконсистентной.
Частые ошибки
- Контейнеры в сети по умолчанию ищут друг друга по имени.
- Приложение слушает
127.0.0.1внутри контейнера. -p 5432:5432для базы на публичном сервере в надежде на ufw.- Данные базы без тома: пропадают при пересоздании контейнера.
- Bind mount пустого каталога поверх каталога с данными образа.
Что спросят на собесе
- Какие сетевые драйверы есть и чем bridge отличается от host.
- Как контейнеры находят друг друга по имени.
- Volume против bind mount, где что использовать.
- Почему опубликованный порт виден снаружи несмотря на ufw.
- Что будет с данными при
docker rmи приdocker compose down -v.