Как использовать библиотеку python, которая постоянно меняется в образе докера или новом контейнере?

Я организую свой код в пакете Python (обычно в виртуальной среде, такой как virtualenv и/или conda), а затем обычно вызываю:

python <path_to/my_project/setup.py> develop

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

Первое решение, о котором я подумал, — это решение, которое копирует данные в моем образе докера с помощью:

COPY /path_to/my_project
pip install /path_to/my_project

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

#BASH FILE TO BUILD AND REBUILD MY STUFF
# build the image with the newest version of 
# my project code and it pip installs it and its depedencies
docker build -t image_name .
docker run --rm image_name python run_ML_experiment_file.py 
docker kill current_container #not sure how to do get id of container
docker rmi image_name

как я уже сказал, моя интуиция подсказывает мне, что это глупо, поэтому я надеялся, что есть одна команда, способная сделать это с помощью Docker или с одним файлом Dockerfile. Кроме того, обратите внимание, что команда должна использовать -v ~/data/:/data, чтобы иметь возможность получить данные и какой-либо другой том/монтирование для записи (на хосте) после завершения обучения.

Еще одно решение, которое я подумал, заключалось в том, чтобы иметь все зависимости python или другие зависимости, которые нужны моей библиотеке, в файле Dockerfile (и, следовательно, в образе), а затем каким-то образом выполнять в работающем контейнере установку моей библиотеки. Возможно с docker exec [OPTIONS] CONTAINER COMMAND как:

docker exec CONTAINER pip install /path_to/my_project

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

docker exec CONTAINER python run_ML_experiment_file.py

тем не менее, я до сих пор не знаю, как систематически получать идентификатор контейнера (потому что я, вероятно, не хочу искать идентификатор контейнера каждый раз, когда делаю это).

В идеале, на мой взгляд, лучшим концептуальным решением было бы просто знать Dockerfile с самого начала, к какому файлу он должен подключаться (например, /path_to/my_project), а затем каким-то образом выполнять python [/path_to/my_project] develop внутри образа, чтобы он всегда был связан с потенциально изменяющимся python. пакет/проект. Таким образом, я могу проводить свои эксперименты с помощью одной команды docker, например:

docker run --rm -v ~/data/:/data python run_ML_experiment_file.py

и не нужно каждый раз явно обновлять образ (включая отсутствие необходимости переустанавливать части изображения, которые должны быть статичными), поскольку он всегда синхронизируется с реальной библиотекой. Кроме того, мне не нужно, чтобы какой-то другой скрипт каждый раз создавал новый образ с нуля. Кроме того, было бы неплохо иметь возможность избежать написания любого bash, если это возможно.


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

# install my library (only when the a container is spun)
CMD python ~/my_tf_proj/setup.py develop

Преимущество заключается в том, что он будет устанавливать мою библиотеку только при запуске нового контейнера. Это решает проблему разработки, потому что повторное создание нового образа занимает много времени. Хотя я только что понял, что если я использую команду CMD, я не могу запускать другие команды, заданные для моего запуска докера, поэтому я на самом деле имею в виду запуск ENTRYPOINT.

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

VOLUME /absolute_path_to/my_tf_proj /my_tf_proj

почему я не могу связать с помощью команды VOLUME в моем Dockerfile? Мое основное намерение при использовании VOLUME — сделать мою библиотеку (и другие файлы, которые всегда нужны этому образу) доступными, когда команда CMD пытается установить мою библиотеку. Возможно ли, чтобы моя библиотека всегда была доступна при запуске контейнера?

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

В качестве справки прямо сейчас мой нерабочий файл Dockerfile выглядит следующим образом:

# This means you derive your docker image from the tensorflow docker image
# FROM gcr.io/tensorflow/tensorflow:latest-devel-gpu
FROM gcr.io/tensorflow/tensorflow
#FROM python
FROM ubuntu

RUN mkdir ~/my_tf_proj/
# mounts my tensorflow lib/proj from host to the container
VOLUME /absolute_path_to/my_tf_proj

#
RUN apt-get update

#
apt-get install vim

#
RUN apt-get install -qy python3
RUN apt-get install -qy python3-pip
RUN pip3 install --upgrade pip

#RUN apt-get install -y python python-dev python-distribute python-pip

# have the dependecies for my tensorflow library
RUN pip3 install numpy
RUN pip3 install keras
RUN pip3 install namespaces
RUN pip3 install pdb

# install my library (only when the a container is spun)
#CMD python ~/my_tf_proj/setup.py develop
ENTRYPOINT python ~/my_tf_proj/setup.py develop

В качестве побочного замечания:

Кроме того, по какой-то причине мне нужно сделать RUN apt-get update, чтобы иметь возможность даже установить pip или vim в моем контейнере. Люди знают, почему? Я хотел сделать это, потому что на тот случай, если я захочу подключиться к контейнеру с терминалом bash, это было бы очень полезно.

