Alpine Linux и PyInstaller
Это короткая история про то, как я захотел упаковать небольшое приложение на Python в бинарный файл, а затем сделать из этого такой же небольшой образ Docker.
А когда говорят “небольшой образ” обычно подразумевают, что первой строкой в Dockerfile будет “FROM alpine”. Но вот загвоздка - Alpine Linux использует musl, а PyInstaller использует glibc и если просто взять и собрать бинарный пакет, то вы получите ожидаемый при таких условиях результат. И что же делать? Вот именно это я и хочу рассказать.
TL;DR
Один хороший человек, Mike Thornton aka six8 все сделал за нас и собрал образ, с помощью которого можно довольно просто получить из Python-файла файл исполняемый. Так что, если вам нужно один раз собрать и забыть, то можете воспользоваться его образом и дальше не читать.
Но я хочу свой Dockerfile
Поэтому я нашел магию, которую Майк использовал для создания Alpine-совместимого бинарного файла. Нам интересен вот этот отрывок из его Dockerfile:
RUN apk add --update \
binutils python3 python3-dev git gcc musl-dev libc-dev libffi-dev zlib-dev && \
# Build bootloader for alpine
git clone https://github.com/pyinstaller/pyinstaller.git /tmp/pyinstaller \
&& cd /tmp/pyinstaller/bootloader \
&& CFLAGS="-Wno-stringop-overflow" python3 ./waf configure --no-lsb all \
&& pip3 install .. \
&& rm -Rf /tmp/pyinstaller
Здесь устанавливается все, чтобы собрать свой PyInstaller с загрузчиком для Alpine Linux, собирается и ставится пакет, удаляется временная директория. После этого мы можем, используя модифицированный PyInstaller, собрать наш бинарный файл. Как-то так:
RUN pip3 install -r requirements.txt && \
pyinstaller \
--noconfirm \
--onefile \
--log-level DEBUG \
--clean \
my_app.py
Собранный файл мы сможем найти в поддиректории dist.
Зачем?
Docker прочно ассоциируется в моем сознании со словосочетанием “Microservice Architecture” и 100Gb “микросервис” рвет в клочья все мои шаблоны. Я стараюсь делать свои образы как можно меньше. Может кому-то проще будет вникнуть на живом примере. Вот наглядный пример, где я запаковал Prometheus Jenkins Exporter с помощью техники, описанной в этой заметке. В итоге я сократил размер конечного образа более, чем в шесть раз, по сравнению с оригинальным.