当前位置: 首页 > 知识库问答 >
问题:

在为Python项目构建Docker映像时,如何避免重新安装包?

端木望
2023-03-14

我的Dockerfile有点像

FROM my/base

ADD . /srv
RUN pip install -r requirements.txt
RUN python setup.py install

ENTRYPOINT ["run_server"]

每次构建新映像时,都必须重新安装依赖项,这在我的区域可能会非常慢。

我想到的一种缓存已安装的软件包的方法是用以下更新的图像覆盖我的基本图像:

docker build -t new_image_1 .
docker tag new_image_1 my/base

所以下次我用这个Dockerfile构建时,我的/base已经安装了一些包。

但这种解决方案有两个问题:

  1. 并不总是可以覆盖基本映像
  2. 随着新图像的分层,基本图像变得越来越大

那么,我可以用什么更好的解决方案来解决这个问题呢?

关于我机器上docker的一些信息:

☁  test  docker version
Client version: 1.1.2
Client API version: 1.13
Go version (client): go1.2.1
Git commit (client): d84a070
Server version: 1.1.2
Server API version: 1.13
Go version (server): go1.2.1
Git commit (server): d84a070
☁  test  docker info
Containers: 0
Images: 56
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Dirs: 56
Execution Driver: native-0.2
Kernel Version: 3.13.0-29-generic
WARNING: No swap limit support

共有3个答案

公羊俊德
2023-03-14

为了尽量减少网络活动,您可以将pip指向主机上的缓存目录。

运行您的docker容器,将主机的pip缓存目录绑定安装到容器的pip缓存目录中。docker run命令应该如下所示:

docker run -v $HOME/.cache/pip-docker/:/root/.cache/pip image_1

然后在Dockerfile中,将您的需求作为入口点语句(或CMD语句)的一部分安装,而不是作为运行命令安装。这一点很重要,因为(如注释中所指出的)在映像构建期间(当执行RUN语句时)装载不可用。Docker文件应如下所示:

FROM my/base

ADD . /srv

ENTRYPOINT ["sh", "-c", "pip install -r requirements.txt && python setup.py install && run_server"]
冯宏恺
2023-03-14

我知道这个问题已经有了一些流行的答案。但是有一种新的方法可以为包管理器缓存文件。我认为,当BuildKit变得更标准时,这可能是一个很好的答案。

从Docker 18.09开始,对BuildKit提供了实验性支持。BuildKit添加了对Dockerfile中一些新功能的支持,包括对将外部卷挂载到RUN步骤的实验性支持。这允许我们为$HOME/. cache/pip/等内容创建缓存。

我们将使用以下requirements.txt文件作为示例:

Click==7.0
Django==2.2.3
django-appconf==1.0.3
django-compressor==2.3
django-debug-toolbar==2.0
django-filter==2.2.0
django-reversion==3.0.4
django-rq==2.1.0
pytz==2019.1
rcssmin==1.0.6
redis==3.3.4
rjsmin==1.1.0
rq==1.1.0
six==1.12.0
sqlparse==0.3.0

PythonDockerfile的典型示例可能如下所示:

FROM python:3.7
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install -r requirements.txt
COPY . /usr/src/app

使用环境变量DOCKER\u BuildKit启用BuildKit后,我们可以在大约65秒内构建未缓存的pip步骤:

$ export DOCKER_BUILDKIT=1
$ docker build -t test .
[+] Building 65.6s (10/10) FINISHED                                                                                                                                             
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                            0.0s
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/python:3.7                                                                                                              0.5s
 => CACHED [1/4] FROM docker.io/library/python:3.7@sha256:6eaf19442c358afc24834a6b17a3728a45c129de7703d8583392a138ecbdb092                                                 0.0s
 => [internal] load build context                                                                                                                                          0.6s
 => => transferring context: 899.99kB                                                                                                                                      0.6s
 => CACHED [internal] helper image for file operations                                                                                                                     0.0s
 => [2/4] COPY requirements.txt /usr/src/app/                                                                                                                              0.5s
 => [3/4] RUN pip install -r requirements.txt                                                                                                                             61.3s
 => [4/4] COPY . /usr/src/app                                                                                                                                              1.3s
 => exporting to image                                                                                                                                                     1.2s
 => => exporting layers                                                                                                                                                    1.2s
 => => writing image sha256:d66a2720e81530029bf1c2cb98fb3aee0cffc2f4ea2aa2a0760a30fb718d7f83                                                                               0.0s
 => => naming to docker.io/library/test                                                                                                                                    0.0s

