Docker Buildkit: Правильное использование --mount=type=cache
TL;DR Содержимое каталогов, смонтированных через
--mount=type=cache
, не сохраняется в docker-образе, поэтому кэшировать надо не целевые каталоги, а промежуточные.
В dockerfile:1.3
появилась возможность монтировать файловые системы во время построения образа, в том числе и в целях кэширования: можно кэшировать скачанные пакеты или промежуточные артефакты компиляции.
Например, пакет uwsgi
каждый раз компилируется при установке, и это время хочется сократить, закэшировав весь каталог с пакетами:
# syntax=docker/dockerfile:1.3
FROM python:3.10
RUN mkdir /pip-packages
RUN --mount=type=cache,target=/pip-packages \
pip install --target=/pip-packages uwsgi
> docker build -t pip-cache -f Dockerfile.pip .
# ...
[+] Building 14.6s (7/7) FINISHED
Выглядит, что все прошло успешно, но целевой каталог пуст:
> docker run -it --rm pip-cache ls -l /pip-packages
total 0
Что-то явно пошло не так. Во время сборки было видно, что uWSGI компилируется и устанавливается. Можно даже это проверить, добавив ls
в процесс сборки:
RUN --mount=type=cache,target=/pip-packages \
pip install --target=/pip-packages uwsgi \
&& ls -1 /pip-packages
> docker build -t pip-cache --progress=plain -f Dockerfile.pip .
<...>
#6 12.48 Successfully installed uwsgi-2.0.20
<...>
#6 12.91 __pycache__
#6 12.91 bin
#6 12.91 uWSGI-2.0.20.dist-info
#6 12.91 uwsgidecorators.py
#6 DONE 13.0s
<...>
Всё на месте. Но нет, в конечном образе снова пусто:
> docker run -it --rm pip-cache ls -l /pip-packages
total 0
А дело в том, что каталог /pip-packages
, находящийся внутри образа, и каталог, который указан в RUN --mount=type=cache,target=<dirname>
, разные. Попробуем что-нибудь положить в него заранее и посмотрим, как меняется его содержимое в процессе сборки:
RUN mkdir /pip-packages \
&& touch /pip-packages/foo \
&& ls -1 /pip-packages
RUN --mount=type=cache,target=/pip-packages \
ls -1 /pip-packages \
&& pip install --target=/pip-packages uwsgi \
&& ls -1 /pip-packages
RUN ls -1 /pip-packages
> docker build -t pip-cache --progress=plain -f Dockerfile.pip-track .
<...>
#5 [stage-0 2/4] RUN mkdir /pip-packages
&& touch /pip-packages/foo
&& ls -1 /pip-packages
#5 sha256:fb542<...>
#5 0.211 foo 👈1️⃣
#5 DONE 0.2s
#6 [stage-0 3/4] RUN --mount=type=cache,target=/pip-packages
ls -1 /pip-packages
&& pip install --target=/pip-packages uwsgi
&& ls -1 /pip-packages
#6 sha256:10ed6<...>
#6 0.292 __pycache__ 👈2️⃣
#6 0.292 bin
#6 0.292 uWSGI-2.0.20.dist-info
#6 0.292 uwsgidecorators.py
#6 2.802 Collecting uwsgi 🤔3️⃣
#6 3.189 Downloading uwsgi-2.0.20.tar.gz (804 kB)
#6 4.400 Building wheels for collected packages: uwsgi
<...>
#6 13.34 __pycache__ 👈4️⃣
#6 13.34 bin
#6 13.34 uWSGI-2.0.20.dist-info
#6 13.34 uwsgidecorators.py
#6 DONE 13.4s
#7 [stage-0 4/4] RUN ls -1 /pip-packages
#7 sha256:fb6f4<...>
#7 0.227 foo 👈5️⃣
#7 DONE 0.2s
<...>
- 1️⃣ файл
foo
успешно создан - 2️⃣ примонтировался каталог с результатами предыдущего
docker build
, и файлаfoo
там нет - 3️⃣ uWSGI снова скачивается, компилируется и устанавливается
- 4️⃣ в каталоге появилась обновленная сборка uWSGI
- 5️⃣ в каталоге остался только файл
foo
Это означает, что --mount=type=cache
работает только в контексте одной инструкции RUN
, заменяя директорию, созданную внутри образа RUN mkdir /pip-packages
, и затем возвращая ее обратно. При этом кэширование оказалось неэффективным, потому что pip
заново установил uWSGI с полной компиляцией.
В данном случае корректно было бы кэшировать не целевую директорию, а /root/.cache
, в которую pip
складывает все артефакты:
RUN --mount=type=cache,target=/root/.cache \
pip install --target=/pip-packages uwsgi
> docker build -t pip-cache -f Dockerfile.pip-right .
> docker run -it --rm pip-cache ls -1 /pip-packages
__pycache__
bin
uWSGI-2.0.20.dist-info
uwsgidecorators.py
Теперь все на месте, установленные пакеты никуда не делись.
Проверим эффективность кэширования, добавив пакет requests
:
RUN --mount=type=cache,target=/root/.cache \
pip install --target=/pip-packages uwsgi requests
👆
> docker build -t pip-cache --progress=plain -f Dockerfile.pip-right .
<...>
#6 6.297 Collecting uwsgi
#6 6.297 Using cached uWSGI-<...>.whl 👈
#6 6.561 Collecting requests
#6 6.980 Downloading requests-2.27.1-py2.py3-none-any.whl (63 kB)
<...>
pip
взял заранее собранный wheel-файл из /root/.cache
и установил из него готовый к использованию пакет.
Все исходники доступны на GitHub.
Всегда жалуйтесь
На днях решили заказать на обед грузинской еды в Bolt Food. Нашли неплохой ресторан, понабрали на 40 евро, заказали, ждем. Доста...
Старомодный сайт
Хочу, чтобы все сайты были такими. Классические три колонки, табличная верстка, подчеркнутые ссылки, никаких развесистых шрифтов...
Трагедия Ханса и Нины Райзер
Жила-была Нина Шаранова, проживала она в Санкт-Петербурге и работала акушером-гинекологом. В 1998 году ее попросили побыть перев...
Немножко из книги Гоггинса
Читаю сейчас первую книгу Дэвида Гоггинса Can't Hurt Me , узнал пару интересных фактов. 💭 "Морские котики" - часто встречающее...
The Slappable Jerk
Давно хотел порекомендовать ютуб-канал The Slappable Jerk (буквально "Придурок, которому можно дать по щам"). Там чувак очень ...
Абстракции и наследование в Си - стреляем по ногам красиво
TL;DR Иногда нет-нет да и хочется что-нибудь абстрагировать и обобщить в коде на Си. К примеру, хочешь ты принтануть содержимо...