Як уникнути перевстановлення пакетів під час створення зображення Docker для проектів Python?


128

Мій Dockerfile щось подібне

FROM my/base

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

ENTRYPOINT ["run_server"]

Кожен раз, коли я будую новий імідж, залежність потрібно перевстановлювати, що може бути дуже повільним у моєму регіоні.

Один із способів, який я думаю про встановлені cacheпакети, - це перекриття my/baseзображення новими такими зображеннями:

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

Тому наступного разу, коли я будую цей Dockerfile, у моїй / базі вже встановлені деякі пакети.

Але це рішення має дві проблеми:

  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

Чи видаляєте проміжне зображення після того, як закінчили створювати зображення?
Реган

Звичайно, ні, але це не має значення, оскільки, коли я відновлюю зображення, я все ще my/base
базуюсь

Відповіді:


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 використовуватиме кеш під час встановлення pip до тих пір, поки ви не зробите жодних змін у requirements.txt, незалежно від того, .змінили чи ні інші файли коду в . Ось приклад.


Ось проста 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")

Вихід збірки докера:

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

Як ви бачите вище, цього разу докер використовує кеш під час збирання. Тепер оновимо requirements.txt:

# requirements.txt

pytest==2.3.4
ipython

Нижче наведено висновок збірки докера:

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

Зверніть увагу, як докер не використовував кеш під час встановлення pip. Якщо це не працює, перевірте свою версію докера.

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
Це, здається, не працює, оскільки кожен раз, коли докер бачить ADDінструкцію, кеш недійсний.
satoru

1
Я не впевнений, чому це не працює. Але в вимовах.txt (<src> на ADD ./requirements.txt /srv/requirements.txt) немає змін , тоді докер повинен використовувати кеш. Див. Додавання сектона в документ Dockerfile.
nacyot

16
Так, він буде використовувати кеш, якщо вимоги.txt не змінюються. Але якщо вимоги.txt змінюються, то всі вимоги завантажуються. Чи є спосіб, щоб я змонтував об'єм кеш-пам'ятки для завантаження з кеша?
Джиту

7
Ключовим моментом у цій відповіді є те, що ви додаєте вимоги.txt ( ADD requirements.txt /srvперед запуском pip ( RUN pip install -r requirements.txt) та додайте всі інші файли після запуску pip. Таким чином, вони мають бути в такому порядку: (1) ADD requirements.txt /srv; (2) RUN pip install -r requirements.txt; ( 3)ADD . /srv
engelen

2
Зверніть увагу, що це не працює при використанні COPY замість ADD
veuncent

29

Щоб мінімізувати мережеву активність, ви можете вказати pip на кеш-каталог на вашій хост-машині.

Запустіть контейнер докера зі зв'язуванням каталогу кеш-файлів вашого хоста, встановленим у каталозі кеш-файлів кеш-файлу вашого контейнера. 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
Не те, що шукав ОП у своєму випадку використання, але якщо ви створюєте сервер для збирання, це чудова ідея
Оден

2
Це здається рецептом проблем, зокрема пропозицією вказати на кеш-пам'ять хосту за замовчуванням. Ви потенційно змішуєте специфічні для арки пакети.
Джакомо Лакава

@GiacomoLacava дякую, це дуже вдалий момент. Я відкоригував свою відповідь і видалив частину, яка запропонувала використовувати повторно використовувати кеш-каталог каталогу хостів.
Якуб Кукул

24

Я розумію, на це питання вже є кілька популярних відповідей. Але є більш новий спосіб кешування файлів для менеджерів пакетів. Я думаю, що це може стати гарною відповіддю в майбутньому, коли 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

Типовий приклад 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

З увімкненим BuildKit за допомогою DOCKER_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(наприклад, додавши новий рядок між двома пакетами), щоб примусити вимкнути кеш і запустити знову:

$ 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 Compose або іншими інструментами, які безпосередньо використовують Docker API на даний момент. Зараз це підтримується в Docker Compose станом на 1.25.0. Див. Як увімкнути BuildKit за допомогою docker-compose?
  • На даний момент немає жодного прямого інтерфейсу для кешованого кешу. Він очищається, коли ви робите docker system prune -a.

Сподіваємось, ці функції перетворять його в Docker для побудови, а BuildKit стане стандартним. Якщо / коли це станеться, я спробую оновити цю відповідь.


Я можу підтвердити, що це рішення працює дуже добре. Моя збірка знизилася з понад хвилини до всього 2,2 секунди. Дякую @ andy-shinn
Kwuite

2
Тепер також Докер-Compose: stackoverflow.com/questions/58592259 / ...
Rexcirus

Примітка: Якщо ви використовуєте SUDO для запуску докера, вам, мабуть, потрібно зробити: sudo DOCKER_BUILDKIT = 1 ...
Vinícius M

Я отримую цю помилку: - не вдалося вирішити з frontend dockerfile.v0: не вдалося створити визначення LLB: рядок помилки розбору Dockerfile 10: Невідомий прапор: mount
Mayur Dangar

Це звучить так, ніби ви пропустили коментар у верхній частині Dockerfileверсії або версія Docker занадто стара. Я створив би нове запитання з усією вашою інформацією про налагодження.
Енді Шінн

-10

Я виявив, що кращий спосіб - просто додати каталог сайтів пакунків Python як об'єм.

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

Таким чином я можу просто встановити нову бібліотеку без необхідності повного відновлення.

EDIT : Ігноруйте цю відповідь, відповідь jkukul вище працював на мене. Моя мета полягала в кешуванні папки сайтів-пакунків . Це виглядало б приблизно так:

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

Хоча кешування папки завантаження набагато чистіше. Це також кешує колеса, тому він належним чином виконує завдання.


2
І що відбувається, коли ви намагаєтеся створити цей докер-файл на іншій машині. Це не стійке рішення.
Аарон Мак-Міллін

По-справжньому розгублений, це виявилося баггі, і я насправді не знав, чому. Чи можете ви детальніше розповісти.
jaywhy13

3
Ваше зображення докера залежить від стану хост-системи. Це втрачає більшість утиліт докера. Все, що потрібно для зображення, має бути встановлено в ньому. використовуйте Dockerfile, щоб встановити всі залежності. Якщо ви хочете уникнути повторного завантаження пакетів щоразу, коли ви збираєте відповідь з jkukul, щоб змонтувати кеш-пам'ять, це шлях.
Аарон Макміллін

2
Лампочка просто згасла завдяки. Насправді я намагався змонтувати каталог сайтів пакунків із віртуального комп'ютера, а не хоста. Досить недогляд. Думаю, духом я намагався зробити те саме, що запропонував jkulkul. Дякую за ясність!
jaywhy13

@AaronMcMillin Він насправді не залежить від шляху до господаря. Він монтує сайти-пакети в контейнер до анонімного тома. Все ж погана ідея, хоча
ruohola
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.