Баннер мобильный (3) Пройти тест

Практичный гайд по Docker: как работают и зачем нужны контейнеры

Что сделать, чтобы ваш код работал отлично в любом окружении

Разбор

21 октября 2026

Поделиться

Скопировано
Практичный гайд по Docker: как работают и зачем нужны контейнеры

Содержание

    Если вы занимаетесь машинным обучением, анализом данных или просто пишете код, то рано или поздно вы столкнетесь с моментом, когда ваш идеально работающий скрипт или обученная модель откажутся запускаться на компьютере коллеги или на боевом сервере. 

    «Но у меня локально все работало!» — это, пожалуй, самая частая фраза в IT. Разные версии Python, конфликтующие библиотеки CUDA для нейросетей, странные системные зависимости — все это превращает перенос кода в настоящий ад.

    Сегодня мы разберем инструмент, который навсегда решает эту проблему, — Docker. Я постараюсь объяснить все максимально простым языком, без лишней академической духоты, но с примерами из ML-практики. 

    Что такое Docker — контейнеризация простыми словами

    Давайте начнем с аналогии из реальной жизни. Представьте себе морские грузоперевозки до 1950-х годов. Люди грузили товары в мешках, бочках и ящиках разных размеров. Погрузка корабля занимала недели, товары ломались, а перегрузить ящики с корабля на поезд было целым квестом. Потом придумали стандартный морской контейнер. Железная коробка фиксированного размера. Теперь крану, кораблю и грузовику абсолютно все равно, что внутри, — дорогие автомобили, бананы или китайские игрушки. Они просто берут контейнер и везут его.

    Docker — это то же самое, но для программного обеспечения. Вы кладете в контейнер свой Python-код, веса ML-модели, нужную версию операционной системы (например, Ubuntu 22.04) и все библиотеки. Docker-движок (тот самый «кран») запускает этот контейнер на любом сервере.

    Чем контейнеры отличаются от виртуальных машин (VM)

    Новички часто спрашивают: «А зачем нужен Docker, если я могу просто создать виртуальную машину через VirtualBox?».

    Представьте, что вы хотите поселить трех разных разработчиков.

    • Виртуальная машина (VM) — это постройка трех отдельных домов. Для каждого дома нужен свой фундамент, свои стены, своя крыша, свой водопровод (своя полноценная операционная система со своим ядром). Это занимает много места на диске (гигабайты) и долго запускается (минуты).
    • Контейнер Docker — это многоквартирный дом. Фундамент и крыша (ядро операционной системы вашего компьютера) у них общие. Но квартиры (контейнеры) изолированы друг от друга. Вы не слышите соседей, у вас свои обои и мебель. Контейнеры весят мегабайты и запускаются за миллисекунды.

    Давайте посмотрим на архитектурную разницу на схеме:

    Как работают виртуальные машины и Docker: сравнительная таблица

    Какие проблемы решает Docker для ML-инженера

    1. «У меня локально работало»: теперь локально и на сервере работает абсолютно идентичная среда.
    2. Разъезжающиеся окружения: вам больше не нужно бояться, что обновление системного Python сломает ваши старые проекты. Каждый проект живет в своем контейнере со своим Python.
    3. Долгий онбординг: приходит новый джун. Вместо того чтобы дать ему инструкцию на 10 страниц («установи это, пропиши пути в PATH, скачай эту версию C++ компилятора»), вы говорите ему одну команду: docker-compose up. И через 5 минут у него на компьютере развернута база данных, очередь задач и ваша модель.

    Как устроен Docker — ключевые понятия

    Чтобы уверенно пользоваться Докером, нужно понимать его терминологию. Понятий не так много, но они фундаментальны.

    1. Образ (Image). Это «чертеж» или «рецепт» вашего приложения. Образ не изменяется. В нем записано: «Возьми Linux, установи Python 3.9, скопируй папку с кодом, установи библиотеки из requirements.txt». В терминах ML образ можно сравнить с архитектурой нейросети и замороженными весами.
    2. Контейнер (Container). Это запущенный экземпляр образа. Если образ — это рецепт борща, то контейнер — это сам борщ, который вы прямо сейчас едите. Из одного образа (рецепта) можно запустить сколько угодно контейнеров (сварить сколько угодно кастрюль супа).
    3. Слои (Layers). Образы состоят из слоев. Каждый шаг в рецепте создает новый слой. Если вы изменили только свой код (последний слой), Docker не будет заново скачивать Linux и устанавливать библиотеки — он переиспользует старые слои из кэша. Это делает сборку очень быстрой.
    4. Dockerfile. Это текстовый файл с инструкциями, как именно собрать ваш образ.
    5. Реестр (Registry / Docker Hub). Это как GitHub, но для Docker-образов. Вы можете скачать готовый образ с установленным PyTorch и Jupyter (docker pull jupyter/scipy-notebook), чтобы не настраивать все с нуля.
    6. Тома (Volumes). Контейнеры эфемерны. Если контейнер удалить, все данные внутри него исчезнут. Тома — это способ сохранить данные (например, логи, базу данных или сохраненные .pt веса модели) на жестком диске вашего компьютера, независимо от жизни контейнера.
    7. Сети (Networks). Они позволяют контейнерам общаться друг с другом. Например, контейнер с вашим Python-API должен иметь доступ к контейнеру с базой данных PostgreSQL.

    Client и Daemon: что происходит при docker run

    Docker имеет клиент-серверную архитектуру. Когда вы пишете команду в терминале, вы общаетесь с Docker Client. Клиент отправляет запрос Docker Daemon (фоновому процессу, который делает всю тяжелую работу).

    Вот что происходит, когда вы командуете запустить приложение:

    Схема работы Docker Daemon

    Если образ уже есть на вашем компьютере, четвертый шаг пропускается, и контейнер стартует за доли секунды.

    Установка Docker и первые команды

    Установка

    • Linux. Устанавливается через пакетный менеджер (apt/yum). Работает нативно и идеально.
    • macOS. Установите Docker Desktop. Он создаст легковесную виртуальную машину под капотом, так как Docker требует ядро Linux.
    • Windows. Раньше это было болью, но теперь есть WSL2 (Windows Subsystem for Linux). Обязательно установите WSL2, а затем Docker Desktop. В этом варианте нужно указать в настройках использование WSL2. Это даст вам почти нативную скорость работы и, что критично для ML, позволит пробрасывать видеокарту (GPU) внутрь контейнеров!

    Проверка установки

    Откройте терминал и введите:

    docker run hello-world

    Если вы видите сообщение: «Hello from Docker!», это значит, что все настроено верно.

    Базовый набор команд Docker (Шпаргалка)

    Давайте разберем команды, которые вы будете использовать каждый день.

    1. Команда docker pull <имя_образа> скачивает образ из интернета. Пример: docker pull python:3.10-slim (скачает легкую версию Python 3.10).
    2. Команда docker images показывает список всех скачанных образов на вашем компьютере и их размер. (ML-образы с CUDA могут весить по 5–10 ГБ, так что следите за свободным местом).
    3. Команда docker run <флаги> <имя_образа> создает и запускает контейнер.
    • Флаг -d (detached): запускает контейнер в фоновом режиме, освобождая терминал.
    • Флаг -p <порт_хоста>:<порт_контейнера>: проброс портов. Пример: docker run -d -p 8080:80 nginx. Запустит веб-сервер. Если вы перейдете в браузере на localhost:8080, вы увидите стартовую страницу Nginx.
    1. Команда docker ps показывает список работающих контейнеров. Добавьте флаг -a (docker ps -a), чтобы увидеть вообще все контейнеры, включая остановленные. Вы увидите уникальный CONTAINER ID для каждого.
    2. Команда docker logs <ID_контейнера> выводит логи приложения. Незаменимо для отладки, если ваша модель упала с ошибкой. Добавьте -f (follow), чтобы читать логи в реальном времени.
    3. Команда docker exec -it <ID_контейнера> bash — это магия! Команда позволяет «зайти» внутрь работающего контейнера и открыть там терминал. Полезно, чтобы проверить, лежат ли файлы на своих местах. (Если bash не работает, попробуйте sh.)
    4. Команда docker stop <ID_контейнера> мягко останавливает работающий контейнер.
    5. Команда docker rm <ID_контейнера> удаляет остановленный контейнер (образ при этом остается на диске).

    Практика: собираем свой образ в Docker

    Хватит теории, давайте упакуем реальное приложение! Представим, что мы обучили простую модель и написали для нее API на Python с использованием Flask.

    Создайте на компьютере папку my-ml-app и положите туда три файла:

    1. app.py (Наш код)
    
    from flask import Flask, jsonify
    
    app = Flask(__name__)
    
    @app.route('/predict', methods=['GET'])
    def predict():
        # Имитация работы ML-модели
        result = {"prediction": 42, "status": "success"}
        return jsonify(result)
    
    if __name__ == '__main__':
        # Слушаем на всех интерфейсах (0.0.0.0), порт 5000
        app.run(host='0.0.0.0', port=5000)
    
    2. requirements.txt (Наши зависимости)
    
    Flask==2.3.2
    
    3. Dockerfile (Без расширения, с заглавной буквы. Это наш рецепт)
    
    # 1. Берем за основу официальный образ Python (slim - значит облегченный)
    FROM python:3.10-slim
    
    # 2. Устанавливаем рабочую директорию внутри контейнера
    WORKDIR /app
    
    # 3. Копируем ТОЛЬКО файл с зависимостями
    COPY requirements.txt .
    
    # 4. Устанавливаем библиотеки
    RUN pip install --no-cache-dir -r requirements.txt
    
    # 5. Копируем весь остальной код нашего приложения
    COPY . .
    
    # 6. Указываем, какой порт слушает приложение (для документации)
    EXPOSE 5000
    
    # 7. Команда, которая выполнится при старте контейнера
    CMD ["python", "app.py"]

    Зачем нужен .dockerignore? Рядом с Dockerfile всегда создавайте файл .dockerignore (работает как .gitignore). Впишите туда venv/, __pycache__/, *.ipynb. Иначе Docker скопирует вашу локальную виртуальную среду и мусорные файлы внутрь контейнера, раздув его размер.

    Сборка и запуск

    Открываем терминал в папке с проектом и собираем образ:

    docker build -t my-ml-api:v1 .

    Разбор: 

    • -t my-ml-api:v1 — мы даем образу имя (тег). v1 — это версия. 
    • . (точка в конце) — указывает, что Dockerfile лежит в текущей папке.

    Дождитесь окончания сборки. Теперь запустим наше приложение:

    docker run -d -p 8000:5000 my-ml-api:v1

    Мы сказали Docker: «Запусти это в фоне (-d) и свяжи порт 8000 моего компьютера с портом 5000 внутри контейнера».

    Откройте браузер и перейдите по адресу: http://localhost:8000/predict. Вы увидите JSON: {«prediction»: 42, «status»: «success»}. Поздравляю! Вы только что контейнеризировали приложение.

    Магия кэширования слоев

    Обратите внимание на шаги 3 и 5 в Dockerfile. Почему мы сначала копируем requirements.txt, устанавливаем зависимости и только потом копируем весь код COPY . .? Если вы измените код в app.py и запустите docker build снова, Docker увидит, что requirements.txt не изменился. Он мгновенно достанет из кэша слои установки библиотек (а установка PyTorch или TensorFlow может занимать 10 минут!) и выполнит только копирование нового кода. Это экономит много времени при разработке.

    Данные, сети и конфигурация в Docker

    Куда пропадают данные? Тома и Bind Mounts

    Контейнеры не хранят состояние. Представьте, что ваш скрипт скачивает датасет и сохраняет его в контейнере. Вы остановили контейнер, удалили его, запустили новый — датасета нет.

    Чтобы данные жили дольше контейнера, используют два подхода:

    1. Volumes (Тома). Docker сам создает скрытую папку на вашем компьютере и управляет ей. Идеально для баз данных (PostgreSQL, MongoDB).
    2. Bind Mounts (Проброс папок). Вы жестко связываете папку на вашем ПК с папкой в контейнере. Это суперполезно для ML-разработки!

    Пример Bind Mount:

    docker run -d -p 8000:5000 -v $(pwd):/app my-ml-api:v1

    Флаг -v $(pwd):/app означает: «Возьми мою текущую папку на macOS/Windows и примонтируй ее в /app внутри контейнера». Теперь, если вы измените код в app.py у себя в редакторе, изменения мгновенно появятся внутри контейнера! Не нужно пересобирать образ каждый раз при отладке.

    Сети и переменные окружения

    Часто приложению нужны секреты (API-ключи, пароли). Их нельзя вшивать в код или Dockerfile. Для этого используют переменные окружения (Environment Variables). Запуск с передачей ключа:

    docker run -e OPENAI_API_KEY="sk-12345" my-ml-api:v1

    Внутри Python вы просто вызовете os.getenv(«OPENAI_API_KEY»). Если ключей много, можно создать файл .env и передать его через флаг —env-file .env.

    Коротко о Docker Compose

    В реальном мире ваше ML-приложение редко состоит из одного контейнера. Обычно это:

    1. FastAPI-приложение (модель).
    2. PostgreSQL (база данных для логов).
    3. Redis (кэш).

    Запускать каждый из них вручную через docker run, прописывать им общую сеть, чтобы они видели друг друга по именам — это мучение. Для этого придумали Docker Compose. Вы описываете всю инфраструктуру в одном файле docker-compose.yml, и запускаете все одной командой: docker-compose up -d. 

    Docker: лучшие практики и частые ошибки

    Чтобы ваш опыт с Docker был позитивным, а коллеги на код-ревью не кидали в вас помидоры, запомните несколько правил хорошего тона (особенно актуально для Data Science, где образы склонны раздуваться до неприличных размеров):

    1. Следите за порядком инструкций для кэша. Мы это уже обсуждали. Сначала копируем зависимости, устанавливаем их и только в самом конце копируем исходный код.
    2. Используйте Multi-stage build. Если для сборки вашей C++ ML-библиотеки нужны тяжелые компиляторы (gcc, cmake), используйте один базовый образ для сборки, а скомпилированный бинарный файл копируйте в чистый, легкий образ. Это уменьшит размер финального контейнера с 3 ГБ до 100 МБ.
    3. Откажитесь от тега :latest — никогда не пишите FROM python:latest или FROM pytorch/pytorch:latest. Завтра выйдет новая версия Python, и ваш код сломается, потому что latest указывает на новую версию. Всегда фиксируйте версии жестко: python:3.10.12-slim. Это гарантия воспроизводимости — святого грааля машинного обучения.
    4. Не запускайте процессы от имени root — по умолчанию приложения в контейнере работают с правами суперпользователя. С точки зрения безопасности серверов это плохо. В конце Dockerfile создавайте обычного пользователя и переключайтесь на него инструкцией USER appuser.
    5. Регулярно проводите уборку — docker любит «съедать» место на диске старыми образами и остановленными контейнерами. Раз в месяц запускайте команду:
    • docker system prune -a
    • Она удалит весь неиспользуемый мусор и освободит вам десятки гигабайт.

    Куда двигаться дальше? Docker — это лишь первый шаг в мир MLOps (Machine Learning Operations). Освоив его, вы заложите фундамент для изучения Docker Compose (оркестрация нескольких сервисов на одном ПК) и Kubernetes (управление тысячами контейнеров на кластерах серверов).

    Начните с малого: возьмите свой текущий пет-проект, напишите для него Dockerfile и попробуйте запустить. Уверен, после первого успешного запуска вы больше никогда не захотите возвращаться к ручной настройке окружений.

    Удачной контейнеризации и стабильных вам деплоев! 

    Разбор

    Поделиться

    Скопировано
    0 комментариев
    Комментарии