Volumes и сети
Почему volumes
Проблема: данные внутри контейнера пропадают при удалении.
docker run -d --name pg -e POSTGRES_PASSWORD=secret postgres:16
# Создал БД, наполнил данными
docker rm -f pg
# ВСЁ ПРОПАЛО
Решение: volumes — хранилища вне контейнера, переживают удаление.
Три типа хранилищ
1. Anonymous volume (анонимный)
Создаётся автоматически. Docker генерирует имя.
docker run -v /var/lib/postgresql/data postgres
Плюс: просто. Минус: непонятное имя, сложно искать.
2. Named volume (именованный)
Явно указываешь имя. Docker хранит в /var/lib/docker/volumes/.
docker run -v pg_data:/var/lib/postgresql/data postgres
Плюс: понятно, переиспользуется. Для: БД, кеши.
3. Bind mount (привязка к хосту)
Монтируешь папку с хоста.
docker run -v /home/user/code:/var/www/html nginx
Плюс: видно с хоста, удобно для разработки. Минус: проблема с правами.
Для: кода во время разработки.
Управление volumes
docker volume ls
docker volume create pg_data
docker volume inspect pg_data
docker volume rm pg_data
docker volume prune # удалить неиспользуемые
Где лежат:
ls /var/lib/docker/volumes/
Практика: PostgreSQL с volume
1. Запусти с volume:
docker run -d --name pg \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=skills_db \
-p 5432:5432 \
-v pg_data:/var/lib/postgresql/data \
postgres:16-alpine
2. Создай данные:
docker exec -it pg psql -U postgres -d skills_db
CREATE TABLE test (id serial, name text);
INSERT INTO test (name) VALUES ('hello');
SELECT * FROM test;
\q
3. Удали контейнер:
docker rm -f pg
4. Запусти заново с тем же volume:
docker run -d --name pg \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=skills_db \
-p 5432:5432 \
-v pg_data:/var/lib/postgresql/data \
postgres:16-alpine
5. Проверь данные:
docker exec -it pg psql -U postgres -d skills_db -c "SELECT * FROM test;"
# Данные на месте!
Volume спасает данные.
Практика: bind mount
Создай HTML:
mkdir -p /tmp/site
echo '<h1>Hello from bind mount</h1>' > /tmp/site/index.html
Запусти Nginx:
docker run -d --name web \
-p 8080:80 \
-v /tmp/site:/usr/share/nginx/html \
nginx
Проверь:
curl http://localhost:8080
# <h1>Hello from bind mount</h1>
Измени файл на хосте:
echo '<h1>Updated!</h1>' > /tmp/site/index.html
curl http://localhost:8080
# <h1>Updated!</h1>
Изменения сразу видны.
Read-only mount
Монтировать только для чтения:
docker run -v /tmp/site:/usr/share/nginx/html:ro nginx
ro — контейнер не может писать.
Для: конфигов, сертификатов.
Проблема с правами
Симптом: файлы, созданные контейнером, принадлежат root на хосте.
Проверка:
docker exec -it my-app touch /var/www/test.txt
ls -la /var/www/test.txt
# -rw-r--r-- 1 root root
Решение: указать UID/GID пользователя.
docker run -u $(id -u):$(id -g) -v $PWD:/var/www my-app
Или создавать пользователя в Dockerfile с нужным UID.
Сети
Зачем
Проблема: два контейнера не знают друг друга по имени.
Решение: создать сеть и подключить контейнеры.
Виды сетей
1. bridge (по умолчанию) — для контейнеров на одном хосте.
2. host — контейнер использует сеть хоста напрямую.
3. none — без сети.
4. overlay — для кластера (Swarm, K8s).
5. macvlan — как отдельное устройство в сети.
Управление сетями
docker network ls
docker network create my-network
docker network inspect my-network
docker network rm my-network
docker network prune
Практика: два контейнера
1. Создай сеть:
docker network create skills-net
2. Запусти PostgreSQL:
docker run -d --name pg \
--network skills-net \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=skills_db \
postgres:16-alpine
3. Запусти приложение:
docker run -d --name app \
--network skills-net \
-e DB_HOST=pg \
-e DB_DATABASE=skills_db \
-e DB_USERNAME=postgres \
-e DB_PASSWORD=secret \
my-laravel-app
4. Проверь связь:
docker exec -it app ping pg
# Работает
docker exec -it app nc -zv pg 5432
# Connection to pg 5432 port [tcp/postgresql] succeeded!
Docker DNS резолвит имена контейнеров в IP.
5. Работает в приложении:
В Laravel .env:
DB_HOST=pg
DB_DATABASE=skills_db
DB_USERNAME=postgres
DB_PASSWORD=secret
Порт пробрасывать наружу не нужно — контейнеры в одной сети видят друг друга.
Ports — что публиковать
Только то, что нужно извне:
- Веб-сервер — да,
-p 80:80,-p 443:443 - Приложение (Laravel/Node) — да,
-p 8000:8000(для отладки) - БД — нет, если приложение в той же сети
- Redis — нет, если приложение в той же сети
Правило: наружу — только порт Nginx (или LB).
Проброс хоста
Если контейнер слушает только 127.0.0.1 внутри — проброс не сработает извне.
Причина: контейнер слушает loopback, не 0.0.0.0.
Пример — приложение:
# Плохо — слушает только 127.0.0.1 внутри
my-app --host=127.0.0.1
# Хорошо — слушает все интерфейсы
my-app --host=0.0.0.0
Для Next.js:
npx next start -H 0.0.0.0 -p 3000
Просмотр сети контейнера
docker inspect my-app | grep IPAddress
docker exec -it my-app cat /etc/hosts
docker network inspect skills-net
Итог
Volumes:
- Named — для данных БД, кеша
- Bind mount — для кода при разработке
- Read-only — для конфигов
Сети:
- Контейнеры в одной сети видят друг друга по имени
- Наружу публикуй только нужные порты
- Приложения должны слушать
0.0.0.0, не127.0.0.1
Дальше — Dockerfile основы.