сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Docker и Kubernetes

Docker для приложения

Docker для Python-приложения

Docker убирает «у меня работало», но только если ты понимаешь, как устроено кэширование слоёв и почему секреты нельзя зашивать в образ. Собес проверяет именно эти инженерные детали - по ним видно, писал ли человек Dockerfile для прода или копировал из туториала.

Типовые формулировки: «зачем COPY requirements отдельно от кода?», «что даёт multi-stage build?», «как прокинуть секреты в контейнер».

Образ и кэш слоёв

Образ фиксирует код, зависимости, версию Python и системные библиотеки в ОДНОМ артефакте - одинаковом на ноутбуке, в CI и на проде. Это и лечит «у меня работало»: среда едет вместе с кодом.

Ключевой приём Dockerfile - порядок слоёв: сначала COPY requirements и install, и только ПОТОМ COPY кода. Тогда тяжёлый слой с зависимостями берётся из кэша, пока requirements не менялся, и пересобирается лишь при смене зависимостей. Скопируешь код до install - любая правка исходника инвалидирует слой зависимостей, и pip install гоняется на каждую мелкую правку.

// .dockerignore держит лишнее (венвы, .git, кэши) вне контекста сборки - меньше контекст, быстрее и чище образ.

COPY requirements.txt .
RUN pip install -r requirements.txt   # слой кэшируется
COPY . .   # правки кода не ломают слой выше
образ
код + зависимости + рантайм в фиксированном виде
кэш слоёв
неизменные инструкции берутся из кэша при пересборке

Multi-stage и секреты снаружи

Multi-stage build отделяет стадию сборки (компиляторы, кэш pip, dev-инструменты) от финального образа, где остаётся только рантайм. Итог - меньше размер и меньше поверхность атаки: в проде не лежат компиляторы, которых там быть не должно. Тащить билд-инструменты в финальный образ вместо multi-stage - раздувать его и множить риски.

Секреты и конфиг среды в образ НЕ зашивают: образ один на все среды, и зашитый секрет утечёт вместе с ним. Их прокидывают переменными окружения при запуске (или через секрет-менеджер) - тогда один и тот же образ безопасно едет в dev, stage и prod.

// Правило 12-factor тут и проявляется: артефакт неизменен, различия сред задаются снаружи конфигом, а не пересборкой.

multi-stage
отделить сборку от тонкого рантайм-образа
.dockerignore
исключить лишнее из контекста сборки

Как отвечать: «Зачем COPY requirements отдельно, до COPY кода?»

Ради кэша слоёв. Docker кэширует каждый слой и переиспользует его, пока инструкция и её входные данные не изменились. Если я сначала копирую requirements и ставлю зависимости, а код копирую отдельным шагом после, то тяжёлый слой с pip install остаётся в кэше при любой правке исходника - пересобирается только когда реально поменялся requirements. А если скопировать весь код до install одной командой, то любое изменение файла инвалидирует слой, и pip install прогоняется заново каждый раз - сборки становятся мучительно долгими. То есть это про то, чтобы дорогой шаг зависел только от того, что редко меняется.

Объяснён механизм кэша, показан контраст обоих порядков и назван эффект на скорость сборки - практическая деталь, которую знают только те, кто реально оптимизировал образы.

