PythonプロジェクトのDockerイメージをビルドするときにパッケージの再インストールを回避するにはどうすればよいですか?


128

私のDockerfileは次のようなものです

FROM my/base

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

ENTRYPOINT ["run_server"]

新しいイメージをビルドするたびに、依存関係を再インストールする必要があるため、私の地域では非常に遅くなる可能性があります。

cacheインストールされたパッケージについて私が考える1つの方法は、次のmy/baseような新しいイメージでイメージをオーバーライドすることです。

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

したがって、このDockerfileを使用して次回ビルドするとき、my / baseにはすでにいくつかのパッケージがインストールされています。

しかし、このソリューションには2つの問題があります。

  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

イメージの構築が完了したら、中間イメージを削除しますか?
リーガン2014

もちろん、そうではありませんが、イメージを再構築するとき、私はまだオリジナルに基づいているので、関係ありませんmy/base
satoru

回答:


139

次のような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"]

Dockerはrequirements.txt、他のコードファイル.が変更されたかどうかに関係なく、に変更を加えない限り、pipのインストール中にキャッシュを使用します。ここに例があります。


簡単な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")

Dockerビルドの出力:

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

2
これは機能しないようです。DockerがADD命令を見るたびに、キャッシュが無効化されるためです。
satoru 2014

1
なぜ動かないのかわかりません。しかし、requirements.txt(<src> on ADD ./requirements.txt /srv/requirements.txt)に変更はないため、Dockerはキャッシュを使用する必要があります。Dockerfileドキュメントにセクションを追加するをご覧ください。
nacyot 2014

16
はい、requirements.txtが変更されない場合はキャッシュを使用します。しかし、requirements.txtが変更されると、すべての要件がダウンロードされます。キャッシュからロードするために、pipキャッシュボリュームをDockerコンテナーにマウントする方法はありますか?
ジトゥ

7
この回答の要点は、ADD requirements.txt /srvpipis(RUN pip install -r requirements.txt)を実行する前にrequirements.txtを追加し、pipを実行した後に他のすべてのファイルを追加することです。したがって、それらは次の順序である必要があります:(1)ADD requirements.txt /srv;(2)RUN pip install -r requirements.txt;( 3)ADD . /srv
engelen 2017

2
ADDの代わりにCOPYを使用する場合、これは機能しないことに注意してください
veuncent

29

ネットワークアクティビティを最小限に抑えるにはpip、ホストマシンのキャッシュディレクトリを指定します。

ホストのpipキャッシュディレクトリバインドをコンテナのpipキャッシュディレクトリにマウントして、Dockerコンテナーを実行します。docker runコマンドは次のようになります。

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

次に、Dockerfileに要件の一部としてインストールします ENTRYPOINTコマンドではCMDなくステートメント(またはステートメント)のRUN。(コメントで指摘されているように)イメージのビルド中(RUNステートメントの実行時)にはマウントを使用できないため、これは重要です。Dockerファイルは次のようになります。

FROM my/base

ADD . /srv

ENTRYPOINT ["sh", "-c", "pip install -r requirements.txt && python setup.py install && run_server"]

4
OPは、彼のユースケースで探していたが、あなたが、ビルドサーバーを作成する場合、これは素晴らしいアイデアではありません何か
おでん

2
これは、問題、特にデフォルトのホストキャッシュを指すようにする提案のレシピのようです。アーチ固有のパッケージが混在している可能性があります。
Giacomo Lacava

@GiacomoLacavaありがとう、それは非常に良い点です。私は自分の答えを調整し、ホストのキャッシュディレクトリの再利用を提案する部分を削除しました。
Jakub Kukul

24

この質問にはすでにいくつかの人気のある回答があることを理解しています。ただし、パッケージマネージャーのファイルをキャッシュする新しい方法があります。BuildKitがより標準になるとき、それは将来的に良い答えになると思います。

Docker 18.09 以降BuildKitの実験的なサポートがあります。BuildKitは、外部ボリュームRUN段階的にマウントする実験的なサポートを含む、Dockerfileのいくつかの新機能のサポートを追加します。これにより、などのキャッシュを作成できます$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

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

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、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秒。最初のビルドに似ています。

