Полное руководство с шаблонными скриптами для начинающих Docker

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

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

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

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

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

Предварительное условие

Шаг 1 Создайте Dockerfile👷🏻‍♀️

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

Dockerfile - это текстовый документ, содержащий все команды, которые пользователь может вызвать в командной строке для сборки изображения.

(Вы можете создать образ Docker из Dockerfile или docker-compose.yml. Если ваш код можно реорганизовать как приложение Docker с несколькими контейнерами, вы можете изучить docker compose, но теперь Dockerfile должно хватить.)

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

В приведенном выше файле 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

Если в вашем коде еще нет main.py или сценария оболочки для запуска обучения / вывода модели, то вы можете сначала реорганизовать свой код. Также не забудьте закрепить зависимости библиотеки в requirements.txt файле с pipreqs path/to/project. Я рекомендую использовать pipreqs, а не pip freeze, потому что, когда вы запускаете pip freeze > requirements.txt, он выводит все установленные пакеты в этой среде, тогда как pipreqs дает вам только те, которые фактически импортированы этим проектом. (Установите pipreqs с pip(3) install pipreqs, если у вас его еще нет)

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

Есть много лучших практик, чтобы сделать Dockerfile меньше и эффективнее, но большинство из них выходит за рамки этой статьи. Однако следует помнить о нескольких вещах:

1.1 Использование Python stretch в качестве базового изображения

Люди говорят, что вместо того, чтобы начинать с общего образа Ubuntu, используйте официальный базовый образ, такой как Alpine Python. Alpine Python - это действительно небольшой образ Python Docker, основанный на Alpine Linux, намного меньший, чем образы Python докеров по умолчанию, но все же в нем есть все необходимое для наиболее распространенных проектов Python. Но мне было чрезвычайно трудно работать, особенно при установке пакетов.

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

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

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

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

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

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

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

Если вам интересно узнать больше о Dockerfile, в приложении I приведен краткий обзор нескольких основных команд, которые мы использовали. Или вы можете проверить документацию Dockerfile здесь.

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

Шаг 2 - Создайте образ с помощью Dockerfile 👩🏻‍🍳

После того, как у вас будет Dockerfile готов, самое время создать образ контейнера. docker build создает изображение в соответствии с инструкциями, приведенными в Dockerfile. Все, что вам нужно сделать, это дать вашему изображению имя (необязательный тег версии).

$ docker build -t IMAGE_NAME:TAG .
$ # or
$ docker build -t USERNAME/IMAGE_NAME:TAG .

Единственная разница между этими двумя командами заключается в том, что перед именем изображения стоит USERNAME/. Имя образа состоит из разделенных косой чертой компонентов имени, обычно префиксом является имя хоста реестра. USERNAME/IMAGE_NAME не является обязательным форматом для указания имени изображения в целом. Но изображения в репозиториях Amazon ECR соответствуют полному REGISTRY/REPOSITORY:TAG соглашению об именах. Например, aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app:latest.

В приведенных выше командах происходит несколько вещей.

Сначала мы сказали демону Docker получить Dockerfile, присутствующий в текущем каталоге (это то, что делает . в конце).

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

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

$ docker build -t USERNAME/IMAGE_NAME .
$ docker tag USERNAME/IMAGE_NAME USERNAME/IMAGE_NAME:1.0
$ ...
$ docker build -t USERNAME/IMAGE_NAME .
$ docker tag USERNAME/IMAGE_NAME USERNAME/IMAGE_NAME:2.0
$ ...

Вы можете пометить изображение при создании изображения с помощью docker build -t USERNAME/IMAGE_NAME:TAG . или явным образом пометить его, например, docker tag SOURCE_IMAGE:TAG TARGET_IMAGE:TAG, после того, как оно было построено.

Теперь, если вы запустите docker images, вы должны увидеть, что изображение существует локально, репозиторий которого - USERNAME/IMAGE_NAME, а тег - TAG.

$ docker images

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

$ docker run USERNAEM/IMAGE_NAME:TAG

Не стесняйтесь проверить приложение II для краткого обзора некоторых основных команд Docker CLI или официальный документ здесь.

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

Приложение I - Команды Dockerfile 👩🏻‍🏫

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

Приложение II - Команды интерфейса командной строки Docker 👩🏻‍🏫

Некоторые базовые команды интерфейса командной строки Docker включают в себя:

  • docker build создает образ из файла Docker
  • docker images отображает все образы Docker на машине
  • docker run запускает контейнер и выполняет в нем команды
  • docker run варианты:
  • -p укажите порты в хосте и контейнере Docker
  • -it открывает интерактивную консоль
  • -d запускает контейнер в режиме демона (работает в фоновом режиме)
  • -e устанавливает переменные среды
  • docker ps отображает все запущенные контейнеры
  • docker rmi удаляет одно или несколько изображений
  • docker rm удаляет один или несколько контейнеров
  • docker kill убивает один или несколько запущенных контейнеров
  • docker tag помечает изображение псевдонимом, на который можно будет ссылаться позже
  • docker login входит в реестр Docker.

Об авторе

Я инженер по машинному обучению и пишу о продуктивности и саморазвитии помимо технических руководств. Я посетил Йеллоустон 4 июля, так что на обложке.