Если вы когда-нибудь слышали фразу «у меня на машине работает, а на сервере нет» — добро пожаловать в мир, до которого ещё не дошёл Docker. Именно эту проблему он и решает. В этой статье разберём, что такое Docker, зачем нужен Docker Compose, как они работают изнутри — и почему сегодня без них не обходится ни один серьёзный production-стенд.
- Docker — что это и зачем
- Определение
- Аналогия: грузовой контейнер
- Контейнер vs. Виртуальная машина
- Архитектура Docker
- Ключевые понятия Docker
- Образ (Image)
- Контейнер (Container)
- Dockerfile
- Volumes (Тома)
- Networks (Сети)
- Docker Compose
- Проблема без Compose
- Что такое Docker Compose
- Пример compose.yaml
- Основные команды Compose
- Ключевые возможности Docker Compose
- Зависимости между сервисами (depends_on)
- Переменные окружения и .env файлы
- Профили (Profiles)
- Масштабирование сервисов
- Типичные сценарии применения
- Среда разработки
- CI/CD и автоматическое тестирование
- Продакшн на одном сервере
- Шпаргалка по командам
- Docker — образы
- Docker — контейнеры
- Docker Compose
- Лучшие практики
- Один процесс — один контейнер
- Используйте .dockerignore
- Не запускайте процессы от root
- Фиксируйте версии образов
- Multi-stage builds для минимизации размера
- Healthcheck для критических сервисов
- Заключение
Docker — что это и зачем
Определение
Docker — это открытая платформа для разработки, доставки и запуска приложений. Его ключевая идея: отделить приложение от инфраструктуры, на которой оно работает.
Официальная документация описывает это так: Docker позволяет упаковать и запустить приложение в изолированной среде, называемой контейнером. Контейнеры легковесны, содержат всё необходимое для работы и не зависят от того, что установлено на хосте.
Аналогия: грузовой контейнер
Представьте морской контейнер. Раньше при погрузке на корабль для каждого типа груза нужны были разные способы укладки, разные крепления, разные условия. Морской контейнер стандартизировал этот процесс: неважно, что внутри — автомобиль, бананы или электроника. Снаружи всё контейнеры одинаковые, их можно грузить на любой корабль, поезд или грузовик.
Docker — это то же самое, но для программного обеспечения. Ваше приложение упаковывается в стандартный контейнер, который одинаково работает на ноутбуке разработчика, тестовом сервере и в облаке.
Контейнер vs. Виртуальная машина
Частый вопрос: «Чем контейнер отличается от виртуальной машины?»
| Параметр | Виртуальная машина | Docker-контейнер |
| Размер | Гигабайты | Мегабайты |
| Запуск | Минуты | Секунды |
| Изоляция | Полная (отдельное ядро ОС) | Уровень процессов (общее ядро) |
| Накладные расходы | Высокие | Минимальные |
| Переносимость | Средняя | Высокая |
Виртуальная машина эмулирует железо и запускает полноценную операционную систему. Контейнер же использует ядро хостовой ОС, изолируя только окружение процессов через механизмы Linux — cgroups (ограничение ресурсов) и namespaces (изоляция пространства имён). Это и делает контейнеры такими быстрыми и лёгкими.
Архитектура Docker
Docker работает по модели клиент-сервер. Ниже приведены ключевые компоненты:
| Компонент | Описание |
| Docker Client (CLI) | То, с чем работает пользователь. Команды docker run, docker build, docker ps. Клиент отправляет их в Daemon через REST API. |
| Docker Daemon (dockerd) | Фоновый сервис, который управляет образами, контейнерами, сетями и томами. Именно он выполняет реальную работу. |
| Docker Registry | Хранилище образов. Публичный реестр — Docker Hub. Можно поднять свой приватный реестр. |
| Docker Hub | Официальный публичный реестр образов. Содержит более 100 000 готовых образов от сообщества и вендоров. |
Ключевые понятия Docker
Образ (Image)
Docker Image — это шаблон только для чтения, из которого создаются контейнеры. Можно провести аналогию с ISO-образом диска или классом в объектно-ориентированном программировании.
Образ состоит из слоёв (layers). Каждая инструкция в Dockerfile создаёт отдельный слой. Это даёт два преимущества:
- Слои кешируются — повторная сборка происходит быстро, пересобираются только изменённые слои
- Образы эффективно переиспользуют общие слои — если два образа основаны на Ubuntu, слой с Ubuntu хранится один раз
Контейнер (Container)
Контейнер — запущенный экземпляр образа. Если образ — это класс, то контейнер — это объект (экземпляр класса). Из одного образа можно запустить сколько угодно контейнеров одновременно.
# Запустить контейнер из образа nginx
docker run -d -p 80:80 --name my-nginx nginx
# Посмотреть запущенные контейнеры
docker ps
# Зайти внутрь контейнера
docker exec -it my-nginx /bin/bash
# Остановить и удалить
docker stop my-nginx && docker rm my-nginx Dockerfile
Dockerfile — текстовый файл с набором инструкций для сборки образа. Каждая строка — это шаг, каждый шаг — новый слой в образе.
# Базовый образ
FROM python:3.11-slim
# Рабочая директория внутри контейнера
WORKDIR /app
# Сначала копируем только зависимости (кеширование слоёв)
COPY requirements.txt .
# Устанавливаем зависимости
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код приложения
COPY . .
# Открываем порт
EXPOSE 5000
# Команда запуска
CMD ["python", "app.py"] Совет по оптимизации: копируйте requirements.txt и устанавливайте зависимости ДО копирования кода приложения. Тогда при изменении кода слой с зависимостями не пересобирается — он уже в кеше.
Volumes (Тома)
По умолчанию данные внутри контейнера эфемерны: при удалении контейнера они пропадают. Volume — это механизм для хранения данных вне контейнера, на хостовой машине.
# Создать том
docker volume create my-data
# Запустить контейнер с томом
docker run -d -v my-data:/var/lib/mysql mysql:8.0
# Или монтировать директорию хоста
docker run -d -v /home/user/data:/var/lib/mysql mysql:8.0 Networks (Сети)
Docker позволяет создавать изолированные виртуальные сети для контейнеров. Контейнеры в одной сети могут обращаться друг к другу по имени.
# Создать сеть
docker network create my-network
# Запустить контейнеры в одной сети
docker run -d --network my-network --name app my-app
docker run -d --network my-network --name db postgres:15
# Из контейнера app можно обратиться к db по имени: db:5432 Docker Compose
Проблема без Compose
Представьте типичное веб-приложение: фронтенд, бэкенд на Node.js, база данных PostgreSQL, кеш Redis, брокер сообщений RabbitMQ. Запуск всего этого вручную выглядит так:
docker network create app-network
docker run -d \
--name postgres \
--network app-network \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
-v pgdata:/var/lib/postgresql/data \
postgres:15
docker run -d \
--name redis \
--network app-network \
redis:7
# ... и так далее для каждого сервиса Это громоздко, легко ошибиться и невозможно передать коллеге. Каждый раз нужно помнить все флаги и переменные окружения.
Что такое Docker Compose
Docker Compose — инструмент для определения и запуска многоконтейнерных приложений. Вся конфигурация описывается в одном YAML-файле (compose.yaml), а весь стек запускается одной командой.
Compose упрощает управление всем стеком приложения: легко управлять сервисами, сетями и томами в едином конфигурационном файле, а затем запустить всё одной командой: docker compose up.
Пример compose.yaml
services:
postgres:
image: postgres:15
container_name: app-postgres
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: app-redis
networks:
- app-network
backend:
build: ./backend
container_name: app-backend
ports:
- "3000:3000"
environment:
DB_HOST: postgres
DB_PORT: 5432
REDIS_HOST: redis
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
networks:
- app-network
frontend:
build: ./frontend
container_name: app-frontend
ports:
- "80:80"
depends_on:
- backend
networks:
- app-network
volumes:
pgdata:
networks:
app-network:
driver: bridge Основные команды Compose
# Запустить весь стек в фоне
docker compose up -d
# Посмотреть статус сервисов
docker compose ps
# Логи всех сервисов в реальном времени
docker compose logs -f
# Логи конкретного сервиса
docker compose logs -f backend
# Остановить и удалить контейнеры
docker compose down
# Остановить и удалить включая тома
docker compose down --volumes Ключевые возможности Docker Compose
Зависимости между сервисами (depends_on)
depends_on позволяет указать порядок запуска. С параметром condition: service_healthy Compose дождётся, пока healthcheck сервиса вернёт успех, прежде чем запустить зависящий от него сервис.
depends_on:
postgres:
condition: service_healthy Переменные окружения и .env файлы
Compose автоматически читает .env файл из директории проекта. Это позволяет хранить чувствительные данные отдельно от конфига.
# .env файл
DB_PASSWORD=supersecret
APP_PORT=3000
# compose.yaml
services:
backend:
environment:
DB_PASSWORD: ${DB_PASSWORD}
ports:
- "${APP_PORT}:3000" Профили (Profiles)
Профили позволяют включать/выключать группы сервисов при запуске.
services:
app:
image: my-app
debug-tools:
image: some-debug-image
profiles:
- debug # Запускается только с --profile debug
# Запустить с инструментами отладки
docker compose --profile debug up -d Масштабирование сервисов
# Запустить 3 экземпляра backend
docker compose up -d --scale backend=3 Типичные сценарии применения
Среда разработки
Compose позволяет сократить «гайд по настройке» для нового разработчика с 10 страниц до трёх команд:
git clone https://github.com/myorg/myapp
cd myapp
docker compose up -d CI/CD и автоматическое тестирование
Compose позволяет создавать и уничтожать изолированные тестовые среды всего несколькими командами:
# В pipeline: создаём окружение, запускаем тесты, сносим
docker compose up -d
./run_tests.sh
docker compose down Продакшн на одном сервере
Для небольших проектов Compose прекрасно работает в продакшне. Обновление образов:
# Обновить образы из реестра
docker compose pull
# Пересоздать только изменившиеся контейнеры
docker compose up -d Шпаргалка по командам
Docker — образы
| Команда | Описание |
| docker images | Список всех локальных образов |
| docker pull nginx:latest | Скачать образ из реестра |
| docker build -t myapp:1.0 . | Собрать образ из Dockerfile |
| docker rmi myapp:1.0 | Удалить образ |
| docker image prune | Удалить неиспользуемые образы |
Docker — контейнеры
| Команда | Описание |
| docker run -d -p 80:80 nginx | Запустить контейнер в фоне |
| docker ps | Список запущенных контейнеров |
| docker ps -a | Список всех контейнеров |
| docker stop <name> | Остановить контейнер |
| docker start <name> | Запустить остановленный контейнер |
| docker rm <name> | Удалить контейнер |
| docker logs <name> -f | Логи контейнера в реальном времени |
| docker exec -it <name> bash | Войти внутрь контейнера |
| docker system prune -a | Очистить всё неиспользуемое |
Docker Compose
| Команда | Описание |
| docker compose up -d | Запустить весь стек в фоне |
| docker compose down | Остановить и удалить контейнеры |
| docker compose ps | Статус всех сервисов |
| docker compose logs -f | Логи всех сервисов |
| docker compose logs -f <svc> | Логи конкретного сервиса |
| docker compose exec <svc> bash | Войти в контейнер сервиса |
| docker compose pull | Обновить образы |
| docker compose build | Пересобрать образы |
| docker compose restart <svc> | Перезапустить сервис |
| docker compose config | Проверить итоговую конфигурацию |
| docker compose top | Процессы внутри контейнеров |
Лучшие практики
Один процесс — один контейнер
Не запускайте nginx, php-fpm и postgresql в одном контейнере. Каждый сервис — отдельный контейнер. Это упрощает масштабирование, обновление и отладку.
Используйте .dockerignore
Аналог .gitignore для Docker. Исключайте node_modules, .git, .env, временные файлы — это ускорит сборку и не даст секретам попасть в образ.
node_modules
.git
.env
*.log
__pycache__ Не запускайте процессы от root
В Dockerfile создайте отдельного пользователя для большей безопасности:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser Фиксируйте версии образов
Вместо nginx:latest пишите nginx:1.25.3. Это обеспечивает воспроизводимость сборки и защищает от неожиданных изменений.
Multi-stage builds для минимизации размера
Это позволяет использовать полное окружение для сборки, но итоговый образ содержит только то, что нужно для запуска:
# Stage 1: Сборка
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Продакшн образ
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/main.js"] Healthcheck для критических сервисов
Без healthcheck Compose считает контейнер готовым сразу после старта. С базами данных это проблема: PostgreSQL может быть запущен, но ещё не готов принимать подключения.
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5 Заключение
Docker решает проблему «работает только у меня», изолируя приложение со всеми его зависимостями в контейнер. Контейнеры легковесны, быстры и переносимы: они одинаково работают на ноутбуке разработчика, тестовом сервере и в облаке.
Docker Compose решает проблему управления несколькими контейнерами: весь стек описывается в одном YAML-файле и поднимается одной командой. Это стандарт де-факто для локальной разработки, CI/CD и небольших продакшн-окружений.
Вместе они формируют базу современного DevOps-подхода — infrastructure as code, воспроизводимые среды и быстрый цикл разработки.








