如何使用在docker镜像或新容器中不断更改的python库?

时间:2016-12-08 20:10:56

标签: python docker dockerfile

我在python包中组织我的代码(通常在virtualenv和/或conda等虚拟环境中),然后通常调用:

python <path_to/my_project/setup.py> develop

这样我就可以使用最新版本的代码了。由于我主要开发统计或机器学习算法,因此我每天都会编写原型并更改代码。但是,最近在我可以访问的集群上运行实验的推荐方法是通过docker。我了解了码头工具,我认为我对如何使其工作有一个粗略的想法,但我不确定我的解决方案是否良好,或者是否有更好的解决方案。

我认为第一个解决方案是使用以下方法复制Docker镜像中的数据的解决方案:

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来获取数据,并在完成培训时将其他一些卷/ mount写入(在主机中)。

我认为另一个解决方案是拥有我的库在Dockerfile中所需的所有python依赖项或其他依赖项(因此在映像中),然后以某种方式在运行容器中执行我的库的安装。也许以docker exec [OPTIONS] CONTAINER COMMAND为:

docker exec CONTAINER pip install /path_to/my_project

在正在运行的容器中。之后,我可以使用相同的exec命令运行我想要运行的真实实验:

docker exec CONTAINER python run_ML_experiment_file.py
但是,我仍然不知道如何系统地获取容器ID(因为我可能不想每次都查看容器ID)。

理想情况下,最好的概念解决方案是简单地让Dockerfile从一开始就知道它应该挂载到哪个文件(即/path_to/my_project)然后以某种方式在图像中做python [/path_to/my_project] develop所以它总是链接到可能改变的python包/项目。这样我就可以使用单一泊坞码命令运行我的实验,如下所示:

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

优点是每当我运行一个新容器时它只会pip安装我的库。这解决了开发问题,因为重新创建新图像需要很长时间。虽然我刚刚意识到,如果我使用CMD命令,那么我就无法运行给予docker run的其他命令,所以我实际上是指运行ENTRYPOINT

现在唯一要解决的问题是我在使用卷时遇到问题,因为我无法成功链接到Dockerfile中的我的宿主项目库(这似乎需要一个绝对的出于某种原因的路径)。我目前正在做(这似乎不起作用):

VOLUME /absolute_path_to/my_tf_proj /my_tf_proj

为什么我无法使用Dockerfile中的VOLUME命令进行链接?我使用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安装,以便始终在容器中安装最新版本的软件?

2 个答案:

答案 0 :(得分:2)

对于部署/分发,为您的包提供docker镜像是无缝的。如果不是图像,则需要将源代码传输到需要运行的环境,配置卷以使源容器内部容器可以构建等等。使用image只需拉动并运行容器出来的。

但为了方便并摆脱构建图像的手动步骤,请考虑使用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/

答案 1 :(得分:2)

在开发过程中,将主机目录与不断变化的源映射/挂载到Docker容器中是完全正确的。其余的(python版本,您依赖的其他库都可以在docker容器中以正常方式安装。

一旦稳定,我删除了地图/装载,并将包添加到要与add一起安装的项目列表中。我有一个运行pip的单独容器,所以我可以devpi - 安装包,无论我是将它们一直推送到PyPI,还是只将它们推送到我的本地pip容器。

即使您使用通用(但更有限)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仍然会花费更多时间,您需要重建/重启容器。由于我的包也可以只是复制到/链接(除了安装),这仍然是一个很大的开销。我在容器中运行一个小程序,检查(已安装/映射)源是否更改,然后重新运行我正在开发/测试的任何内容。所以我只需要保存一个新版本并观察容器的输出。