На чём валят

  • COPY кода до install: любая правка исходника инвалидирует слой зависимостей - install каждый раз.
  • Зашивать секреты в образ: они утекут вместе с ним и ломают «один образ на все среды».
  • Тащить билд-инструменты в финальный образ вместо multi-stage - размер и поверхность атаки.
  • Забыть .dockerignore: в контекст сборки уезжают венвы, .git и кэши - медленно и грязно.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.

  1. #docker_pyapp1 / 5
    Почему в Dockerfile сначала COPY requirements.txt и install, а COPY кода — позже?
    A)Слой с зависимостями кэшируется, пока requirements не менялся
    B)Python требует, чтобы зависимости лежали строго выше исходников
    C)Это уменьшает итоговый размер образа за счёт объединения слоёв
    D)Иначе pip не найдёт файл requirements и установка просто упадёт
    показать ответ и разбор
    +A)Слой с зависимостями кэшируется, пока requirements не менялся

    // разбор: Docker кэширует слои: если requirements.txt не изменился, слой с pip install берётся из кэша, а переустанавливается только при правке зависимостей. Скопируй код раньше — любое изменение исходника инвалидирует последующий install и пересобирает зависимости каждый раз. Порядок инструкций = скорость пересборки.

  2. #docker_pyapp2 / 5
    Зачем для Python-сервиса multi-stage build?
    A)Чтобы запускать несколько версий Python в одном образе параллельно
    B)Собрать зависимости в одном слое, а в финальный взять только нужное
    C)Это обязательный синтаксис — без него Dockerfile не соберётся
    D)Чтобы каждый этап сборки работал в отдельном запущенном контейнере
    показать ответ и разбор
    +B)Собрать зависимости в одном слое, а в финальный взять только нужное

    // разбор: Multi-stage build отделяет этап сборки (компиляторы, dev-заголовки, кэш pip) от финального образа, куда копируют лишь готовые артефакты и рантайм-зависимости. Итог — образ меньше и с меньшей поверхностью атаки: билд-инструментов в проде нет. Особенно заметно, когда есть пакеты с C-расширениями.

  3. #docker_pyapp3 / 5
    Что не стоит зашивать внутрь Docker-образа приложения?
    A)Сам код приложения — его правильнее монтировать томом на проде
    B)Файл requirements.txt со списком зависимостей проекта
    C)Точку входа (CMD), запускающую сервер приложения при старте
    D)Секреты и конфиг среды — их прокидывают при запуске
    показать ответ и разбор
    +D)Секреты и конфиг среды — их прокидывают при запуске

    // разбор: Секреты (пароли БД, API-ключи) и специфичный для среды конфиг не зашивают в образ: образ версионируется и раздаётся, а вместе с ним утекут и секреты, к тому же один образ должен работать на всех средах. Их прокидывают переменными окружения или секрет-менеджером при запуске. .dockerignore держит лишнее вне контекста сборки.

  4. #docker_pyapp4 / 5
    Почему в Dockerfile COPY requirements и install ставят ДО COPY кода приложения?
    A)Слой с зависимостями кешируется и не пересобирается при каждой правке кода
    B)Это уменьшает итоговый размер образа за счёт сжатия слоя с зависимостями
    C)Иначе pip не найдёт файл requirements и установка зависимостей завершится ошибкой
    D)Так требует синтаксис Dockerfile: install обязан идти раньше COPY исходников
    показать ответ и разбор
    +A)Слой с зависимостями кешируется и не пересобирается при каждой правке кода

    // разбор: Docker кеширует слои: слой пересобирается, только если изменились его входные данные. Скопировав сначала requirements и установив зависимости, а КОД — позже, вы делаете так, что правка исходника инвалидирует лишь верхние дешёвые слои, а тяжёлый install берётся из кеша, пока requirements не менялся. Обратный порядок пересобирает зависимости на каждую правку кода.

  5. #docker_pyapp5 / 5
    Что фиксирует Docker-образ приложения?
    A)Настройки конкретной среды (адреса, ключи), вшитые в образ на этапе его сборки
    B)Только исходный код приложения, а зависимости докачиваются при каждом запуске контейнера
    C)Код, зависимости, версию Python и системные библиотеки в одном артефакте
    D)Работающий процесс вместе с его текущим состоянием оперативной памяти на момент сборки
    показать ответ и разбор
    +C)Код, зависимости, версию Python и системные библиотеки в одном артефакте

    // разбор: Образ упаковывает всё нужное для запуска: код, зависимости, версию интерпретатора, системные библиотеки — в один неизменяемый артефакт. Один и тот же образ одинаково работает на ноутбуке, в CI и на проде — лечит «у меня работало». Конфиг среды в образ не зашивают (иначе образ перестанет быть единым для всех сред) — его передают переменными окружения при запуске.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.