关于python:在运行时将pip需求添加到docker镜像

Add pip requirements to docker image in runtime

我想能够添加一些额外的要求到自己的创建Docker图像。我的策略是用一个命令从dockerfile构建映像,该命令将在运行时使用装载的卷执行"pip install-r"命令。

这是我的文件:

1
2
3
4
5
6
7
8
9
FROM ubuntu:14.04

RUN apt-get update
RUN apt-get install -y python-pip python-dev build-essential
RUN pip install --upgrade pip

WORKDIR /root

CMD ["pip install -r /root/sourceCode/requirements.txt"]

有了那个dockerfile,我建立了一个形象:

1
sudo docker build -t test .

最后,我尝试使用以下命令附加我的新需求:

1
sudo docker run -v $(pwd)/sourceCode:/root/sourceCode -it test /bin/bash

我的本地文件夹"sourcecode"在有效的requirements.txt文件中(它只包含一行值为"gunicorn")。当我得到提示时,我可以看到需求文件在那里,但是如果我执行一个pip freeze命令,gunicorn包就不会被列出。

为什么requirements.txt文件已正确附加,但pip命令工作不正常?


您可以将最后一个语句,即CMD更改为下面的语句。

--在下面的语句中指定PIP位置的绝对路径

1
CMD ["/usr/bin/pip","install","-r","/root/sourceCode/requirements.txt"]

更新:根据评论添加其他答案。

必须注意的一点是,如果定制的图像需要附加的要求,那么它应该是图像的一部分,而不是在运行时执行。

使用下面的基本图像进行测试:

1
docker pull colstrom/python:legacy

因此,应该使用dockerfile的RUN命令运行安装包。并且应该使用CMD作为容器内的进程运行的应用程序。

只需运行下面的命令来检查基本映像是否有任何PIP包,结果什么也没有。

1
docker run --rm --name=testpy colstrom/python:legacy /usr/bin/pip freeze

下面是一个简单的例子来演示相同的内容:

文档文件

1
2
3
4
FROM colstrom/python:legacy
COPY requirements.txt /requirements.txt
RUN ["/usr/bin/pip","install","-r","/requirements.txt"]
CMD ["/usr/bin/pip","freeze"]

要求.txt

1
selenium

用pip包构建图像希望您知道将dockerfile,requirements.txt文件放在新目录中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
D:\dockers\py1>docker build -t pypiptest .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM colstrom/python:legacy
 ---> 640409fadf3d
Step 2 : COPY requirements.txt /requirements.txt
 ---> abbe03846376
Removing intermediate container c883642f06fb
Step 3 : RUN /usr/bin/pip install -r /requirements.txt
 ---> Running in 1987b5d47171
Collecting selenium (from -r /requirements.txt (line 1))
  Downloading selenium-3.0.1-py2.py3-none-any.whl (913kB)
Installing collected packages: selenium
Successfully installed selenium-3.0.1
 ---> f0bc90e6ac94
Removing intermediate container 1987b5d47171
Step 4 : CMD /usr/bin/pip freeze
 ---> Running in 6c3435177a37
 ---> dc1925a4f36d
Removing intermediate container 6c3435177a37
Successfully built dc1925a4f36d
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

现在运行图像如果不传递任何外部命令,那么容器从CMD接收命令,该命令只显示pip包的列表。在这种情况下,selenium

1
2
3
4
5
D:\dockers\py1>docker run -itd --name testreq pypiptest
039972151eedbe388b50b2b4cd16af37b94e6d70febbcb5897ee58ef545b1435

D:\dockers\py1>docker logs testreq
selenium==3.0.1

因此,上面显示包已成功安装。

希望这是有帮助的。


TLDR

pip命令没有运行,因为您告诉docker运行/bin/bash

1
2
3
docker run -v $(pwd)/sourceCode:/root/sourceCode -it test /bin/bash
                                                              ^
                                                             here

更长的解释

容器的默认ENTRYPOINT/bin/sh -c。你不会在dockerfile中覆盖它,所以它仍然存在。默认的CMD指令可能是空的。你在你的dockerfile中覆盖了它。运行时(为了简洁起见,忽略卷)

1
docker run -it test

容器内实际执行的是

1
/bin/sh -c pip install -r /root/sourceCode/requirements.txt

非常直接,看起来当您启动容器时,它将运行pip

现在让我们看一下您用来启动容器的命令(同样,忽略卷)

1
docker run -v -it test /bin/bash

容器内实际执行的是

1
/bin/sh -c /bin/bash

在dockerfile中指定的CMD参数被命令行中指定的COMMAND参数覆盖。回想一下,docker run命令采用这种形式

1
docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

进一步阅读

  • 这个答案对CMDENTRYPOINT指令的作用有一个真正切题的解释。


    The ENTRYPOINT specifies a command that will always be executed when the container starts.

    The CMD specifies arguments that will be fed to the ENTRYPOINT.

  • 这篇关于ENTRYPOINTCMD指令差异的博客文章值得一读。


  • 使用@rao和@romanarmy在他们的答案中解释的概念,我最终找到了一种做我想要做的事情的方法:为自己创建的docker映像添加额外的python需求。

    我的新dockerfile如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    FROM ubuntu:14.04

    RUN apt-get update
    RUN apt-get install -y python-pip python-dev build-essential
    RUN pip install --upgrade pip

    WORKDIR /root

    COPY install_req.sh .

    CMD ["/bin/bash" ,"install_req.sh"]

    我已经将具有以下内容的shell脚本的执行添加为第一个命令:

    1
    2
    3
    #!/bin/bash
    pip install -r /root/sourceCode/requirements.txt
    pip freeze > /root/sourceCode/freeze.txt

    最后,我使用以下命令构建和运行图像:

    1
    2
    docker build --tag test .
    docker run -itd --name container_test -v $(pwd)/sourceCode:/root/sourceCode test <- without any parameter at the end

    正如我在文章开头所解释的,我在本地文件夹中有一个名为sourcecode的文件夹,其中包含一个只有一行"gunicorn"的有效requirements.txt文件。

    最后,我可以向给定的docker映像添加一些额外的需求(本例中的gunicorn包)。

    在构建并运行了我的实验之后,如果我检查日志(docker logs container_test),我会看到如下情况:

    1
    2
    3
    Downloading gunicorn-19.6.0-py2.py3-none-any.whl (114kB)
        100% |################################| 122kB 1.1MB/s
    Installing collected packages: gunicorn

    此外,容器在安装的卷内创建了一个freeze.txt文件,其中包含所有安装的PIP包,包括所需的gunicorn:

    1
    2
    3
    4
    5
    6
    7
    chardet==2.0.1
    colorama==0.2.5
    gunicorn==19.6.0
    html5lib==0.999
    requests==2.2.1
    six==1.5.2
    urllib3==1.7.1

    现在我对新创建的文件的权限还有其他问题,但这可能会出现在新文章中。

    谢谢您!