现在,让我们添加实验标头并修改RUN步骤以缓存Python包:

# syntax=docker/dockerfile:experimental

FROM python:3.7
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
COPY . /usr/src/app

现在继续进行另一个构建。这需要同样的时间。但这次它将Python包缓存在我们新的缓存装载中:

$ docker build -t pythontest .
[+] Building 60.3s (14/14) FINISHED                                                                                                                                             
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                            0.0s
 => resolve image config for docker.io/docker/dockerfile:experimental                                                                                                      0.5s
 => CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:9022e911101f01b2854c7a4b2c77f524b998891941da55208e71c0335e6e82c3                                 0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load metadata for docker.io/library/python:3.7                                                                                                              0.5s
 => CACHED [1/4] FROM docker.io/library/python:3.7@sha256:6eaf19442c358afc24834a6b17a3728a45c129de7703d8583392a138ecbdb092                                                 0.0s
 => [internal] load build context                                                                                                                                          0.7s
 => => transferring context: 899.99kB                                                                                                                                      0.6s
 => CACHED [internal] helper image for file operations                                                                                                                     0.0s
 => [2/4] COPY requirements.txt /usr/src/app/                                                                                                                              0.6s
 => [3/4] RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt                                                                                  53.3s
 => [4/4] COPY . /usr/src/app                                                                                                                                              2.6s
 => exporting to image                                                                                                                                                     1.2s
 => => exporting layers                                                                                                                                                    1.2s
 => => writing image sha256:0b035548712c1c9e1c80d4a86169c5c1f9e94437e124ea09e90aea82f45c2afc                                                                               0.0s
 => => naming to docker.io/library/test                                                                                                                                    0.0s

大约60秒。与我们的第一个版本相似。

对<规范>要求做一个小改动。txt(例如在两个包之间添加新行)以强制缓存失效并再次运行:

$ docker build -t pythontest .
[+] Building 15.9s (14/14) FINISHED                                                                                                                                             
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => => transferring context: 2B                                                                                                                                            0.0s
 => resolve image config for docker.io/docker/dockerfile:experimental                                                                                                      1.1s
 => CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:9022e911101f01b2854c7a4b2c77f524b998891941da55208e71c0335e6e82c3                                 0.0s
 => [internal] load build definition from Dockerfile                                                                                                                       0.0s
 => => transferring dockerfile: 120B                                                                                                                                       0.0s
 => [internal] load .dockerignore                                                                                                                                          0.0s
 => [internal] load metadata for docker.io/library/python:3.7                                                                                                              0.5s
 => CACHED [1/4] FROM docker.io/library/python:3.7@sha256:6eaf19442c358afc24834a6b17a3728a45c129de7703d8583392a138ecbdb092                                                 0.0s
 => CACHED [internal] helper image for file operations                                                                                                                     0.0s
 => [internal] load build context                                                                                                                                          0.7s
 => => transferring context: 899.99kB                                                                                                                                      0.7s
 => [2/4] COPY requirements.txt /usr/src/app/                                                                                                                              0.6s
 => [3/4] RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt                                                                                   8.8s
 => [4/4] COPY . /usr/src/app                                                                                                                                              2.1s
 => exporting to image                                                                                                                                                     1.1s
 => => exporting layers                                                                                                                                                    1.1s
 => => writing image sha256:fc84cd45482a70e8de48bfd6489e5421532c2dd02aaa3e1e49a290a3dfb9df7c                                                                               0.0s
 => => naming to docker.io/library/test                                                                                                                                    0.0s

只有16秒!