Кажется, что Docker просто заставляет вас установить apt, чтобы всегда иметь самую последнюю версию программного обеспечения в контейнере?


person Charlie Parker    schedule 08.12.2016    source источник
comment
Чтобы систематически получать идентификатор контейнера, вы назначаете ему имя при запуске: docker run --name a_name your_image, затем ссылаетесь на него в docker exec a_name /your/command.   -  person mustaccio    schedule 08.12.2016
comment
Альтернативой без имени является cid=$(docker run -d image), которая запускает контейнер отдельно (в фоновом режиме) и выводит идентификатор контейнера на стандартный вывод.   -  person Matt    schedule 09.12.2016
comment
stackoverflow.com/questions/40914696/   -  person Matt    schedule 09.12.2016


Ответы (2)


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

После стабилизации я удаляю карту/монтировку и добавляю пакет в список элементов для установки с помощью pip. У меня есть отдельный контейнер, на котором работает devpi, поэтому я могу pip устанавливать пакеты независимо от того, загружаю ли я их полностью в PyPI или просто помещаю в свой локальный контейнер devpi.

Это ускорит создание контейнера, даже если вы используете распространенный (но более ограниченный) python [path_to_project/setup.py] develop. Ваш Dockerfile в этом случае должен выглядеть так:

 # the following seldom changes, only when a package is added to setup.py
 COPY /some/older/version/of/project/plus/dependent/packages /older/setup
 RUN pip /older/setup/your_package.tar.gz

 # the following changes all the time, but that is only a small amount of work
 COPY /latest/version/of/project     
 RUN python [path_to_project/setup.py] develop

Если первая копия приведет к изменениям в файлах под /older/setup, контейнер будет перестроен оттуда.

Запуск python ... develop по-прежнему занимает больше времени, и вам нужно пересобрать/перезапустить контейнер. Поскольку все мои пакеты также могут быть просто скопированы/связаны с ними (в дополнение к установке), это все еще большие накладные расходы. Я запускаю небольшую программу в контейнере, которая проверяет, изменяются ли (смонтированные/сопоставленные) источники, а затем автоматически перезапускает все, что я разрабатываю/тестирую. Так что мне нужно только сохранить новую версию и посмотреть вывод контейнера.

person Anthon    schedule 08.12.2016
comment
да, я согласен с тем, что мой каталог хоста можно смонтировать в образ. Но я не хочу говорить, что нужно заново создавать целое изображение с нуля каждый раз, когда мне нужно изменить только мою библиотеку. По сути, если бы я мог запускать python [path_to_project/setup.py] develop из образа каждый раз, когда запускаю контейнер с правильной версией моей библиотеки, это было бы идеально. - person Charlie Parker; 09.12.2016
comment
Вы можете сделать это с минимальной перестройкой контейнера, я обновил свой ответ, указав, как это сделать. - person Anthon; 09.12.2016

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

Но для простоты и избавления от ручных шагов по созданию образа рассмотрите возможность использования docker-compose.

docker-compose.yml может выглядеть так:

ml_experiment:
  build: <path/to/Dockerfile>
  volumes:
    - ~/data/:/data
  command: ["python", "run_ML_experiment_file.py"] 

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

docker-compose up --build

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

См. https://docs.docker.com/compose/

person Yuva    schedule 08.12.2016
comment
извините за мое невежество (не знаком с docker compose), но как этот docker-compose решает мою проблему необходимости повторной установки моей библиотеки, которая постоянно меняется? - person Charlie Parker; 09.12.2016
comment
Dockerfile по-прежнему будет иметь эту инструкцию pip. docker-compose создает образ из этого Dockerfile каждый раз перед запуском контейнера. Таким образом, нет необходимости запускать команды exec вручную. - person Yuva; 09.12.2016
comment
извините за мое невежество, чем это лучше/хуже, чем решение, которое я предложил (если оно просто каждый раз создает новое изображение)? - person Charlie Parker; 09.12.2016
comment
@CharlieParker Это позволяет вам создавать/запускать/конфигурировать с помощью одной команды. Это не решает проблему развития - person Matt; 09.12.2016
comment
может быть, я должен был уточнить, я на самом деле не хочу продолжать делать изображения, некоторые части изображений статичны. Я просто хочу иметь возможность переустановить свою библиотеку Python без переустановки статических частей. - person Charlie Parker; 09.12.2016
comment
обратите внимание, что мое решение не запускает команды вручную, так как они находятся в файле bash. - person Charlie Parker; 09.12.2016
comment
Как предложил @Matt, он не избегает повторного создания изображений. Я думал, вы хотите избавиться от написания этих команд exec. Извините, я неправильно прочитал это. - person Yuva; 09.12.2016