Полное пошаговое руководство по созданию образа Docker (GPU или CPU) вместе с объяснением всех передовых методов, которым следует следовать, которые будут использоваться для обслуживания любого программного обеспечения на основе машинного обучения.

Создание образа Docker обычно считается тривиальным по сравнению с разработкой других компонентов системы машинного обучения, таких как конвейер данных, обучение модели, обслуживание инфраструктуры и т. Д. Но неэффективный, громоздкий образ докера может значительно снизить производительность и даже может привести к снижению производительности инфраструктуры обслуживания.

0. Отказ от ответственности:

Этот блог направлен на создание идеального образа Docker, а не на его концепцию или преимущества. Я предполагаю, что у вас есть базовые знания по нескольким темам, связанным с Docker:

  • Общая работа Docker
  • Основы сборки Docker, запуск
  • Написание и синтаксис Dockerfile

1. Общие рекомендации по сборке Docker

Существует довольно много очень хороших источников для общих рекомендаций, таких как официальное руководство по докерам, но я хотел бы сохранить это краткое и актуальное для проекта на основе системы машинного обучения.

  • Requirements.txt всегда должен иметь версию пакета Python. Никогда не пишите просто имя пакета, так как он всегда будет устанавливать последний пакет и в процессе полностью лишает смысла использование docker.

  • Всегда группируйте похожую команду RUN, что приведет к единому слою Docker. (Я избегу соблазна объяснить это, поскольку это немного выходит за рамки)

eg:

