Mon Dockerfile est quelque chose comme
FROM my/base
ADD . /srv
RUN pip install -r requirements.txt
RUN python setup.py install
ENTRYPOINT ["run_server"]
Chaque fois que je construis une nouvelle image, des dépendances doivent être réinstallées, ce qui peut être très lent dans ma région.
Une façon dont je pense à cache
paquets qui ont été installés est de remplacer le my/base
image avec de nouvelles images comme celle-ci:
docker build -t new_image_1 .
docker tag new_image_1 my/base
Donc, la prochaine fois que je compile avec ce fichier Dockerfile, des paquets sont déjà installés sur mon/base.
Mais cette solution a deux problèmes:
Alors, quelle meilleure solution pourrais-je utiliser pour résoudre ce problème?
Quelques informations sur le menu fixe sur ma machine:
☁ 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
Essayez de construire avec Dockerfile ci-dessous.
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"]
S'il y a des changements sur .
_ (votre projet), docker skip pip install
ligne en utilisant cache.
Docker ne lance que pip install
sur la construction lorsque vous modifiez le fichier requirements.txt.
J'écris simple Hello, World!
programme.
$ 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")
Ci-dessous est la sortie.
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
Je ne mets à jour que run.py et tente de construire à nouveau.
# run.py
print("Hello, Python")
Ci-dessous est la sortie.
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
Comme vous pouvez le voir ci-dessus, docker utilise le cache de construction. Et je mets à jour les exigences.txt cette fois.
# requirements.txt
pytest==2.3.4
ipython
Ci-dessous est la sortie.
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
Et docker n'utilise pas le cache de construction. Si cela ne fonctionne pas, vérifiez votre version de 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
Pour minimiser l'activité du réseau, vous pouvez pointer pip
vers un répertoire de cache sur votre ordinateur hôte.
Exécutez votre conteneur docker avec la liaison de répertoire de cache pip de votre hôte montée dans le répertoire de cache cache de votre conteneur. docker run
La commande devrait ressembler à ceci:
docker run -v $HOME/.cache/pip/:/root/.cache/pip image_1
Ensuite, dans votre fichier Docker, installez vos exigences en tant qu’élément de l’instruction ENTRYPOINT
(ou de l’instruction CMD
) au lieu d’une commande RUN
. Ceci est important car (comme indiqué dans les commentaires), le montage n'est pas disponible pendant la création de l'image (lorsque les instructions RUN
sont exécutées). Le fichier Docker devrait ressembler à ceci:
FROM my/base
ADD . /srv
ENTRYPOINT ["sh", "-c", "pip install -r requirements.txt && python setup.py install && run_server"]
Il est probablement préférable que le répertoire pip par défaut du système hôte soit utilisé comme cache (par exemple, $HOME/.cache/pip/
sur Linux ou $HOME/Library/Caches/pip/
sur OSX), comme je l’ai suggéré dans l’exemple docker run
commande.
Je comprends que cette question a déjà des réponses populaires. Mais il existe un moyen plus récent de mettre en cache les fichiers pour les gestionnaires de paquets. Je pense que cela pourrait être une bonne réponse à l'avenir lorsque BuildKit deviendra plus standard.
Depuis Docker 18.09, il existe un support expérimental pour BuildKit . BuildKit ajoute la prise en charge de certaines nouvelles fonctionnalités du fichier Dockerfile, notamment prise en charge expérimentale du montage de volumes externes en RUN
étapes. Cela nous permet de créer des caches pour des choses comme $HOME/.cache/pip/
.
Nous utiliserons le fichier requirements.txt
Suivant comme exemple:
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
Un exemple typique Python Dockerfile
pourrait ressembler à ceci:
FROM python:3.7
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install -r requirements.txt
COPY . /usr/src/app
Avec BuildKit activé à l'aide de la variable d'environnement DOCKER_BUILDKIT
, Nous pouvons construire l'étape pip
non mise en cache en environ 65 secondes:
$ DOCKER_BUILDKIT=1 $ time d 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
Ajoutons maintenant l'en-tête expérimental et modifions l'étape RUN
pour mettre en cache les packages 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
Allez-y et faites une autre construction maintenant. Cela devrait prendre le même temps. Mais cette fois, il met en cache les packages Python dans notre nouveau montage en cache:
$ 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
Environ 60 secondes. Semblable à notre première construction.
Modifiez légèrement le paramètre requirements.txt
(Par exemple, en ajoutant une nouvelle ligne entre deux packages) pour forcer l'invalidation de la mémoire cache et relancez l'exécution:
$ 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
Seulement environ 16 secondes!
Nous obtenons cette accélération car nous ne téléchargeons plus tous les Python paquets. Ils ont été mis en cache par le gestionnaire de paquets (pip
dans ce cas) et stockés dans un montage de volume de cache Le montage du volume est fourni à l'étape d'exécution afin que pip
puisse réutiliser nos packages déjà téléchargés. Ceci se produit en dehors de la mise en cache de la couche Docker .
Les gains devraient être bien meilleurs avec des valeurs plus grandes requirements.txt
.
Remarques:
docker system Prune -a
.Espérons que ces fonctionnalités seront intégrées à Docker pour la construction et que BuildKit devienne la valeur par défaut. Si/quand cela se produit, je vais essayer de mettre à jour cette réponse.