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

Однако, когда я попыталась создать Docker с моей собственной моделью, я быстро обнаружила, что его работа интуитивно не так понятна. Использовать Docker совсем не так же просто, как ввести RUN в начале скрипта загрузки EC2. Непредсказуемое поведение возникает часто, и довольно утомительно учиться налаживать новый инструмент.

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

Дисклеймер: модель, о которой пойдет речь — это единичная работа для конкретного случая, это НЕ веб-сервис с конечными точками API, НЕ распределенные параллельные задания. Если вы будете следовать этому руководству, весь процесс помещения кода в контейнер займет не более 25 минут. 

Обязательные требования

  • AWS аккаунт
  • установленный AWS CLI 
  • установленный Docker с настроенным пользовательским аккаунтом
  • установленный Python 3 

Создание Docker-файла ??‍♀️

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

Минимальный Dockerfile для приложения Python:

FROM python:3.6-stretch
MAINTAINER Tina Bu <[email protected]>

# устанавливаем параметры сборки
RUN apt-get update && \
 apt-get install -y gcc make apt-transport-https ca-certificates build-essential

# проверяем окружение python 
RUN python3 --version
RUN pip3 --version

# задаем рабочую директорию для контейнера 
WORKDIR  /usr/src/<app-name>

# устанавливаем зависимости python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# копируем все файлы из корня проекта в рабочую директорию 
COPY src/ /src/
RUN ls -la /src/*

# запускаем приложение Python 
CMD ["python3", "/src/main.py"]

В Dockerfile выше я начала с базового образа Python 3.6. С помощью команды apt-get обновила системные библиотеки и установила make и build. Затем проверила, что версии python и pip актуальны, задала рабочий каталог, скопировала requirements.txt в контейнер, и pip установил в него все библиотеки. Наконец, я скопировала все файлы с остальным кодом в контейнер, проверила, чтобы все нужные мне файлы были на месте, и запустила файл точки входа main.py.

Dockerfile должен работать, если структура каталога кода выглядит так: 

- app-name
     |-- src
          |-- main.py
          |-- other_module.py
     |-- requirements.txt
     |-- Dockerfile

Все, что нужно сделать, — это изменить <app-name> на имя вашего приложения, и мы готовы собрать образ. 

Существует множество рекомендаций, как сделать docker-файл меньше и повысить его эффективность, но большинство из них выходит за рамки этого поста. Тем не менее о нескольких вещах вам стоит помнить: 

Используйте Python stretch как базовый образ

Говорят, что вместо использования универсального образа Ubuntu, стоит использовать официальные базовые образы, например, Alpine Python. Но оказалось, что с ним очень сложно работать, особенно, устанавливать пакеты. Базовый образ Ubuntu ведет себя предсказуемо, но я предлагаю начать с Python 3.6 stretch, официального образа Python, основанного на Debian 9 (он же stretch). Python stretch предоставляется с установленной и обновленной средой Python и pip. Вам остается только разобраться, как его установить, если вы выбрали Ubuntu.

Устанавливайте только то, что вам нужно

Очень заманчиво просто скопировать какой-нибудь шаблонный Dockerfile, особенно, если это ваш первый Docker-проект. Но я рекомендую устанавливать только то, что вам действительно нужно, чтобы контролировать размер образа. Если вы видите целую кучу make и build, установленных другими людьми, попробуйте не включать их сразу, сначала проверьте, работает ли ваш контейнер. Меньший размер образа — это быстрое создание и развертывание. (Еще одна причина попробовать мой минимальный шаблон выше!) 

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

.git
.gitignore
README.md
LICENSE
Dockerfile*
docker-compose*
data/*
test/*

Добавьте requirements.txt перед копированием кода

Перед копированием исходного кода всегда добавляйте в Dockerfile файл requirements.txt. Таким образом, каждый раз, когда вы будете менять код и пересобирать контейнер, Docker будет заново использовать кэшированный слой, пока пакеты не установятся, а не исполнять команду pip install в каждой сборке, даже если нужные пакеты никогда не менялись. Никто не хочет ждать лишнюю минуту просто потому, что вы добавили пустую строку в код.

Если вам интересно узнать больше о Dockerfile, в Приложении есть короткий справочник по нескольким базовым командам. Переходим к Шагу 2 — создаем контейнер с только что созданным Dockerfile.

Создаем образ с Dockerfile

Команда docker build создает образ согласно инструкциям, заданным в Dockerfile. Осталось дать образу имя.

docker build -t ${IMAGE_NAME}:${VERSION} .

Убедитесь, что образ существует локально:

docker images

Также можно поставить tag с более понятным именем, вместо использования hash ID.

docker tag ${IMAGE_ID} ${IMAGE_NAME}:${TAG}
# or
docker tag ${IMAGE_NAME}:${VERSION} ${IMAGE_NAME}:${TAG}

Теперь нужно протестировать образ локально, чтобы убедиться, все ли работает хорошо. 

docker run ${IMAGE_NAME}:${TAG}

Поздравляю! Вы упаковали модель в контейнер, который может быть запущен в любом месте, где установлен Docker. 

Приложение. Команды Dockerfile 

  • Dockerfile начинается с FROM— это необходимое условие. Образы создаются слоями, то есть можно использовать другой образ как основу для вашего. Команда FROM определяет базовый слой и в качестве аргумента берет имя образа. Можно добавить имя пользователя в Docker Cloud и версию образа в форматеusername/imagename:version.
  • RUN используется, чтобы собрать образ. Для каждой команды RUN Docker запускает ее, затем создает новый слой образа. Таким образом, вы можете легко вернуться к предыдущей версии образа. Синтаксис инструкции RUN заключается в размещении полного текста команды оболочки после RUN(например, RUN mkdir /user/local/foo). RUN автоматически запускается в оболочке /bin/sh; другую оболочку можно задать так RUN /bin/bash -c 'mkdir /user/local/foo'.
  • COPY копирует локальные файлы в контейнер.
  • CMD определяет команды, которые запускаются в начале работы образа. В отличие от RUN, он не создает новый слой, а просто запускает команды. В Dockerfile или образе может быть только один CMD. Если вам нужно запустить несколько команд, лучше, чтобы CMD запустил скрипт.  
  • EXPOSE создает подсказку для пользователей образа о том, какие порты предоставляют сервисы. Она включена в информацию, которую можно извлечь отсюда docker inspect <container-id>. Имейте в виду, что команда EXPOSE не делает порты доступными для хоста! Для этого требуется публикация портов с помощью флага -p при использовании docker run.
  • PUSH загружает образ в частный или облачный реестр.

Читайте также:


Перевод статьи Tina Bu: Build a Docker Container with Your Machine Learning Model

Предыдущая статья8 базовых алгоритмических задач на собеседованиях
Следующая статьяПерсонализация контента с IBM Watson