RUN apt update && \
apt install --no-install-recommends -y build-essential gcc curl      ca-certificates python3 && \
apt clean && rm -rf /var/lib/apt/lists/*
  • Используйте флаг ‘- -no-cache-dir’ команды pip, поскольку целевая среда является производственной.
RUN pip install --no-cache-dir --user -r /req.txt
  • Используйте .dockerignore, чтобы избежать ненужного контекста сборки. Это работает точно так же, как .gitignore
  • По возможности используйте тонкую версию базового образа, например python: buster-slim, debian: buster-slim и т. Д.
  • Избегайте использования базового образа Docker на базе Alpine. Это может быть немного спорно, но поверьте мне, они плохо работают с Python. Обратитесь к этому прекрасному блогу Itamar Turner-Trauring.

2. Создание образа Docker для любого проекта Python (ЦП):

В большинстве случаев система машинного обучения будет основана на Python, поэтому очень важно эффективно создавать любой образ Docker на основе Python. Давайте пройдемся по ним.

2.1 Одноступенчатый

  • Одноступенчатый будет выполнять все задачи за одно и то же время сборки докера.
  • Последовательность действий состоит в том, чтобы выбрать базовый образ, установить пакеты ОС, скопировать исходный код, установить пакеты, установить точку входа (если требуется) или другие команды.

В демонстрационных целях я использую следующие пакеты:

После запуска команды docker build размер образа докера составил 1,64 ГБ.

  • Одноэтапный метод очень прост, может работать во многих случаях использования. Это неплохая практика, но у нее есть некоторые фундаментальные недостатки, особенно для проекта на основе Python.
  • Здесь использование '- -no-install-рекомендует' в apt и '- -no-cache-dir' в pip является ключевым, как я уже говорил ранее, мы не хотим хранить кеш, поскольку он не предназначен для разработки окружающая среда, но не для производства. Фактически, если вы используете любую платформу CI / CD (например, действие Github) с ограниченным пространством для хранения, она будет работать только с использованием этого метода.
  • Библиотека Python не работает из коробки, ее нужно сначала скомпилировать на C. Нам просто нужна скомпилированная часть любой библиотеки, а не все остальные остатки. Как вы можете видеть в одноступенчатом примере выше; при выполнении «pip install» все библиотеки сначала загружаются, а затем компилируются.
  • Мы должны удалить (и мы можем использовать команды bash) все промежуточные и оставшиеся компоненты, созданные при установке библиотек. Это доставит много хлопот и может даже сломать библиотеку, если будет сделано неправильно. Это настоящий прорыв, поэтому многие из нас просто избегают этого и внедряют громоздкий образ в производство. Но Docker Multi-stage приходит нам на помощь.

2.2 Многоступенчатая

  • Многоступенчатая сборка Docker на сегодняшний день является одним из наиболее эффективных методов оптимизации, при этом их легко читать и поддерживать. Чтобы написать действительно эффективный файл Dockerfile, вам традиционно приходилось использовать уловки оболочки и другую логику, чтобы слои были как можно меньше и чтобы каждый слой имел артефакты, необходимые для предыдущего слоя, и ничего больше.
  • При многоступенчатой ​​сборке вы используете несколько операторов FROM в своем Dockerfile. Каждая FROM инструкция может использовать различную базу, и каждая из них начинает новый этап сборки. Вы можете выборочно копировать артефакты с одного этапа на другой, оставляя все нежелательное в конечном изображении. Чтобы показать, как это работает, см. Пример.

Для сравнения: размер многоступенчатого образа докера составляет 1,61 ГБ, а размер одноступенчатого - 1,64 ГБ. Это улучшение (хотя оно и кажется небольшим), здесь происходит много вещей, что позволяет нам попытаться понять вкратце.

  • Строка 1–5 - это 1-й этап или этап компиляции, на котором мы устанавливаем библиотеки Python (которые сначала загружаются, а затем выполняются на языке C, поэтому мы даже установили gcc). Затем мы просто копируем скомпилированные библиотеки со стадии 1 на стадию 2 или стадию выполнения, используя синтаксис
COPY --from=<stage 1> stage1/src stage2/destination
  • Но, как видно из скриншота, особых улучшений мы не видим. Мы, безусловно, увидим огромное улучшение в других языках, но у Python есть несколько хитростей,

→ Многие библиотеки теперь поставляются в виде предварительно скомпилированных .whl - это формат колеса из PyPi, который не требует какой-либо компиляции.

→ Значит ли это, что для проекта Python не существует многоэтапной сборки? Абсолютно да !!! Не все пакеты PyPi предварительно скомпилированы в формате .whl, многие из них являются устаревшими tar.gz (сжатыми tarballs), которые необходимо сначала скомпилировать И здесь многоступенчатая сборка сработает.

→ Кроме того, многоэтапность применима, если вы создаете пакет python из исходного кода или используете локальный пакет с помощью setup.py, поскольку, опять же, они должны быть сначала скомпилированы.

→ Я настоятельно рекомендую вам прочитать эту статью из Real Python, в которой объясняется, что такое колеса в Python.

→ Из req.txt, который я использую для демонстрации, только вышеуказанные пакеты не являются форматом колеса, а также они уже очень маленькие по размеру. Но если некоторые пакеты не являются предварительно скомпилированными колесами и имеют большой размер, это приведет к потере большого размера диска.

3. Создание образа Docker для любого проекта Python (GPU):

Создание образа Docker на базе ЦП несложно, но не то же самое, что и создание докера на базе графического процессора. Если не построить должным образом, он может стать огромным. Я сосредоточусь на практической части и части реализации, а не на ее теоретической части (так как я думаю, что это выходит за рамки данной статьи).

3.1 Необходимые условия

  • И Tensorflow, и Pytorch используют драйверы графического процессора Nvidia CUDA. Поэтому последние драйверы Nvidia, драйверы CUDA и соответствующие им cuDNN должны быть сначала установлены на хост-машине (я не могу описывать процесс здесь, поскольку он выходит за рамки, возможно, для какого-то другого блога).
  • После подготовки хост-устройства необходимо установить nvidia-docker2, что позволит движку Docker получить доступ к базовым драйверам графического процессора Nvidia.
  • Наиболее важной частью является выбор правильной версии / тега CUDA, cuDNN для образа докера nvidia и тензорного потока / pytorch по отношению к нему. Таким образом, эта система машинного обучения может использовать базовое оборудование графического процессора. Поверьте мне, это может быть действительно неприятной задачей, поэтому у меня есть практическое правило:

→ Всегда используйте ту же версию CUDA и cuDNN в образе Docker, что и на соответствующем хост-компьютере.

→ Не устанавливайте слепо последнюю версию библиотеки tensorflow / pytorch из PyPi. Совершенно неверно, что любая версия этого пакета будет работать с любой версией CUDA, cuDNN. Фактически, комбинация обеих последних версий, tenorflow / pytorch с CUDA / cuDNN может быть несовместимой. Всегда сначала проверяйте комбинацию в среде разработки.

→ В Docker-хабе Nvidia много образов, поэтому понимание их тегов и выбор правильного образа - самый важный строительный блок. Описание официального докер-хаба Nvidia:

Нас интересует только база, среда выполнения, а не разработка (поскольку мы ориентируемся на среду prod). Как выбрать конкретный тег? Я отвечу на него в следующем подразделе.

3.2 Одноступенчатый

  • Выбор тега: практическое правило, которому я следую:

я. Шаг 1. Проверьте версию CUDA и cuDNN базовой хост-машины.

II. Шаг 2. Выберите образ Docker на основе шага 1. Итак, в моем случае я выбрал «nvidia / cuda: 10.1-cudnn7-runtime». Почему время выполнения? Потому что это тот, который включает как CUDA, так и cuDNN.

iii. Шаг 3: Выберите правильную версию tensorflow / pytorch, совместимую с этой версией CUDA и cuDNN. В моем случае это был тензорный поток = 2,20.

iv. Предупреждающий шаг: образ докера от Nvidia может быть более старой версией Ubuntu (18.04 или даже 16.04), которая установит python 3.6. Поэтому здесь необходимо уделить внимание проверке совместимости вашего проекта, а также внешних пакетов с версией python. В любом случае конкретная версия может быть установлена ​​из исходников.

Примечание. Поскольку вы можете видеть, что образ Docker от nvidia основан на ubuntu 18.04, мне нужно сделать небольшую дополнительную настройку, чтобы установить tensorflow = 2.2.0.

3.3 Многоступенчатая

  • Мы можем использовать тот же механизм, который я показал в 2.2.
  • Первый этап будет использоваться для загрузки и компиляции пакетов Python, а затем они будут скопированы на второй этап или этап выполнения.
  • Здесь также должны использоваться все правила большого пальца из 3.2.

Примечание: чтобы сделать python 3.8 по умолчанию, я добавил дополнительный код, если это не ваш случай, вы можете избежать этой проблемы.

  • Опять же, незначительное улучшение, но даже здесь логика / объяснение применимы, как и в случае 2.2.
  • Я настоятельно рекомендую всегда использовать многоступенчатую сборку в любом сценарии использования, так как это также улучшает читаемость.

4. Проверка образа Docker с помощью Dive

  • Даже после создания образа докера с соблюдением всех возможных передовых практик мы все равно должны исследовать какие-либо улучшения.
  • Dive - отличный инструмент командной строки, предназначенный для изучения образа докера, содержимого слоя и поиска способов уменьшить размер вашего образа Docker / OCI. Он имеет 24k + звезд на GitHub. Кроме того, им очень легко пользоваться и ориентироваться.
  • В нем есть две очень полезных матрицы:

я. Потенциально потраченное впустую пространство
ii. Оценка эффективности изображения

  • Но его лучшая особенность - это интеграция с любым инструментом CI. Мы можем установить условие для любой из двух или обеих метрик, и если условие не выполняется, задание CI также не удастся. Таким образом, мы всегда можем быть уверены в образе Docker, созданном для каждого задания CI.

Вывод: основной целью всегда должен быть минимальный размер образа докера, поскольку любая сборка образа докера для системы машинного обучения всегда будет тяжелой. Мы всегда должны следовать всем лучшим практикам, особенно многоступенчатой ​​сборке и управлению версиями пакетов. И последнее, но также самое важное для образов на основе графических процессоров - это проверка конфигурации в среде разработки.

Вы можете связаться со мной через LinkedIn, если потребуется помощь.