Можно ли устанавливать зависимости внутри Docker-контейнера?

Ответ

Да, установка зависимостей внутри контейнера — это стандартная и рекомендуемая практика. Она обеспечивает изоляцию среды, воспроизводимость сборки и независимость от хостовой системы.

Правильный подход через Dockerfile:

FROM python:3.9-slim

# Копируем файл зависимостей отдельно для кэширования слоя
COPY requirements.txt /tmp/
RUN pip install --no-cache-dir -r /tmp/requirements.txt

# Затем копируем остальной код приложения
COPY . /app
WORKDIR /app

Ключевые принципы:

  1. Фиксация версий: В requirements.txt должны быть зафиксированы конкретные версии пакетов (например, Django==4.2.0).
  2. Кэширование слоев: Отдельное копирование requirements.txt и выполнение RUN pip install до копирования всего кода позволяет Docker кэшировать слой с зависимостями. Это значительно ускоряет последующие сборки, если зависимости не менялись.
  3. Многоэтапная сборка (Multi-stage): Для production-образов используйте multi-stage builds, чтобы уменьшить итоговый размер образа, оставив в финальном образе только необходимые артефакты и зависимости времени выполнения.

Пример requirements.txt для воспроизводимости:

Django==4.2.0
djangorestframework==3.14.0
psycopg2-binary==2.9.6

Ответ 18+ 🔞

А, ну это же классика, ёпта! Сейчас объясню, как тут всё устроено, чтобы потом не орать «какого хуя?» на проде.

Смотри, если ты копируешь зависимости с хоста в контейнер через -v, то это пиздец какой распиздяйский подход. Представь: у тебя на локальной машине стоит Django 4.2, а в продакшене — 3.2. Контейнер возьмёт твои локальные пакеты, запустится, а потом начнёт сыпаться ошибками, потому что API поменялось. И ты будешь сидеть и чесать репу: «Ну я же на своей машине всё проверил, ёб твою мать!». А потому что доверия к такой схеме — ноль ебать.

Правильный путь — делать всё внутри Dockerfile, вот так:

FROM python:3.9-slim

# Сначала тащим файлик с зависимостями — это для хитрого кэширования
COPY requirements.txt /tmp/
RUN pip install --no-cache-dir -r /tmp/requirements.txt

# А уж потом весь наш код закидываем
COPY . /app
WORKDIR /app

Почему это работает, как швейцарские часы:

  1. Версии — святое. В requirements.txt ты пишешь чётко: Django==4.2.0, а не просто Django. Иначе сегодня соберётся с одной версией, а завтра — уже с другой, и будет тебе хиросима. Пизда рулю твоему продакшену.
  2. Кэш слоёв — твой лучший друг. Докер умный, он смотрит: ага, requirements.txt не менялся с прошлой сборки. Значит, можно не качать и не ставить все эти пакеты заново, а взять готовый слой из кэша. Экономия времени — овердохуища. Если же скопировать весь код сразу, то любое изменение в нём заставит переустанавливать зависимости каждый раз. Зачем тебе этот мазохизм?
  3. Multi-stage для прода — это айс. Если хочешь, чтобы образ весил не как хуй с горы, а компактно, гугли «multi-stage build». Суть в том, что в одном контейнере ты всё собираешь и компилируешь, а в финальный образ копируешь только готовые артефакты и рантайм-зависимости. Итоговый образ — легковесный и безопасный.

Вот как должен выглядеть твой requirements.txt, чтобы не было сюрпризов:

Django==4.2.0
djangorestframework==3.14.0
psycopg2-binary==2.9.6

Короче, запомни: зависимости живут внутри образа, а не примазываются к нему сбоку. Собрал образ — и он самодостаточный кусок железа, который будет работать везде одинаково. Иначе это не контейнеризация, а одна большая мудя.