Learning Platform
Глоссарий Troubleshooting
Урок 03.05 · 18 мин
Начальный
dockerdata-engineeringbest-practices

Когда использовать Docker

Контейнеры — это инструмент, а не серебряная пуля. Они отлично решают одни задачи и плохо подходят для других. В этом уроке посмотрим конкретные DE-сценарии, где Docker почти всегда правильный выбор, и пограничные случаи, где он становится anti-pattern’ом.

Цель урока — чтобы после него у тебя в голове был чек-лист: «если задача такая — берём Docker без вопросов; если такая — берём bare-metal или managed-сервис; если такая — берём k8s, не Docker напрямую».


DE-кейсы, где Docker — стандарт

Пять кейсов, где Docker почти всегда правильный выбор для DE
1. Воспроизводимая средаPostgres 16.4 точной версии, или конкретный Spark с конкретным Hadoop. Образ гарантирует одинаковую конфигурацию у всех в команде
2. Локальные DAG'иЗапустить Airflow на ноутбуке для разработки DAG'ов. compose up airflow — есть локальный Airflow с теми же провайдерами что в проде
3. Integration teststestcontainers-python поднимает Postgres/Kafka на лету в pytest. Тест видит реальную базу, после теста контейнер уходит
4. CI image как артефактGitHub Actions собирает образ ETL-джоба, пушит в GHCR, дальше Airflow KubernetesPodOperator его подхватывает. Один артефакт от commit до prod
5. Sandbox для экспериментовПопробовать Clickhouse не ставя его — docker run clickhouse, поиграть, docker rm -f. Никаких следов на хосте

Кейс 1: воспроизводимая среда

Команда разрабатывает ETL pipeline. Все используют Postgres 16.4. Без Docker это означало бы: кто-то на Mac ставит через brew, кто-то на Linux через apt, кто-то на WSL — и версии разные (16.2, 16.4, 16.5), config разный, locale разный. Через две недели появляется баг «у меня работает, у тебя нет».

С Docker:

docker run -d --name pg -p 5432:5432 \
  -e POSTGRES_PASSWORD=secret \
  postgres:16.4

У всех в команде — байт-в-байт одна и та же Postgres. У этого образа фиксированный SHA. Image pull с Docker Hub приносит ровно тот файл, что лежит на сервере.

Это и есть «воспроизводимая среда». Не для prod (там обычно managed Postgres от облака), а для dev и CI.


Кейс 2: локальная разработка DAG’ов Airflow / dbt

Airflow — сложный софт. Чтобы он работал, нужны Postgres (метастор), Redis или Celery (очередь), плюс сам Airflow webserver, scheduler, worker. Без Docker поднимать это локально — день работы и зоопарк конфигов.

С Docker Compose это compose up и через 30 секунд у тебя локальный Airflow на http://localhost:8080. С тем же Python (например, 3.12), теми же providers (например, apache-airflow-providers-postgres 5.x), теми же DAG-файлами, что и в проде.

То же для dbt: dbt-postgres контейнер с твоей dbt-моделью, тот же подключаемый Postgres, тот же seed. Локальный run в один шаг.


Свой systemd-сервис: пишем .service unit для Python ETL daemon

Кейс 3: integration tests через testcontainers

Тебе нужно проверить, что твой pipeline корректно пишет в Postgres. Варианты:

  • Mock базу. Тест проходит, но не доказывает ничего. На проде ловишь баг.
  • Shared CI Postgres. Тесты конфликтуют (один тест видит данные другого), флаки, страдания.
  • Контейнер на лету. testcontainers-python поднимает Postgres именно для этого теста. Test проходит, контейнер уходит, нет следов.

Пример:

import psycopg
from testcontainers.postgres import PostgresContainer

def test_etl_writes_correctly():
    with PostgresContainer("postgres:16.4") as pg:
        conn = psycopg.connect(pg.get_connection_url())
        # ... твой ETL код ...
        # ... проверка через SELECT ...

Каждый тест — свой контейнер. Параллельно тесты не пересекаются. На CI работает out of the box, потому что GitHub Actions runner — это уже Docker-совместимая среда.

Подробнее testcontainers будет в модуле 18.


Кейс 4: CI image как артефакт

Современный DE pipeline в CI выглядит так:

  1. Commit -> GitHub Actions.
  2. Actions собирает Docker image с твоим Python-кодом (ETL + зависимости).
  3. Image пушится в registry (GHCR, ECR, Harbor).
  4. В Airflow задача — это KubernetesPodOperator(image="ghcr.io/myorg/etl-job:v1.2.3").
  5. В нужное время k8s поднимает контейнер из этого image, запускает джоб.

Преимущество: один артефакт от коммита до prod. Версия Python зафиксирована в image, версии зависимостей зафиксированы в image, твой код зафиксирован в image. Деплой — это смена тэга image.

Это совсем не возможно без контейнеров. Без них пришлось бы либо ставить зависимости на каждый Airflow worker (тогда worker’ы становятся stateful), либо упаковывать всё в один большой Python wheel (хрупкое и сложно поддерживаемое).


Кейс 5: sandbox для экспериментов

Хочешь попробовать новую базу — Clickhouse, DuckDB, DragonflyDB? Без Docker — это часы на установку, потом разбор с конфигурацией, потом мучение с удалением. С Docker:

docker run --rm -it clickhouse/clickhouse-server
# поиграл, прервал Ctrl+C — всё ушло

Никаких следов на хосте. Идеально для «надо посмотреть, не подходит — забываем».


Когда Docker — anti-pattern