requirements.txt(2つのパッケージの間に新しい行を追加するなど)に小さな変更を加えて、キャッシュを強制的に無効にし、再度実行します。

$ 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レイヤーのキャッシュの外側で発生します。

利益は大きいほどはるかに良くなるはずです requirements.txtます。

ノート:

  • これは実験的なDockerfile構文であり、そのように扱う必要があります。現在、これを本番環境で構築したくない場合があります。
  • BuildKitのものは、現時点ではDocker APIを直接使用するDocker Composeまたはその他のツールでは機能しません。1.25.0以降、Docker Composeでこれがサポートされるようになりました。参照してください。あなたがドッキングウィンドウ・コンでBuildKitを有効にするにはどうすればよいですか?
  • 現時点では、キャッシュを管理するための直接的なインターフェースはありません。を実行すると消去されますdocker system prune -a

うまくいけば、これらの機能がビルド用のDockerに組み込まれ、BuildKitがデフォルトになります。もしそれが起こったら、私はこの答えを更新しようとします。


このソリューションが非常にうまく機能することを確認できます。私のビルドは1分以上からわずか2.2秒になりました。@ andy-shinnに感謝します。
Kwuite、

2
今もドッカー-作曲:stackoverflow.com/questions/58592259/...
Rexcirus

注:あなたがドッキングウィンドウを実行するSUDOを使用している場合、あなたはおそらく実行する必要があります。sudo DOCKER_BUILDKIT = 1 ...
ヴィニシウスM

このエラーが発生しました:-フロントエンドdockerfile.v0で解決できませんでした:LLB定義の作成に失敗しました:Dockerfile解析エラー行10:不明なフラグ:マウント
Mayur Dangar

上部のコメントを見逃したDockerfileか、Dockerのバージョンが古すぎるようです。すべてのデバッグ情報を使用して新しい質問を作成します。
Andy Shinn

-10

より良い方法は、Pythonのサイトパッケージディレクトリをボリュームとして追加することです。

services:
    web:
        build: .
        command: python manage.py runserver 0.0.0.0:8000
        volumes:
            - .:/code
            -  /usr/local/lib/python2.7/site-packages/

このようにして、完全な再構築を行わなくても、新しいライブラリをpipインストールするだけで済みます。

編集:この答えを無視してください、上のjkukulの答えは私のために働きました。私の目的は、site-packagesフォルダーをキャッシュすることでした。それは次のようになります。

volumes:
   - .:/code
   - ./cached-packages:/usr/local/lib/python2.7/site-packages/

ただし、ダウンロードフォルダーのキャッシュはかなりクリーンです。これはホイールもキャッシュするため、タスクを適切に実行します。


2
そして、別のマシンでこのdockerfileをビルドしようとするとどうなりますか。これは持続可能な解決策ではありません。
アーロンマクミリン2017年

完全に混乱しましたが、これはバグのあるものであることが判明し、実際にその理由がわかりませんでした。詳細をもう少し教えてください。
jaywhy13 2017年

3
Dockerイメージは、ホストシステムからの状態に依存しています。これにより、Dockerのユーティリティのほとんどが無効になります。イメージに必要なものはすべてインストールする必要があります。Dockerfileを使用してすべての依存関係をインストールします。jkukulから回答をビルドするたびにパッケージを再ダウンロードしないようにしたい場合は、pipキャッシュをマウントするのがよいでしょう。
アーロンマクミリン2017年

2
おかげで電球が消えました。実際には、ホストではなくVMからsite-packagesディレクトリをマウントしようとしていました。かなり見落とし。私は精神的に私がjkulkulが提案したのと同じことをしようとしていたと思います。
jaywhy13 2017年

@AaronMcMillin彼は実際にはホスト上のパスに依存していません。彼はコンテナー内のサイトパッケージを匿名ボリュームにマウントしています。それでも悪い考え
ruohola
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.