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.
Учим немецкий
Герц (Гц) - это единица измерения частоты, сколько раз в секунду происходит какое-то событие. Названа в честь Генриха Герца ( He...
Привет с Большого Бодуна
Попытались тут посмотреть новую "Дюну", и вообще не поняли, в чем соль. Не то чтобы я целиком и полностью понял первый фильм, но...
Машина времени и пространства
Возможно, вы не задумывались, но машина времени должна перемещать человека не только во времени, но и в пространстве, учитывая п...
Старомодный сайт
Хочу, чтобы все сайты были такими. Классические три колонки, табличная верстка, подчеркнутые ссылки, никаких развесистых шрифтов...
Вскрытие покажет. Записки увлеченного судмедэксперта
Прочитал на днях книгу про работу судмедэксперта. Местами хорошо и познавательно (например, про то, как настоящая работа отлич...
Chippendales
Однажды в детстве я наткнулся на передачу про нечто под названием Chippendales. Я тогда особо не вникал, просто сразу захотел ее...