Docker и Docker Compose: Полное руководство от А до Я

Какие проблемы решает Docker?

  • Конфликты зависимостей: Позволяет запускать на одном сервере приложения, требующие разные версии Python, Node.js или системных библиотек.
  • Изоляция: Приложение «думает», что оно одно в системе. Оно не может случайно повредить файлы хоста или другого контейнера.
  • Гарантия идентичности: Образ, собранный в одной системе, это все тот же образ в другой.
  • Эффективное использование ресурсов: В отличие от VM, контейнеры запускаются за доли секунды и потребляют минимум памяти.

Как работает Docker изнутри

Docker использует клиент-серверную архитектуру.

  1. Docker Daemon (dockerd): Фоновый процесс на хосте, который управляет объектами (образами, контейнерами, сетями и тд…).
  2. Docker Client: Утилита командной строки (docker), через которую мы отдаем команды демону.
  3. 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 делает три действия сразу:

  1. Pull: Ищет образ локально, и если не находит — скачивает его.
  2. Create: Создает контейнер из образа.
  3. 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-app

docker 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 whoami

docker 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 prune

Docker спросит подтверждение и удалит всё лишнее.

# Удалить вообще всё
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 prune

docker 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_name

docker kill

Принудительно останавливает один или несколько работающих контейнеров.

Разница с docker stop

docker stop посылает сигнал SIGTERM (вежливая просьба закрыться), давая время на сохранение данных. kill посылает SIGKILL (немедленное завершение процесса).

ФлагОписаниеЗачем нужен
-s (--signal)Отправить специфичный сигнал вместо SIGKILL.Для отладки приложений, реагирующих на разные системные сигналы.

Пример

# Немедленно убить зависший контейнер
docker kill my_stuck_app

docker 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:8080

docker 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.

Зачем нужны теги?

  1. Версионирование: Вы можете пометить один и тот же образ как v1.0.5 и как latest.
  2. Подготовка к отправке (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 -d

docker compose up

Читает файл конфигурации, собирает/скачивает образы и запускает все сервисы одной командой.

ФлагОписаниеЗачем нужен
-d (detached)Запуск в фоновом режиме.Чтобы консоль не закрывалась логами и можно было работать дальше.
--buildПринудительная пересборка.Если вы изменили код или Dockerfile, этот флаг заставит Docker собрать новый образ перед запуском.
--scaleМасштабирование сервиса.Например, --scale worker=3 запустит 3 экземпляра сервиса worker.
--force-recreateПересоздать контейнеры.Даже если конфигурация не менялась, Docker удалит старые и создаст новые контейнеры.
Примеры
# Запустить проект в фоне и пересобрать образы, если были изменения
docker compose up -d --build

docker 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 migrate

docker 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

Нюансы форматирования:

  1. Массивы vs Строки: Порты и переменные можно писать списком (- "80:80") или мапой. Список через дефис — стандарт.
  2. Кавычки для портов: Всегда берите порты в кавычки "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 объединяет их в один итоговый конфиг по следующим правилам:

  1. Новые ключи — добавляются (например, добавились новые переменные окружения).
  2. Существующие ключи — заменяются (например, порт 8080:80 заменится на 80:80).
  3. Списки (порты, тома) — объединяются или заменяются в зависимости от контекста.

Практический пример

Базовый файл: 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

Главные преимущества этого подхода

  1. Безопасность: В файле prod вы отключаете проброс портов БД наружу (5432:5432), который нужен вам в dev, но смертельно опасен в интернете.
  2. Чистота: Вам не нужно поддерживать два полных файла по 100 строк каждый. Если вы добавите новый сервис (например, Redis), вы пропишете его только в базовом файле.
  3. Гибкость: Можно создать файл docker-compose.test.yml специально для прогона тестов в CI/CD (например, с временной пустой базой).

Совет

Используйте команду docker compose -f file1.yml -f file2.yml config. Она не запускает контейнеры, а просто выводит в консоль итоговый результат слияния. Это лучший способ проверить, не ошиблись ли вы в отступах или логике переопределения.