> Как сконфигурировать Docker и Docker Compose для Django приложения (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: Стилсофт

Стек: Python, Docker

> Пример ответа

Короткий ответ

Для Django-приложения нужен многоступенчатый Dockerfile: базовый образ для зависимостей, слой для сборки статики и финальный runtime-образ. Docker Compose описывает сервисы web, db (PostgreSQL) и, при необходимости, redis, nginx. Ключевые моменты: использование .dockerignore, отдельный requirements-файл для production, healthcheck для БД, volume для кода и статики, а также передача настроек через environment variables, а не через settings.py.

Подробное объяснение

Конфигурация Docker для Django сводится к двум уровням: образ и оркестрация. Образ должен быть минимальным, безопасным и воспроизводимым. Для этого используется multi-stage build: на первом этапе ставятся все зависимости, включая build-инструменты, на втором - только runtime-пакеты. Это уменьшает размер образа и поверхность атаки.

Docker Compose решает задачу локальной разработки и production-деплоя. В development режиме код монтируется volume'ом, чтобы изменения применялись без пересборки. В production код копируется в образ, а volume используется только для статики и медиа. Отдельный сервис для БД обязателен, так как Django не должен работать с БД внутри контейнера приложения.

Важный trade-off: использовать python:3.12-slim как базовый образ - он меньше, но требует установки системных библиотек для psycopg2. Альтернатива - python:3.12-alpine, но он может вызвать проблемы с компиляцией некоторых пакетов. Для senior-уровня важно уметь обосновать выбор.

На практике

Начинаем с .dockerignore - исключаем .git, __pycache__, venv, staticfiles, media. Затем Dockerfile: сначала pip install --no-cache-dir -r requirements.txt, потом копируем код. Для production добавляем gunicorn как entrypoint. В Compose указываем depends_on с condition: service_healthy для БД, чтобы Django не стартовал раньше PostgreSQL.

Для миграций используем отдельную команду docker compose run --rm web python manage.py migrate, а не выполняем их в entrypoint - это позволяет контролировать порядок при деплое. Статику собираем через collectstatic в volume, который шарится с nginx.

Пример кода

# Dockerfile
FROM python:3.12-slim AS builder
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc libpq-dev && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends libpq && rm -rf /var/lib/apt/lists/*
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
COPY . .
EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]
YAML
# docker-compose.yml
version: "3.9"
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 5s
timeout: 5s
retries: 5
web:
build: .
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
env_file:
- .env
depends_on:
db:
condition: service_healthy
ports:
- "8000:8000"
nginx:
image: nginx:1.25-alpine
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- static_volume:/static
- media_volume:/media
ports:
- "80:80"
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:

Как отвечать на собеседовании

Начни с целей: воспроизводимость, изоляция, простота деплоя. Затем опиши структуру образа и почему multi-stage. Обязательно упомяни healthcheck для БД и порядок запуска. Покажи понимание разницы между dev и prod конфигурациями - это ключевой сигнал для senior. Расскажи про обработку статики через nginx и почему не стоит отдавать её через Django в production. Если спросят про безопасность - скажи про запуск от non-root пользователя и --no-cache-dir.

Что проверяет интервьюер

Проверяется понимание жизненного цикла контейнера, умение проектировать инфраструктуру, а не просто писать Dockerfile. Важно, чтобы кандидат объяснил, зачем нужен каждый слой, как решаются проблемы с зависимостями и как обеспечить отказоустойчивость. Также оценивается знание best practices: .dockerignore, healthcheck, volumes, environment variables. Senior должен уметь аргументировать выбор базового образа и стратегию обновления зависимостей.

Типичные ошибки

  • Запуск миграций в entrypoint - это приводит к гонкам при масштабировании.
  • Использование latest тегов для базовых образов - невоспроизводимо.
  • Хранение секретов в Dockerfile или Compose без env_file.
  • Отсутствие .dockerignore - образ раздувается.
  • Монтирование всего кода volume'ом в production - теряется изоляция.
  • Запуск Django dev-server вместо gunicorn в production.
  • Неправильная обработка статики: попытка отдавать её через Django или отсутствие volume для nginx.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью