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.
Colon
В английском языке слово colon используется и для обозначения знака "двоеточие" : , и для обозначения толстого кишечника. Ка...
Spense.app v0.2
Привет! Я закончил работу над очередной версией Spense с кучей улучшений и по традиции делюсь самым интересным. Страница со сче...
Не прокатило
Звонок программисту (П) от рекрутера (HR). HR: Ало, привет, я Маша из агентства "Олег и таланты", у меня есть классная вакансия...
Немножко из книги Гоггинса
Читаю сейчас первую книгу Дэвида Гоггинса Can't Hurt Me , узнал пару интересных фактов. 💭 "Морские котики" - часто встречающее...
Spense.app "под капотом". Код.
Пока Spense v0.2 в разработке, хочу рассказать о внутренней организации приложения с технической точки зрения. Статья эта в осно...
Airplane! (1980)
Посмотрел вчера фильм Airplane! (1980), это одна из тех старых комедий, где люди каламбурят на серьезных щах, создавая абсурдн...