Docker и Docker Compose: Полное руководство от А до Я
Какие проблемы решает Docker?
- Конфликты зависимостей: Позволяет запускать на одном сервере приложения, требующие разные версии Python, Node.js или системных библиотек.
- Изоляция: Приложение «думает», что оно одно в системе. Оно не может случайно повредить файлы хоста или другого контейнера.
- Гарантия идентичности: Образ, собранный в одной системе, это все тот же образ в другой.
- Эффективное использование ресурсов: В отличие от VM, контейнеры запускаются за доли секунды и потребляют минимум памяти.
Как работает Docker изнутри
Docker использует клиент-серверную архитектуру.
- Docker Daemon (dockerd): Фоновый процесс на хосте, который управляет объектами (образами, контейнерами, сетями и тд…).
- Docker Client: Утилита командной строки (
docker), через которую мы отдаем команды демону. - Docker Registry: Хранилище образов (например, Docker Hub).
Ключевые понятия:
- Dockerfile - текстовый файл с последовательными инструкциями.
- Image (Образ) - замороженная копия файловой системы состоящая из слоев.
- Layer (Слой) - каждая команда в Dockerfile создает новый слой. Если вы изменили только последнюю строку кода, Docker пересоберет только этот слой, используя кэш для остальных.
- Контейнер - это запущенный экземпляр образа, который работает как изолированный процесс в операционной системе.
Примечание
Изменение одного слоя ведет к пересборке этого слоя и всех последующих.
Отличия Docker от VM
Главное отличие — в уровне виртуализации.
| Характеристика | Виртуальная машина | Docker Контейнер |
|---|---|---|
| Основа | Гипервизор (виртуализация железа) | Ядро основной ОС (изоляция процессов) |
| Гостевая ОС | Нужна полная ОС внутри каждой ВМ | Не нужна (использует ядро хоста) |
| Вес | Гигабайты | Мегабайты |
| Скорость запуска | Минуты | Миллисекунды |
| Производительность | Ниже (из-за накладных расходов) | Почти нативная (как у обычного процесса) |
Docker Compose
Если Docker — это один кирпич, то Docker Compose — это план всего здания. Он позволяет описать многоконтейнерное приложение (база + бэкенд + фронтенд + redis) в одном файле docker-compose.yml и запустить всё одной командой.
Справочник команд Docker
`docker build
Запускает процесс превращения Dockerfile в готовый к работе образ.
При запуске docker build ., отправляется контекст.
Точка . в конце команды — это путь к папке. Docker берет все файлы из этой папки и отправляет их демону (dockerd).
Нюанс
Если в папке лежат гигабайты ненужных данных (например, папка
node_modulesили логи), сборка будет идти очень долго. Используйте файл.dockerignore, чтобы исключить лишнее.
| Флаг | Описание | Пример |
|---|---|---|
-t (tag) | Задает имя и тег образу в формате имя:тег. | -t my-app:1.0 |
-f (file) | Путь к Dockerfile (если он называется не Dockerfile или лежит в другой папке). | -f Dockerfile.dev |
--build-arg | Передает переменные, которые доступны только в момент сборки. | --build-arg VERSION=1.2 |
--no-cache | Принудительная сборка без использования кэша слоев. | docker build --no-cache . |
--target | Если в Dockerfile несколько стадий (multi-stage), собирает только до указанной. | --target builder |
--pull | Всегда пытаться скачать свежую версию базового образа (например, python:3.14). Образ может не поменять название, но обновиться, использование флага --pull решает эту проблему. | docker build --pull . |
| Примеры использования |
# Docker ищет файл с названием `Dockerfile` в текущей директории, копирует файлы и создает образ с именем `my-python-app`.
docker build -t my-python-app:latest .# Сборка из специфичного файла (для разных окружений)
docker build -f Dockerfile.production -t my-app:prod .# Если в Dockerfile есть строка `ARG API_KEY`, можно передать его при сборке:
docker build --build-arg API_KEY=xyz123 -t secure-app .# Сборка до определенного этапа
docker build --target builder -t app-test .docker run
Команда docker run делает три действия сразу:
- Pull: Ищет образ локально, и если не находит — скачивает его.
- Create: Создает контейнер из образа.
- Start: Запускает процесс внутри контейнера.
| Флаг | Описание | Пример |
|---|---|---|
-d (detached) | Запуск в фоновом режиме. Консоль остается свободной. | docker run -d nginx |
-p (publish) | Проброс портов: порт_хоста:порт_контейнера. | -p 8080:80 |
--name | Присваивает контейнеру понятное имя вместо случайного (типа angry_beaver). | --name my-web |
-v (volume) | Подключение папки с компьютера внутрь контейнера (для данных). | -v /my/data:/data |
-e (env) | Передача переменной окружения внутрь контейнера. | -e DB_PASS=123 |
--rm | Автоматически удалить контейнер после того, как он будет остановлен. | docker run --rm ... |
-it | Интерактивный режим + псевдо-терминал (нужно для входа в консоль). Является комбинацией флагов -i(Сохраняет стандартный ввод открытым, даже если он не подключен) и -t(Создает псевдотерминал) | docker run -it alpine sh |
--network | Подключение контейнера к определенной виртуальной сети. | --network my-net |
--restart | Политика перезапуска (например, если упал или перезагрузился сервер). | --restart always |
| Примеры использования |
# Запуск веб-сервера (Nginx)
docker run -d -p 80:80 --name web-server nginx
# `-d`: Работает в фоне.
# `-p 80:80`: Запросы на ваш компьютер по порту 80 уйдут в порт 80 контейнера.# Одноразовая задача (проверить версию Python)
docker run --rm python:3.11 python --version
# `--rm`: Контейнер создастся, напечатает версию и тут же удалится, не захламляя систему.# Вход «внутрь» контейнера (Интерактивно)
docker run -it --name debug-box ubuntu bash
# Вы окажетесь внутри Ubuntu. Когда напишете `exit`, контейнер остановится.# Запуск базы данных с томом и паролем
docker run \
--name some-postgres
-e POSTGRES_PASSWORD=mysecretpassword
-v pg_data:/var/lib/postgresql/data \
-d postgres:17-alpine
# -v pg_data:...: Даже если вы удалите контейнер, данные в именованном томе `pg_data` сохранятся.
docker ps
Показывает список всех запущенных контейнеров и их текущее состояние.
По умолчанию команда выводит таблицу с информацией только о запущенных в данный момент контейнерах. Если контейнер упал с ошибкой или вы его остановили, в обычном списке docker ps он не появится.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-a (--all) | Показать все контейнеры. | Найти остановленные контейнеры или те, что завершились с ошибкой. |
-q (--quiet) | Вывести только ID контейнеров. | Удобно для автоматизации (передачи ID в другие команды). |
-s (--size) | Показать размер контейнера. | Узнать, сколько места занимает «слой записи» (writable layer). |
-l (--latest) | Показать только последний созданный контейнер. | Быстро проверить статус только что запущенного сервиса. |
-n [число] | Показать последние n созданных контейнеров. | Если вы запускали пачку сервисов. |
--format | Вывод в кастомном виде (JSON или таблица с нужными колонками). | Для скриптов или красивых отчетов. |
--filter | Фильтрация по условиям (статус, имя, образ). | Найти все контейнеры, которые «упали» (exited). |
Флаг --filter (Фильтрация) |
docker ps --filter "ключ:значение"| Ключ | Что делает | Пример |
|---|---|---|
status | Фильтрует по состоянию (created, restarting, running, removing, paused, exited, dead). | status=exited |
name | Ищет контейнер по имени (можно часть имени). | name=webapp |
ancestor | Фильтрует по образу (показывает все контейнеры, созданные из этого образа). | ancestor=postgres:17-alpine |
id | Поиск по ID контейнера. | id=7b34a... |
label | Ищет контейнеры с определенной меткой (указывается в Dockerfile или Compose). | label=version=1.0 |
expose / publish | По портам, которые открыты или проброшены. | publish=80/tcp |
health | По результатам проверки здоровья (starting, healthy, unhealthy). | health=unhealthy |
Флаг --format (Форматирование) |
Пример
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"Основные переменные (Placeholders):
Все переменные пишутся в двойных фигурных скобках: {{.Название}}.
.ID— Идентификатор контейнера..Names— Имя..Image— Образ..Status— Статус..Ports— Порты..Size— Размер..Mounts— Примонтированные тома.
Или вывести в json:
docker ps --format jsonПримеры
# Вывод всех контейнеров(в том числе не рабочих)
docker ps -a# Быстрое удаление всех контейнеров из системы
docker rm -f $(docker ps -aq)# Фильтрация по статусу
docker ps --filter "status=restarting"# Красивый вывод
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}"docker logs
Позволяет заглянуть внутрь контейнера и увидеть всё, что приложение вывело в стандартные потоки вывода (stdout и stderr). Другими словами - позволяет видеть логи контейнера.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-f (--follow) | Вывод будет обновляться в реальном времени. | Когда нужно видеть, что происходит в момент обращения к приложению. |
-n / --tail | Показать последние N строк. | Чтобы не ждать, пока прогрузятся тысячи старых строк логов. |
-t (--timestamps) | Показать время каждой строки. | Если само приложение не пишет время в логах, Docker добавит своё. |
--since | Показать логи после определенного времени. | Например: --since 10m (за последние 10 минут) или конкретная дата. |
--until | Показать логи до определенного времени. | Ограничить поиск «сверху» по времени. |
| Примеры |
# Логи в реальном времени
docker logs -f my-web-app# Вывести последние 50 строк
docker logs --tail 50 my-web-app# Вывод за определенный период, например сервис упал 20 минут назад, выводим промежуток от 30 минут назад, до 15 минут назад
docker logs --since 30m --until 15m my-web-app# Вывод в реальном временр послежних 10 строк логов с добавлением времени от docker
docker logs -f -t --tail 10 my-web-appdocker exec
Позволяет выполнять команды внутри запущенного контейнера.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-i (--interactive) | Интерактивный режим. | Оставляет поток ввода открытым, даже если вы не подключены. (Пример: команда требует ввода текста, без этого флага мы не сможем его ввести) |
-t (--tty) | Выделяет псевдо-терминал. | Делает работу в консоли удобной (появляется цвет, автодополнение по Tab). |
-u (--user) | Запуск команды от имени конкретного пользователя. | Если нужно выполнить действие от root или наоборот от containeruser. |
-w (--workdir) | Задает рабочую директорию для команды. | Чтобы не писать полные пути к файлам. |
-e (--env) | Устанавливает временную переменную окружения. | Для тестов, которые зависят от ENV-переменных. |
-d (--detach) | Запустить команду в фоне. | Выполнил и забыл (например, запуск скрипта бэкапа). |
| Примеры |
# Вход в терминал контейнера
docker exec -it my_container shПримечание
Используйте
bash, если он установлен в образе, илиshдля легких образов типа Alpine.
# Проверить содержимое папки, не прерывая работу контейнера
docker exec my_container ls -la /app/logs# Сделать дамп (бэкап) базы данных прямо с хоста
docker exec my_postgres_db pg_dump -U username db_name > backup.sql# Иногда приложение работает от пользователя `www-data`, и вам нужно проверить права доступа
docker exec -it -u www-data my_web_server whoamidocker rm
Удаляет контейнер. По умолчанию docker rm может удалить только остановленный контейнер. Если вы попытаетесь удалить работающий контейнер, Docker выдаст ошибку, защищая вас от случайного прерывания важного процесса.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-f (--force) | Принудительное удаление. | Удаляет контейнер, даже если он запущен (Docker просто «убивает» процесс сигналом SIGKILL). |
-v (--volumes) | Удалить анонимные тома. | Если к контейнеру были привязаны временные данные, которые не имеют имени, этот флаг их зачистит. |
-l (--link) | Удалить связь (link). | Используется редко, удаляет только сетевую связь между контейнерами, но не сам контейнер. |
| Примеры использования |
# Простое удаление по имени или ID
docker rm my_web_serverПримечание
Сработает только если контейнер остановлен.
# Удаление работающего контейнера
docker rm -f my_working_app# Удаление вместе с анонимными томами
docker rm -fv database_testПримечание
Полезно для тестов: удаляет и контейнер, и временные файлы, которые он создал в своих томах, чтобы не засорять диск.
# Удалить все остановленные контейнеры
docker container pruneDocker спросит подтверждение и удалит всё лишнее.
# Удалить вообще всё
docker rm -f $(docker ps -aq)Примечание
Эта команда сначала берет список ID всех контейнеров в системе (
ps -aq) и передает их вrm -f.
docker image / docker images
Выводит список всех образов, которые хранятся на вашем компьютере.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-a (--all) | Показать все образы, включая промежуточные слои. | Для полной инспекции дискового пространства. |
-q (--quiet) | Вывести только ID образов. | Для массового удаления образов скриптами. |
--digests | Показать хеш-суммы образов (sha256). | Для проверки целостности и точной идентификации версии. |
--format | Кастомный вывод через шаблоны. | Для создания отчетов или JSON выгрузок. |
Пример
# Показать все локальные образы
docker images# Удалить все неиспользуемые (dangling) образы
docker image prunedocker inspect
Возвращает низкоуровневую информацию об объектах Docker (контейнерах, образах, сетях, томах) в формате JSON.
Нюанс
Информация очень подробная: от IP-адреса и конфигурации сети до путей монтирования и переменных окружения.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-f (--format) | Извлечь конкретное значение из JSON. | Чтобы не читать весь JSON, а получить только IP или статус. |
-s (--size) | Показать размер объекта (для контейнеров). | Увидеть реальный вес работающего процесса. |
| Пример |
# Узнать IP-адрес контейнера
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my_container# Посмотреть всю конфигурацию сети или тома
docker inspect my_network_namedocker kill
Принудительно останавливает один или несколько работающих контейнеров.
Разница с docker stop
docker stopпосылает сигнал SIGTERM (вежливая просьба закрыться), давая время на сохранение данных. kill посылает SIGKILL (немедленное завершение процесса).
| Флаг | Описание | Зачем нужен |
|---|---|---|
-s (--signal) | Отправить специфичный сигнал вместо SIGKILL. | Для отладки приложений, реагирующих на разные системные сигналы. |
Пример
# Немедленно убить зависший контейнер
docker kill my_stuck_appdocker network
Управляет виртуальными сетями, в которых общаются контейнеры. Позволяет изолировать сервисы друг от друга.
| Подкоманда | Описание | Пример |
|---|---|---|
ls | Список всех сетей. | docker network ls |
create | Создать новую сеть. | docker network create my_app_net |
connect | Подключить работающий контейнер к сети. | docker network connect net_name cont_name |
inspect | Посмотреть, кто подключен к сети и их IP. | docker network inspect bridge |
rm | Удалить сеть. | docker network rm my_net |
docker port
Показывает, на какой порт хоста “проброшен” порт контейнера.
Пример
# Посмотреть соответствие портов для контейнера web
docker port web
# Вывод: 80/tcp -> 0.0.0.0:8080docker pull / docker push
Команды для обмена образами с удаленным реестром (Registry, например Docker Hub).
pull— скачивает образ из облака на ПК.push— загружает ваш локальный образ в облако.
| Флаг | Описание | Пример |
|---|---|---|
-a (--all-tags) | Скачать все теги конкретного образа (например, все версии python). | docker pull -a python |
Нюанс
Чтобы сделать push, образ должен быть тегирован вашим логином на Docker Hub:
docker tag my-app:v1 mylogin/my-app:v1.
docker secret / docker service
Команды для работы в режиме Docker Swarm (оркестрация).
secret— безопасное хранение паролей/ключей (недоступны в обычной файловой системе).service— создание и масштабирование “служб” (групп контейнеров).
Важно
В локальной разработке чаще используется Docker Compose. Эти команды нужны при развертывании кластера из нескольких серверов.
docker start / docker stop / docker restart
Управление созданными контейнерами.
start— запускает остановленный контейнер.stop— вежливо останавливает (SIGTERM).restart— стоп + старт.
docker volume
Управляет постоянными данными (Volumes). Это лучший способ сохранять данные БД или логи вне контейнера.
| Подкоманда | Описание | Пример |
|---|---|---|
ls | Список всех томов. | docker volume ls |
create | Создать именованный том. | docker volume create pg_data |
inspect | Узнать, где физически на диске лежат файлы тома. | docker volume inspect pg_data |
rm | Удалить том (данные будут стерты!). | docker volume rm pg_data |
docker system
Это сервисная команда для анализа и очистки всего Docker-окружения. Когда у вас внезапно кончается место на диске, docker system — это первое, куда стоит смотреть.
| Подкоманда | Что делает | Зачем это нужно |
|---|---|---|
df | Показывает, сколько места занимают образы, контейнеры и тома. | Чтобы понять, что именно «съело» диск: старые логи или забытые образы. |
events | Показывает поток событий от Docker в реальном времени. | Для отладки: когда контейнер запускается, останавливается или падает. |
info | Выводит полную информацию о системе (версия, драйверы, кол-во ядер). | Для проверки лимитов и настроек Docker Engine. |
prune | Удаляет весь неиспользуемый «мусор». | Главная команда для очистки системы. |
docker tag
Команда tag не создает копию образа (она не дублирует файлы на диске). Она создает alias (ссылку) на уже существующий Image ID. Это как ярлык на рабочем столе Windows.
Зачем нужны теги?
- Версионирование: Вы можете пометить один и тот же образ как
v1.0.5и какlatest. - Подготовка к отправке (Push): Docker Hub (или другой реестр) поймет, в какой репозиторий отправлять образ, только если в его теге указан адрес или логин.
| Структура тега | my-registry.com:5000 / my-login / repository-name : tag |
|---|---|
| Части | [Адрес сервера] / [Логин] / [Имя приложения] : [Версия] |
Примеры использования
# Создание версии для архива
docker tag my-app:latest my-app:v1.0.1# Подготовка образа для загрузки на Docker Hub
# Если ваш логин "ivanov", Docker позволит запушить образ только если он так называется:
docker tag my-app:latest ivanov/my-app:v1.0
docker push ivanov/my-app:v1.0# Привязка к конкретному серверу (Private Registry)
docker tag my-app:latest cr.yandex/my-project/my-app:prodНюанс
Если вы удалите один из тегов через docker rmi, сам образ и его файлы останутся на диске, пока на него ссылается хотя бы один другой тег. Образ удалится физически только тогда, когда будет удален последний тег.
Команды Docker Compose
Команды Docker Compose во многом дублируют обычный Docker, но их главная сила в том, что они применяются сразу ко всей группе сервисов
Имеет очень важный флаг:
-f (--file) - Указывает путь к конкретному YML-файлу. Чтобы разделять настройки для разработки, тестирования и продакшена.
# Использование специфичного файла для продакшена
docker compose -f docker-compose.prod.yml up -ddocker compose up
Читает файл конфигурации, собирает/скачивает образы и запускает все сервисы одной командой.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-d (detached) | Запуск в фоновом режиме. | Чтобы консоль не закрывалась логами и можно было работать дальше. |
--build | Принудительная пересборка. | Если вы изменили код или Dockerfile, этот флаг заставит Docker собрать новый образ перед запуском. |
--scale | Масштабирование сервиса. | Например, --scale worker=3 запустит 3 экземпляра сервиса worker. |
--force-recreate | Пересоздать контейнеры. | Даже если конфигурация не менялась, Docker удалит старые и создаст новые контейнеры. |
| Примеры |
# Запустить проект в фоне и пересобрать образы, если были изменения
docker compose up -d --builddocker compose down
Полная противоположность up. Останавливает и удаляет контейнеры и сети, созданные проектом.
| Флаг | Описание | Зачем нужен |
|---|---|---|
-v (volumes) | Удалить тома. | Очищает диск вместе с данными баз данных (будьте осторожны!). |
--rmi all | Удалить образы. | Удаляет все образы, которые использовались в проекте. |
docker compose logs
Агрегирует (собирает в одну кучу) логи от всех сервисов проекта. Работает аналогично docker logs.
docker compose ps / docker compose top / docker compose stats
Команды мониторинга состояния вашего “оркестра”.
ps: Список контейнеров проекта и их статус. Работает аналогично docker ps.top: Список активных процессов внутри каждого контейнера.stats: Потребление ресурсов (CPU, RAM, Сеть) в реальном времени.
docker compose exec / docker compose run
Разница между ними критически важна:
exec: Заходит в уже работающий контейнер и выполняет команду.run: Создает новый временный контейнер для выполнения одной задачи.
| Команда | Типичный случай |
|---|---|
exec | Проверить конфиги или зайти в консоль базы данных. |
run | Запустить миграции БД или разовый скрипт обработки. |
| Примеры |
# Выполнить миграции (создаст новый контейнер, выполнит и выйдет)
docker compose run --rm backend python manage.py migratedocker compose build / docker compose pull
build: Только собирает образы из Dockerfile, не запуская контейнеры.pull: Только скачивает готовые образы из реестра (Docker Hub).
docker compose restart / docker compose start / docker compose stop / docker compose kill
Работают аналогично docker restart, docker start, docker stop,docker kill
Управляют состоянием уже созданных контейнеров без их удаления:
stop: Вежливо выключает.start: Включает ранее остановленные.restart: Перезапуск (часто используется после правки конфигов).kill: Немедленно “убивает” процесс (не рекомендуется для БД).
docker compose watch
Автоматически обновляет сервисы при изменении файлов в коде.
Нюанс
Требует специальной настройки секции
developвdocker-compose.yml. Это современная замена старому методу прокидывания кода черезvolumesдля “hot reload”.
docker compose images
Показывает список образов, используемых текущим проектом, их ID и размеры. Помогает понять, какие образы нужно почистить. Работает аналогично docker images
docker compose config
Проверяет ваш docker-compose.yml на ошибки форматирования и показывает итоговый конфиг (со всеми подставленными переменными из .env). Идеально для дебага перед деплоем.
docker compose wait
Решает старую проблему: «Контейнер с базой данных уже запущен, но сама база еще не прогрузилась и не принимает соединения, а бэкенд уже пытается к ней подключиться и падает».
Ранее для этого использовали сторонние скрипты вроде wait-for-it.sh, теперь это встроено в Docker.
Команда блокирует выполнение последующих команд в терминале до тех пор, пока контейнеры не перейдут в состояние Ready (Готов).
| Флаг | Описание | Зачем нужен |
|---|---|---|
[SERVICE] | Имя конкретного сервиса. | Если не указать, Docker будет ждать все сервисы из файла. |
--timeout | Максимальное время ожидания (в секундах). | Чтобы скрипт не “висел” вечно, если сервис так и не запустился. |
--down-on-timeout | Удалить контейнеры при ошибке. | Если время вышло, а сервисы не готовы — Docker сам сделает down. |
Как Docker понимает, что сервис «готов»?
Для работы этой команды в docker-compose.yml у сервиса обязательно должна быть настроена секция healthcheck. Без неё Docker будет считать сервис готовым сразу после старта процесса, что теряет смысл.
Примеры
# В скрипте деплоя: сначала запускаем, потом ждем готовности, потом запускаем тесты
docker compose up -d
docker compose wait db backend --timeout 60
# Эта строка выполнится только когда db и backend станут "healthy"
npm run test-integration# Ждать вообще все сервисы проекта
docker compose waitПравила форматирования docker-compose.yml
YML крайне чувствителен к пробелам (табы использовать нельзя!).
services:
# 1. База данных (Backend-инфраструктура)
db:
image: postgres:17-alpine
container_name: production_db
restart: unless-stopped # Не перезапускать, если остановлен вручную
shm_size: 128mb # Увеличение разделяемой памяти (важно для тяжелых запросов)
networks:
- database_net # Изолированная сеть для БД
environment:
POSTGRES_DB: app_db
POSTGRES_USER: admin
POSTGRES_PASSWORD_FILE: /run/secrets/db_password # Читаем пароль из секрета (безопасно)
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d app_db"]
interval: 5s
timeout: 5s
retries: 5
# 2. Кэш (In-memory хранилище)
redis:
image: redis:7-alpine
networks:
- cache_net
command: ["redis-server", "--appendonly", "yes"] # Включаем сохранение данных на диск
# 3. Основное приложение (Backend)
api:
build:
context: ./backend
dockerfile: Dockerfile
args:
- APP_VERSION=1.0.0
expose:
- "8000" # Открывает порт внутри сети, но не публикует его на хост (безопасно)
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- database_net
- cache_net
- public_net # Имеет доступ и к БД, и к внешнему миру (через прокси)
env_file: .env # Загрузка переменных из внешнего файла
develop: # Настройки для docker compose watch
watch:
- action: sync # Синхронизировать код без перезапуска
path: ./backend/src
target: /app/src
- action: rebuild # Пересобрать образ при изменении зависимостей
path: ./backend/package.json
# 4. Фронтенд (Frontend)
frontend:
build: ./frontend
networks:
- public_net
develop:
watch:
- action: sync
path: ./frontend/src
target: /usr/src/app/src
ignore:
- node_modules/
# 5. Обратный прокси (Nginx) — входная точка
gateway:
image: nginx:stable-alpine
ports:
- "80:80" # Публикация портов на хост-машину
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro # Монтирование только для чтения (ro)
- cert_data:/etc/letsencrypt
networks:
- public_net
depends_on:
- api
- frontend
# --- Глобальные настройки ---
networks:
# Сети обеспечивают изоляцию
database_net: # Только API и DB
driver: bridge
cache_net: # Только API и Redis
driver: bridge
public_net: # API, Frontend и Nginx
driver: bridge
volumes:
postgres_data: # Постоянное хранилище БД
driver: local
cert_data: # Общий том для SSL-сертификатов
secrets:
# Секреты хранятся в файлах на хосте и не попадают в переменные окружения (защита от утечки)
db_password:
file: ./secrets/db_password.txtНюансы форматирования:
- Массивы vs Строки: Порты и переменные можно писать списком (
- "80:80") или мапой. Список через дефис — стандарт. - Кавычки для портов: Всегда берите порты в кавычки
"80:80", иначе YML может интерпретировать их как числа в 60-ричной системе.
Синхронизация изменений в коде
...
develop:
watch:
- action: sync # Синхронизировать код без перезапуска
path: ./backend/src
target: /app/src
- action: rebuild # Пересобрать образ при изменении зависимостей
path: ./backend/package.json
...Данный конфиг синхронизирует локальные файлы и файлы в контейнере. Мы говорим, что содержимое ./backend/src - мы синхронизируем без перезапуска А содержимое package.json синхронизируем с пересборкой контейнера.
Сетевые драйверы (Network Drivers)
Bridge (Мост) - Стандарт по умолчанию. Это виртуальный коммутатор (свитч) внутри ПК.
- Как работает: Docker создает закрытую внутреннюю сеть (например,
172.18.0.0/16). Контейнеры в этой сети получают свои IP и могут общаться друг с другом по именам сервисов (благодаря встроенному DNS). - Почему именно он:
- Изоляция: Внешний мир не видит контейнеры, пока ты явно не пробросишь порт (
-p). - Безопасность: Контейнеры в разных bridge-сетях не видят друг друга.
- Удобство: Это идеальный баланс между безопасностью и простотой для работы на одном сервере.
- Изоляция: Внешний мир не видит контейнеры, пока ты явно не пробросишь порт (
Host Контейнер не получает своего IP, а использует IP самого сервера.
- Зачем: Максимальная скорость (нет затрат на виртуализацию сети).
- Минус: Нет изоляции. Если контейнер займет 80 порт, ты не сможешь запустить второй такой же на этом же сервере.
Overlay Драйвер для связи контейнеров на разных физических серверах (используется в Docker Swarm или Kubernetes). Он создает “единую сеть поверх интернета”.
None У контейнера вообще нет сетевого интерфейса.
Macvlan Позволяет назначить контейнеру реальный MAC-адрес, чтобы он выглядел в твоей физической сети как отдельный компьютер.
Драйверы томов (Volume Drivers)
Local - Стандарт по умолчанию
Docker просто создает папку в своих системных каталогах (обычно /var/lib/docker/volumes/) и пишет данные туда.
- Плюс: Надежно и просто. Работает везде.
- Минус: Данные привязаны к конкретному физическому диску этого сервера.
Cloud-драйверы (AWS EBS, Azure File Storage, GCP Storage)
Позволяют Docker монтировать облачные диски напрямую. Если сервер с контейнером упадет, Docker запустит его на другом сервере и «переподключит» облачный диск с данными.
NFS / Samba Позволяют монтировать сетевые папки как обычные тома. Это удобно, когда несколько серверов должны иметь доступ к одной общей папке с картинками или архивами.
Про secrets можно подробнее почитать здесь.
Разделение Dev и Prod окружений
Никогда не используйте один и тот же конфиг для разработки и production версии.
Слоеная конфигурация (Overriding)
Overriding (переопределение) - профессиональный стандарт работы с Docker Compose. Позволяет не дублировать код, а описывать «базу» один раз и накладывать на неё специфические настройки для разных сред.
Как работает механизм слияния
Когда вы указываете несколько файлов через флаг -f, Docker объединяет их в один итоговый конфиг по следующим правилам:
- Новые ключи — добавляются (например, добавились новые переменные окружения).
- Существующие ключи — заменяются (например, порт
8080:80заменится на80:80). - Списки (порты, тома) — объединяются или заменяются в зависимости от контекста.
Практический пример
Базовый файл: docker-compose.yml
Здесь мы описываем скелет приложения, который неизменен везде.
services:
api:
image: my-app:latest
build: .
depends_on:
- db
networks:
- app_net
db:
image: postgres:15-alpine
networks:
- app_net
networks:
app_net:
driver: bridgeФайл для разработки: docker-compose.override.yml
Docker подтягивает его автоматически, если вы просто пишете docker compose up. Здесь мы добавляем удобства для программиста.
services:
api:
volumes:
- ./src:/app/src # Пробрасываем код для горячей перезагрузки
environment:
- DEBUG=True
ports:
- "8000:8000" # Открываем порт для тестов
db:
ports:
- "5432:5432" # Открываем БД, чтобы зайти через DBeaver/DataGripФайл для продакшена: docker-compose.prod.yml
Этот файл мы будем указывать вручную. Здесь приоритет — безопасность и стабильность.
services:
api:
restart: always
environment:
- DEBUG=False
# Порты НЕ открываем, так как API будет за Nginx
db:
restart: always
# Порты базы НЕ открываем! Доступ к ней будет только у API внутри сети
volumes:
- pg_prod_data:/var/lib/postgresql/data
volumes:
pg_prod_data:Как запускать разные среды
Режим разработки (Development)
Просто вводим привычную команду. Docker сам найдет docker-compose.yml и наложит на него override.yml.
docker compose up -dРежим продакшена (Production) Здесь мы явно указываем цепочку файлов. Важно: базовый файл всегда идет первым!
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -dГлавные преимущества этого подхода
- Безопасность: В файле
prodвы отключаете проброс портов БД наружу (5432:5432), который нужен вам вdev, но смертельно опасен в интернете. - Чистота: Вам не нужно поддерживать два полных файла по 100 строк каждый. Если вы добавите новый сервис (например, Redis), вы пропишете его только в базовом файле.
- Гибкость: Можно создать файл
docker-compose.test.ymlспециально для прогона тестов в CI/CD (например, с временной пустой базой).
Совет
Используйте команду
docker compose -f file1.yml -f file2.yml config. Она не запускает контейнеры, а просто выводит в консоль итоговый результат слияния. Это лучший способ проверить, не ошиблись ли вы в отступах или логике переопределения.