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

sat*_*oru 103 python docker

我的Dockerfile就像

FROM my/base

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

ENTRYPOINT ["run_server"]
Run Code Online (Sandbox Code Playgroud)

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

我想到的cache已经安装的软件包的一种方法是my/base用更新的图像覆盖图像,如下所示:

docker build -t new_image_1 .
docker tag new_image_1 my/base
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

编辑##:

有关我机器上的泊坞窗的一些信息:

?  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
Run Code Online (Sandbox Code Playgroud)

nac*_*yot 119

尝试使用下面的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"]
Run Code Online (Sandbox Code Playgroud)

如果.(您的项目)有一些更改,docker会pip install使用缓存跳过行.

pip install在编辑requirements.txt文件时,Docker仅在构建时运行.


我写简单的Hello, World!程序.

$ 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")
Run Code Online (Sandbox Code Playgroud)

以下是输出.

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 Code Online (Sandbox Code Playgroud)

我只更新run.py并尝试再次构建.

# run.py
print("Hello, Python")
Run Code Online (Sandbox Code Playgroud)

以下是输出.

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
Run Code Online (Sandbox Code Playgroud)

如上所示,docker使用构建缓存.我这次更新了requirements.txt.

# requirements.txt

pytest==2.3.4
ipython
Run Code Online (Sandbox Code Playgroud)

以下是输出.

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
Run Code Online (Sandbox Code Playgroud)

而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
Run Code Online (Sandbox Code Playgroud)

  • 是的,如果requirements.txt没有改变,它将使用缓存.但是,如果requirements.txt发生更改,则会下载所有要求.有什么办法可以将pip缓存卷挂载到docker容器中以从缓存中加载? (11认同)
  • 请注意,这在使用 COPY 而不是 ADD 时不起作用 (5认同)
  • 这个答案的关键是在运行pip之前添加requirements.txt(`ADD requirements.txt/srv`(`RUN pip install -r requirements.txt`),并在运行pip之后添加所有其他文件*因此,它们应按以下顺序排列:(1)`ADD requirements.txt/srv`;(2)`RUN pip install -r requirements.txt`;(3)`ADD./ srv` (4认同)
  • 这似乎不起作用,因为每当docker看到`ADD`指令时,缓存就会失效. (2认同)
  • 我不确定为什么它不起作用。但是requirements.txt(`ADD ./requirements.txt /srv/requirements.txt`上的<src>)没有任何变化,那么docker必须使用缓存。请参阅 Dockerfile 文档中的 [add seciton](https://docs.docker.com/reference/builder/#add)。 (2认同)
  • @veuncent 等等什么?为什么它只适用于 ADD?! (2认同)

Jak*_*kul 25

要最小化网络活动,您可以指向pip主机上的缓存目录.

运行docker容器,将主机的pip缓存目录bind挂载到容器的pip缓存目录中.docker run命令应该如下所示:

docker run -v $HOME/.cache/pip/:/root/.cache/pip image_1
Run Code Online (Sandbox Code Playgroud)

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

FROM my/base

ADD . /srv

ENTRYPOINT ["sh", "-c", "pip install -r requirements.txt && python setup.py install && run_server"]
Run Code Online (Sandbox Code Playgroud)

可能最好是主机系统的默认pip目录将用作缓存(例如$HOME/.cache/pip/在Linux或$HOME/Library/Caches/pip/OSX上),就像我在示例docker run命令中建议的那样.

  • 不是OP在他的用例中寻找的东西,但如果你制作一个构建服务器这是一个好主意 (4认同)
  • 这似乎是解决问题的方法,特别是指向默认主机缓存的建议。您可能会混合特定于 arch 的包。 (2认同)

And*_*inn 11

我知道这个问题已经有一些流行的答案。但是,有一种更新的方式可以为程序包管理器缓存文件。我认为,当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
Run Code Online (Sandbox Code Playgroud)

Python的典型示例Dockerfile如下所示:

FROM python:3.7
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install -r requirements.txt
COPY . /usr/src/app
Run Code Online (Sandbox Code Playgroud)

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

$ 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 Code Online (Sandbox Code Playgroud)

现在,让我们添加实验性标头并修改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
Run Code Online (Sandbox Code Playgroud)

继续并立即进行另一个构建。它应该花费相同的时间。但这一次是在新的缓存安装中缓存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
Run Code Online (Sandbox Code Playgroud)

大约60秒。类似于我们的第一个版本。

对进行一些小的更改requirements.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
Run Code Online (Sandbox Code Playgroud)

仅约16秒!

由于不再下载所有Python软件包,因此我们获得了这种加速。它们由程序包管理器缓存(pip在这种情况下),并存储在缓存卷装载中。卷安装已提供给运行步骤,以便pip可以重复使用我们已经下载的软件包。这发生在任何Docker层缓存之外

大点的收益应该更好requirements.txt

笔记:

  • 这是实验性的Dockerfile语法,应这样对待。您现在可能不想在生产中使用此版本。
  • 目前,BuildKit的东西在Docker Compose或其他直接使用Docker API的工具下不起作用。
  • 目前没有任何直接接口可用于管理缓存。当您执行一次操作时,它将被清除docker system prune -a

希望这些功能可以使其融入Docker进行构建,并且BuildKit将成为默认设置。如果/当发生这种情况时,我将尝试更新此答案。

  • 我可以确认这个解决方案效果很好。我的构建时间从一分多钟缩短到了 2.2 秒。谢谢@andy-shinn。 (2认同)
  • 现在还有 Docker-Compose:/sf/ask/4101458161/ (2认同)
  • 听起来您错过了“Dockerfile”顶部的注释,或者 Docker 版本太旧了。我将使用您的所有调试信息创建一个新问题。 (2认同)