我们正在加速,因为我们不再下载所有的Python包。它们由包管理器缓存(本例中为pip)并存储在缓存卷装载中。卷装载提供给运行步骤,以便pip可以重用我们已经下载的包。这发生在任何Docker层缓存之外。

对于更大的需求,增益应该更好。

笔记:

  • 这是实验性Dockerfile语法,应该这样对待。您目前可能不想在生产环境中使用它进行构建。
  • <罢工> BuildKit目前在Docker Compose或其他直接使用Docker API的工具下不起作用。 从1.25.0开始,Docker Compose中现在支持此功能。请参阅如何使用docker-compose启用BuildKit?
  • 目前没有任何用于托管缓存的直接接口。当您执行docker系统修剪-a时,它会被清除。

希望这些功能能够进入Docker进行构建,BuildKit将成为默认设置。如果/当这种情况发生时,我会尝试更新这个答案。

萧晔
2023-03-14

尝试构建一个Dockerfile,其外观如下所示:

FROM my/base

WORKDIR /srv
ADD ./requirements.txt /srv/requirements.txt
RUN pip install -r requirements.txt
ADD . /srv
RUN python setup.py install

ENTRYPOINT ["run_server"]

只要您不对requirements.txt进行任何更改,Docker将在pip安装期间使用缓存,而不管.中的其他代码文件是否已更改。这是一个示例。

这是一个简单的〈代码〉你好,世界 程序:

$ tree
.
├── Dockerfile
├── requirements.txt
└── run.py   

0 directories, 3 file

# Dockerfile

FROM dockerfile/python
WORKDIR /srv
ADD ./requirements.txt /srv/requirements.txt
RUN pip install -r requirements.txt
ADD . /srv
CMD python /srv/run.py

# requirements.txt
pytest==2.3.4

# run.py
print("Hello, World")

docker build的输出:

Step 1 : WORKDIR /srv
---> Running in 22d725d22e10
---> 55768a00fd94
Removing intermediate container 22d725d22e10
Step 2 : ADD ./requirements.txt /srv/requirements.txt
---> 968a7c3a4483
Removing intermediate container 5f4e01f290fd
Step 3 : RUN pip install -r requirements.txt
---> Running in 08188205e92b
Downloading/unpacking pytest==2.3.4 (from -r requirements.txt (line 1))
  Running setup.py (path:/tmp/pip_build_root/pytest/setup.py) egg_info for package pytest
....
Cleaning up...
---> bf5c154b87c9
Removing intermediate container 08188205e92b
Step 4 : ADD . /srv
---> 3002a3a67e72
Removing intermediate container 83defd1851d0
Step 5 : CMD python /srv/run.py
---> Running in 11e69b887341
---> 5c0e7e3726d6
Removing intermediate container 11e69b887341
Successfully built 5c0e7e3726d6

让我们修改run.py:

# run.py
print("Hello, Python")

尝试重新构建,以下是输出:

Sending build context to Docker daemon  5.12 kB
Sending build context to Docker daemon 
Step 0 : FROM dockerfile/python
---> f86d6993fc7b
Step 1 : WORKDIR /srv
---> Using cache
---> 55768a00fd94
Step 2 : ADD ./requirements.txt /srv/requirements.txt
---> Using cache
---> 968a7c3a4483
Step 3 : RUN pip install -r requirements.txt
---> Using cache
---> bf5c154b87c9
Step 4 : ADD . /srv
---> 9cc7508034d6
Removing intermediate container 0d7cf71eb05e
Step 5 : CMD python /srv/run.py
---> Running in f25c21135010
---> 4ffab7bc66c7
Removing intermediate container f25c21135010
Successfully built 4ffab7bc66c7

正如您在上面看到的,这次docker在构建过程中使用缓存。现在,让我们更新requirements.txt

# requirements.txt

pytest==2.3.4
ipython

以下是docker build的输出:

