Подтвердить что ты не робот

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

My 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

Итак, в следующий раз, когда я создам этот файл 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
4b9b3361

Ответ 1

Попробуйте построить с помощью ниже 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"]

Если в . (ваш проект) есть некоторые изменения, пропустите skip pip install с помощью кеша.

Docker запускает pip install при сборке при редактировании файла требований. 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

Как вы можете видеть выше, докеры используют кеш сборки. И я обновляю require.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

И докер не использует кеш сборки. Если это не сработает, проверьте версию докеры.

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

Чтобы минимизировать сетевую активность, вы можете указать pip на каталог кэша на вашем хост-компьютере.

Запустите ваш докер-контейнер, установив привязку каталога кэша пипов вашего хоста в каталог кэша пипов контейнера. Команда docker run должна выглядеть так:

docker run -v $HOME/.cache/pip/:/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"]

Вероятно, будет лучше, если в качестве кэша будет использоваться каталог pip по умолчанию для хост-системы (например, $HOME/.cache/pip/ в Linux или $HOME/Library/Caches/pip/ в OSX), как я и предлагал в примере команды docker run.

Ответ 3

Я понимаю, что на этот вопрос уже есть некоторые популярные ответы. Но есть более новый способ кеширования файлов для менеджеров пакетов. Я думаю, что это может быть хорошим ответом в будущем, когда 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:[email protected]: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:[email protected]: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:[email protected]: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:[email protected]: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:[email protected]: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 system prune -a.

Надеемся, что эти функции превратятся в Docker для сборки, а BuildKit станет по умолчанию. Если/когда это произойдет, я постараюсь обновить этот ответ.

Ответ 4

Я обнаружил, что лучший способ - просто добавить каталог сайтов пакетов Python в качестве тома.

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

Таким образом, я могу просто установить новые библиотеки, не требуя полной перестройки.

РЕДАКТИРОВАТЬ: проигнорируйте этот ответ, ответ jkukul выше работал у меня. Моей целью было кэшировать папку сайт-пакеты. Это выглядело бы скорее как:

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

Кэширование папки загрузки все же является более чистым. Это также кэширует колеса, поэтому он правильно выполняет задачу.