- PVSM.RU - https://www.pvsm.ru -

Alpine собирает Docker билды под Python в 50 раз медленней, а образы в 2 раза тяжелей

Alpine собирает Docker билды под Python в 50 раз медленней, а образы в 2 раза тяжелей - 1

Alpine Linux — часто рекомендованный как базовый образ для Docker`а. Вам говорят, что использование Alpine сделает ваши билды меньше, а процесс сборки быстрей.

Но если вы используете Alpine Linux для Python приложений, то он:

  • Делает ваши билды намного медленней
  • Делает ваши образы больше
  • Тратит ваше время
  • И в итоге может стать причиной ошибок в рантайме


Давайте рассмотрим почему же Alpine рекомендуют, но почему вам все же не стоит использовать его в месте с Python.

Почему люди рекомендуют Alpine?

Давайте предположим, что нам необходим gcc как часть нашего образа и мы хотим сравнить Alpine Linux vs Ubuntu 18.04, по скорости сборки и конечному размеру образа.

Для начала, скачаем два образа и сравним их размер:

$ docker pull --quiet ubuntu:18.04
docker.io/library/ubuntu:18.04
$ docker pull --quiet alpine
docker.io/library/alpine:latest
$ docker image ls ubuntu:18.04
REPOSITORY          TAG        IMAGE ID         SIZE
ubuntu              18.04      ccc6e87d482b     64.2MB
$ docker image ls alpine
REPOSITORY          TAG        IMAGE ID         SIZE
alpine              latest     e7d92cdc71fe     5.59MB

Как вы видите, базовый образ для Alpine намного меньше. Давайте теперь попробуем установить gcc и начнем с Ubuntu:

FROM ubuntu:18.04
RUN apt-get update && 
    apt-get install --no-install-recommends -y gcc && 
    apt-get clean && rm -rf /var/lib/apt/lists/*

Написание идеальных Dockerfile выходит за рамки этой статьи

Замерим скорость сборки:

$ time docker build -t ubuntu-gcc -f Dockerfile.ubuntu --quiet .
sha256:b6a3ee33acb83148cd273b0098f4c7eed01a82f47eeb8f5bec775c26d4fe4aae

real    0m29.251s
user    0m0.032s
sys     0m0.026s
$ docker image ls ubuntu-gcc
REPOSITORY   TAG      IMAGE ID      CREATED         SIZE
ubuntu-gcc   latest   b6a3ee33acb8  9 seconds ago   150MB

Повторяем все то же самое для Alpine (Dockerfile):

FROM alpine
RUN apk add --update gcc

Собираем, смотрим на время и размер сборки:

$ time docker build -t alpine-gcc -f Dockerfile.alpine --quiet .
sha256:efd626923c1478ccde67db28911ef90799710e5b8125cf4ebb2b2ca200ae1ac3

real    0m15.461s
user    0m0.026s
sys     0m0.024s
$ docker image ls alpine-gcc
REPOSITORY   TAG      IMAGE ID       CREATED         SIZE
alpine-gcc   latest   efd626923c14   7 seconds ago   105MB

Как и обещано, образы на базе Alpine собираются быстрей и сами по себе меньше: 15 секунда вместо 30 и размер образа 105MB против 150MB. Это довольно хорошо!

Но если мы переключимся на сборку Python приложения, то все не так радужно.

Python образ

Python приложения часто используют pandas и matplotlib. Поэтому, один из вариантов взять официальный образ на базе Debian, используя такой Dockerfile:

FROM python:3.8-slim
RUN pip install --no-cache-dir matplotlib pandas

Собираем его:

$ docker build -f Dockerfile.slim -t python-matpan.
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM python:3.8-slim
 ---> 036ea1506a85
Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas
 ---> Running in 13739b2a0917
Collecting matplotlib
  Downloading matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl (13.1 MB)
Collecting pandas
  Downloading pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl (10.4 MB)
...
Successfully built b98b5dc06690
Successfully tagged python-matpan:latest

real    0m30.297s
user    0m0.043s
sys     0m0.020s

Получаем образ размером в 363MB.
Получится у нас лучше с Alpine? Давайте попробуем:

FROM python:3.8-alpine
RUN pip install --no-cache-dir matplotlib pandas

$ docker build -t python-matpan-alpine -f Dockerfile.alpine .                                 
Sending build context to Docker daemon  3.072kB                                               
Step 1/2 : FROM python:3.8-alpine                                                             
 ---> a0ee0c90a0db                                                                            
Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas                                                  
 ---> Running in 6740adad3729                                                                 
Collecting matplotlib                                                                         
  Downloading matplotlib-3.1.2.tar.gz (40.9 MB)                                               
    ERROR: Command errored out with exit status 1:                                            
     command: /usr/local/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/
tmp/pip-install-a3olrixa/matplotlib/setup.py'"'"'; __file__='"'"'/tmp/pip-install-a3olrixa/matplotlib/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'rn'"'"', '"'"'n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-a3olrixa/matplotlib/pip-egg-info                              

...
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
The command '/bin/sh -c pip install matplotlib pandas' returned a non-zero code: 1

Что происходит?

Alpine не поддерживает wheels

Если вы посмотрите на билд, который базируется на Debian, то вы увидите, что он скачивает matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl.

Это бинарник для wheel. Alpine же скачивает исходники `matplotlib-3.1.2.tar.gz`, так как он не поддерживает стандартный wheels [1].

Почему? Большинство Linux дистрибутивов используют GNU версию (glibc) стандартной библиотеки C, который по факту необходим каждой программе написанной на C, включая Python. Но Alpine использует `musl`, а так как те бинарники предназначены для `glibc`, они попросту не вариант.

Поэтому, если вы используете Alpine, вам необходимо компилировать весь код, написанный на C, в каждом пакете Python.

Ах, да, список всех таких зависимостей которые, нужно компилировать придется искать самим.
В данном случае получаем такое:

FROM python:3.8-alpine
RUN apk --update add gcc build-base freetype-dev libpng-dev openblas-dev
RUN pip install --no-cache-dir matplotlib pandas

И время билда занимает…

… 25 минут 57 секунд! А размер образа 851MB.

Образы на базе Alpine собираются намного дольше, сами по себе они большего размера и вам еще нужно искать все зависимости. Можно конечно уменьшить размер сборки используя multi-stage builds [2] но это означает, что нужно проделать еще больше работы.

Это еще не все!

Alpine может быть причиной неожиданных багов в рантайме

  • В теории musl совместим с glibc, но на практике различия могут стать причиной многих проблем. И если они будут, то наверняка неприяные. Вот некоторые проблемы, которые могут возникнуть:
  • Alpine по умолчанию имеет меньший размер стека потока, что может привести к ошибкам в Python [3]
  • Некоторые пользователи обнаружили, что Python приложения работают медленней [4] из-за того как, musl выделяет память (отличается от glibc).
  • Один из пользователей обнаружил ошибку при форматировании даты [5]

Наверняка эти ошибки уже исправили, но кто знает сколько их еще.

Не используйте образы Alpine для Python

Если не хотите возиться с большими и долгими билдами, поиском зависимостей и потенциальными ошибками — не используйте Alpine Linux в качестве базового образа. Сhoosing a good base image [6].

Автор: Olkhovoi Dmitry

Источник [7]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/345025

Ссылки в тексте:

[1] wheels: https://pythonwheels.com/

[2] multi-stage builds: https://pythonspeed.com/articles/smaller-python-docker-images/

[3] ошибкам в Python: https://bugs.python.org/issue32307

[4] Python приложения работают медленней: https://superuser.com/questions/1219609/why-is-the-alpine-docker-image-over-50-slower-than-the-ubuntu-image

[5] обнаружил ошибку при форматировании даты: https://github.com/iron-io/dockers/issues/42#issuecomment-290763088

[6] Сhoosing a good base image: https://pythonspeed.com/articles/base-image-python-docker-images/

[7] Источник: https://habr.com/ru/post/486202/?utm_source=habrahabr&utm_medium=rss&utm_campaign=486202