Sending build context to Docker daemon  5.12 kB
Sending build context to Docker daemon 
Step 0 : FROM dockerfile/python
---> f86d6993fc7b
Step 1 : WORKDIR /srv
---> Using cache
---> 55768a00fd94
Step 2 : ADD ./requirements.txt /srv/requirements.txt
---> b6c19f0643b5
Removing intermediate container a4d9cb37dff0
Step 3 : RUN pip install -r requirements.txt
---> Running in 4b7a85a64c33
Downloading/unpacking pytest==2.3.4 (from -r requirements.txt (line 1))
  Running setup.py (path:/tmp/pip_build_root/pytest/setup.py) egg_info for package pytest

Downloading/unpacking ipython (from -r requirements.txt (line 2))
Downloading/unpacking py>=1.4.12 (from pytest==2.3.4->-r requirements.txt (line 1))
  Running setup.py (path:/tmp/pip_build_root/py/setup.py) egg_info for package py

Installing collected packages: pytest, ipython, py
  Running setup.py install for pytest

Installing py.test script to /usr/local/bin
Installing py.test-2.7 script to /usr/local/bin
  Running setup.py install for py

Successfully installed pytest ipython py
Cleaning up...
---> 23a1af3df8ed
Removing intermediate container 4b7a85a64c33
Step 4 : ADD . /srv
---> d8ae270eca35
Removing intermediate container 7f003ebc3179
Step 5 : CMD python /srv/run.py
---> Running in 510359cf9e12
---> e42fc9121a77
Removing intermediate container 510359cf9e12
Successfully built e42fc9121a77

请注意docker在pip安装期间是如何不使用缓存的。如果不起作用,请检查docker版本。

Client version: 1.1.2
Client API version: 1.13
Go version (client): go1.2.1
Git commit (client): d84a070
Server version: 1.1.2
Server API version: 1.13
Go version (server): go1.2.1
Git commit (server): d84a070
 类似资料:
  • 问题内容: 我的Dockerfile就像 每次构建新映像时,都必须重新安装依赖项,这在我所在的地区可能非常慢。 我想到的已安装软件包的一种方法是用较新的图像覆盖该图像,如下所示: 因此,下一次我使用此Dockerfile进行构建时,我/基础已经安装了一些软件包。 但是此解决方案有两个问题: 并非总是可以覆盖基本图像 随着新图像的叠加,基础图像变得越来越大 那么我可以使用什么更好的解决方案来解决这个

  • 问题内容: 我想创建一个docker镜像并正确安装,因此我选择具有这些属性的基础镜像。因此,的第一行如下: 然后下一个命令是 在创建docker映像时创建了以下错误: 如何能够在此docker映像中安装? 问题答案: 如提示: 获取(13:权限被拒绝) 我相信这是由于您的基本形象: https://github.com/SeleniumHQ/docker- selenium/blob/master

  • 我有一个GitHub项目(我正在使用它(我没有创建它)),名为OpenRefine,我想将其封装在Docker映像中,这样其他人就可以从“Docker Hub”中提取该Docker映像,并将OpenRefine安装在他们的基本交互Docker实体上,即。形象 然后我想将它上传到一个我可以与其他人共享的存储库。 如果它有一个名字,而不是一个疯狂的散列值,那就好了。 是不是我用命令创建了一个“dock

  • 据我所知,docker映像在被拉取时被安装到。有没有办法更改此位置,例如更改到像这样的已安装卷?

  • 问题内容: 我正在尝试为Ruby项目构建Docker映像。问题在于该项目具有一些gem依赖项,需要构建本机扩展。我的理解是,我有两种选择: 从已经安装了构建工具的基础映像开始。 使用没有构建工具的基础映像,并在运行之前在Dockerfile中安装构建工具。 在主机上预编译本机扩展,将gem供应商化,然后简单地将生成的包复制到映像中。 1和2似乎要求生成的映像包含构建本机扩展所需的构建工具。出于安全

  • 今天,我需要一个解决方案来避免在修补程序安装的上下文中通过install4j Version6.1.3创建安装目录。我编写了一个安装程序,其中包含一些文件来将它们安装到现有的应用程序安装中。这些文件应该与修补程序安装程序捆绑在一起。在install files操作中,我停用了install运行时选择点,但安装程序仍然创建了包含名为.install4j的文件夹的安装目录。在这个文件夹中只有一些ins