Случаи, где Docker — плохой выбор
Stateful prod без k8sProduction Postgres на одиночном Docker host без orchestrator. Когда host упадёт, данные могут пострадать, нет HA, нет автоматического восстановления
Сложный prod-стек на чистом Compose50 сервисов в одном compose.yaml без k8s — это потолок Compose. Не масштабируется горизонтально, нет автоматического failover
Multi-tenant chaoЗапуск чужого недоверенного кода на одном хосте. Контейнер изолирует слабее VM. Для multi-tenant нужны gVisor, Firecracker, или managed serverless
Heavy GPU workloadsML inference на больших GPU без специальной поддержки nvidia-container-toolkit. Часто проще выделить bare-metal или managed GPU-сервис
StatefulSet: ordered stateful workloads

Анти-кейс 1: stateful production без orchestrator

Распространённый ход для junior’а — поднял Postgres в Docker на VPS, и думает, что это prod. Проблемы:

  • Host upgrade. Когда VPS перезагружают для обновления ядра — контейнер исчезает. Если ты не настроил --restart=unless-stopped и volumes на правильный путь — данные могут потеряться.
  • Нет HA. Если VPS упал — Postgres недоступен, пока ты не перезапустишь руками.
  • Нет автоматического failover.
  • Бэкапы — вручную.

Production stateful система — это либо managed (RDS, Cloud SQL, Supabase, Neon), либо kubernetes с StatefulSet и оператором (CrunchyData PGO, Zalando postgres-operator). Чистый Docker — это локальная разработка, а не prod.

Анти-кейс 2: огромный compose-стенд в проде

Compose отлично работает для 5-15 сервисов на одной машине. Для 50 сервисов на 10 машинах — это уже Kubernetes.

В мире DE граница такая: один dev-стенд для команды — Compose ок. Production стек, в который попадает реальный трафик клиентов — k8s или managed-сервисы.

Анти-кейс 3: multi-tenant изоляция

Если ты хостишь чужой код (CI для public-репозиториев, serverless для внешних пользователей, sandbox для студентов) — простой Docker недостаточно. Контейнерная изоляция слабее VM, container escape через kernel-уязвимости возможен. Для multi-tenant нужны:

  • gVisor — sandbox-runtime, который перехватывает syscalls.
  • Kata Containers — лёгкие VM вместо контейнеров.
  • Firecracker — то, что использует AWS Lambda и Fargate.

Junior DE редко это пишет, но знать стоит.

Анти-кейс 4: GPU без подготовки

ML inference на GPU в контейнере — это рабочая история, но требует nvidia-container-toolkit, правильных образов от NVIDIA, и понимания, как драйвер шарится между host и контейнером. Для серьёзной ML-инфраструктуры — обычно k8s + GPU operator. Для одиночной задачи на одной машине — иногда проще запустить bare-metal или взять managed-сервис (SageMaker, Vertex AI).


Чек-лист «Docker или нет»

Когда смотришь на новую задачу:

  1. Это локально / dev / CI? -> Почти всегда Docker. Compose для multi-service сценариев.
  2. Это production stateless (HTTP API, ETL worker)? -> Docker, но обычно через k8s или managed (ECS, Cloud Run, Fly.io).
  3. Это production stateful (база, очередь)? -> Не чистый Docker. Managed-сервис или k8s + operator.
  4. Это запуск чужого недоверенного кода? -> Не чистый Docker. gVisor, Firecracker, или managed serverless.
  5. Это интеграционный тест в CI? -> testcontainers. Это и есть Docker, но в удобной обёртке.

Попробуй сам

Если у тебя установлен Docker, сделай мини-эксперимент с воспроизводимостью:

docker run --rm postgres:16.4 postgres --version
docker run --rm postgres:16.5 postgres --version

Вторая команда сначала скачает образ 16.5, потом выведет точную версию. Обрати внимание: версия точно та, что в тэге. Никаких «приблизительно 16».

Дальше можно глянуть, как образ зафиксирован по digest:

docker inspect postgres:16.4 --format='{{index .RepoDigests 0}}'

Вывод будет вроде postgres@sha256:abc123.... Этот SHA — это контентный хэш всего образа. Если бы Docker Hub попытался незаметно подменить файлы, SHA бы изменился, и ты бы это заметил. Это и есть основа воспроизводимости.

Прибери (если ничего не оставалось):

docker image rm postgres:16.4 postgres:16.5

Что дальше

В следующем модуле — установка Docker и обзор альтернатив (Docker Engine vs Desktop vs OrbStack vs Rancher vs Podman). После того, как у тебя на машине что-то рабочее — переходим к первым контейнерам.


Проверка знанийKnowledge check
Назови два-три DE-кейса, где Docker — стандарт, и один кейс, где Docker — anti-pattern. Почему так?
ОтветAnswer
Docker — стандарт для как минимум трёх DE-кейсов. Первый — воспроизводимая среда: вся команда использует ровно одну и ту же Postgres 16.4 (точный SHA образа), а не разные версии на mac/linux/wsl. Второй — локальная разработка DAG'ов Airflow или dbt: compose up поднимает Airflow + Postgres + Redis за 30 секунд, с теми же провайдерами, что в проде. Третий — integration tests через testcontainers-python: pytest поднимает чистый Postgres на лету, после теста контейнер уезжает, без следов и без флаков. Anti-pattern — это stateful production без orchestrator: Postgres в Docker на одиночном VPS как production-база. Когда VPS перезагружают для kernel update, контейнер исчезает; нет HA, нет автоматического failover, бэкапы вручную. Production stateful — это либо managed (RDS, Cloud SQL), либо k8s + StatefulSet + operator. Чистый Docker — это инструмент для dev и CI, а не для prod stateful.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Какой из этих DE-кейсов является ОБРАЗЦОВЫМ применением Docker?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5