Совсем недавно произошёл релиз минималистичного Alpine Linux 3.8. Очень часто данный linux образ используют в докере, собирая очень компактные окружения для runtime.
Сегодняшняя статья будет рассмотрена в срезе использования runtime системы в докере для Python 3.6.X версий, с различным составом пакетов pip. А так же мы соберём самый новый Python 3.7 в Alpine.
В конце статьи будет представлен размер образа image, занимаемый на диске, в зависимости от состава пакетов pip и произведено сравнение между дистрибутивами Alpine 3.8, Debian 9, Fedora 28.
Итак, приступим: для тестирования дистрибутивы выбраны. Будем собирать следующие docker images:
В результате даных заливок, мы получим различные версии: Python без пакетов, Python с web сервером, Python с пакетами для обработки многопоточных математических вычислений, Python с «графическим» стеком и работы с данными.
Итак, результирующие файлы для Debian и Fedora будут выглядеть у нас так:
Debian
Fedora
А вот с Alpine 3.8 пока заминка. Официально на момент написания статьи он ещё на вышел, а посмотреть, то хочется:-). Поэтому нам понадобиться их образ системы:
dl-cdn.alpinelinux.org/alpine/v3.8/releases/x86_64
И мы соберём свой Alpine from Sratch:
github.com/gliderlabs/docker-alpine/tree/master/versions/library-3.8/x86_64
Выкачиваем файловую систему Alpine 3.8:
curl dl-cdn.alpinelinux.org/alpine/v3.8/releases/x86_64/alpine-minirootfs-3.8.0_rc10-x86_64.tar.gz >alpine3.8.tar.gz
Создаём свой докер файл:
Затем копипастим и добавляем в этот файл борку Python 3.6 со страницы github.com/docker-library/python/blob/master/3.6/alpine3.7/Dockerfile
не забыв удалить или закомментировать строку FROM alpine:3.7
И пробуем создать образ с Alpine 3.8 и Python на борту:
Результаты первого шага установка только Python (docker images --all):
Придется гуглить и потом уже добавлять библиотеки сборки в Alpine, чтобы pip успешно собрал их из исходного текста. Затем запускать сборку докера снова, затем опять искать зависимости, читать форумы stackoverflow и issues в github и ждать и ждать и ждать.

Поскольку в следующих шагах мы начнём добавлять математические и графические библиотеки в наш образ runtime Python, и чтобы слишком не увеличивать текст данной статьи, я приведу финальные зависимости для Alpine linux:
В качестве бонуса, попробуем в Alpine 3.8 скомпилировать ещё не вышедший для докера Python 3.7.
Новая версия Python 3.7 представлена 27 июня 2018 года
Код компиляции возьмём из:
github.com/docker-library/python/tree/bbbc37fff3411a34deef30dd9b34dc938fe7b134/3.7-rc/alpine3.7
Размер Alpine 3.8 с Python 3.7 с текущим списком пакетов pip 656 MB
Python
Python tornado cython
Python tornado cython numpy scipy
Python tornado cython numpy scipy websocket-client pytest pandas bokeh pillow
При использовании пустого runtime Python, дистрибутив Alpine linux лидер по минимальному размеру. При увеличени количества библиотек pip до tornado+cython+numpy+scipy Alpine все ещё дает заметную экономию в размере на жёстком диске. Одако как только в пакетах появляются графические утилиты для работы с данными для Python, разница практически исчезает.
При большом количестве графических пакетов, оптимальнее выбрать дистрибутив Fedora, чем заниматься компиляцией пакетов в Alpine (компиляция может длиться 1-2 часа), и в результате получить экономию в один или два десятка процентов места на жёстком диске.
UPDATE1: Тестирование проводилось на Fedora Atomic Host: release 28 (Twenty Eight), Version: 2018.5
UPDATE2: Проверка занимаемого места на диске, была проведена по данному билду hub.docker.com/r/flytrue/python-runtime-docker/tags
Сегодняшняя статья будет рассмотрена в срезе использования runtime системы в докере для Python 3.6.X версий, с различным составом пакетов pip. А так же мы соберём самый новый Python 3.7 в Alpine.
В конце статьи будет представлен размер образа image, занимаемый на диске, в зависимости от состава пакетов pip и произведено сравнение между дистрибутивами Alpine 3.8, Debian 9, Fedora 28.
Итак, приступим: для тестирования дистрибутивы выбраны. Будем собирать следующие docker images:
- Система, ее обновление. И Python3 с обновлённым pip (10 версии)
- п.1 + tornado cython
- п.2 + numpy-scipy
- п.3 + pillow bokeh pandas websocket-client
В результате даных заливок, мы получим различные версии: Python без пакетов, Python с web сервером, Python с пакетами для обработки многопоточных математических вычислений, Python с «графическим» стеком и работы с данными.
Итак, результирующие файлы для Debian и Fedora будут выглядеть у нас так:
Debian
FROM debian RUN apt-get update -y && apt-get install python3-pip -y && pip3 install pip --upgrade && apt-get clean RUN pip3 install cython tornado websocket-client pytest numpy pandas scipy bokeh pillow
Fedora
FROM fedora RUN dnf update -y && dnf install libstdc++ -y && dnf clean all && pip3 install --upgrade pip && python3 --version RUN pip3 install cython tornado websocket-client pytest numpy pandas scipy bokeh pillow
А вот с Alpine 3.8 пока заминка. Официально на момент написания статьи он ещё на вышел, а посмотреть, то хочется:-). Поэтому нам понадобиться их образ системы:
dl-cdn.alpinelinux.org/alpine/v3.8/releases/x86_64
И мы соберём свой Alpine from Sratch:
github.com/gliderlabs/docker-alpine/tree/master/versions/library-3.8/x86_64
Выкачиваем файловую систему Alpine 3.8:
curl dl-cdn.alpinelinux.org/alpine/v3.8/releases/x86_64/alpine-minirootfs-3.8.0_rc10-x86_64.tar.gz >alpine3.8.tar.gz
Создаём свой докер файл:
FROM scratch ADD alpine3.8.tar.gz / ENV RELEASE="v3.8" ENV MIRROR="http://dl-cdn.alpinelinux.org/alpine" ENV PACKAGES="alpine-baselayout,busybox,alpine-keys,apk-tools,libc-utils" ENV TAGS=(alpine:3.8)
Затем копипастим и добавляем в этот файл борку Python 3.6 со страницы github.com/docker-library/python/blob/master/3.6/alpine3.7/Dockerfile
не забыв удалить или закомментировать строку FROM alpine:3.7
И пробуем создать образ с Alpine 3.8 и Python на борту:
$docker build -t alpine3.8 . #результат работы (последние строки) (1/2) Purging .fetch-deps (0) (2/2) Purging libressl (2.7.4-r0) Executing busybox-1.28.4-r0.trigger OK: 33 MiB in 45 packages + python get-pip.py --disable-pip-version-check --no-cache-dir 'pip==10.0.1' Collecting pip==10.0.1 Downloading https://files.pythonhosted.org/packages/0f/74/ecd13431bcc456ed390b44c8a6e917c1820365cbebcb6a8974d1cd045ab4/pip-10.0.1-py2.py3-none-any.whl (1.3MB) Collecting setuptools Downloading https://files.pythonhosted.org/packages/7f/e1/820d941153923aac1d49d7fc37e17b6e73bfbd2904959fffbad77900cf92/setuptools-39.2.0-py2.py3-none-any.whl (567kB) Collecting wheel Downloading https://files.pythonhosted.org/packages/81/30/e935244ca6165187ae8be876b6316ae201b71485538ffac1d718843025a9/wheel-0.31.1-py2.py3-none-any.whl (41kB) Installing collected packages: pip, setuptools, wheel Successfully installed pip-10.0.1 setuptools-39.2.0 wheel-0.31.1 + pip --version pip 10.0.1 from /usr/local/lib/python3.6/site-packages/pip (python 3.6) + find /usr/local -depth '(' '(' -type d -a '(' -name test -o -name tests ')' ')' -o '(' -type f -a '(' -name '*.pyc' -o -name '*.pyo' ')' ')' ')' -exec rm -rf '{}' + + rm -f get-pip.py ---> f7439dca5f31 Removing intermediate container e1fcd1c74873 Step 17/17 : CMD python3 ---> Running in a4dc6dfa5184 ---> 6924b206c6b9 Removing intermediate container a4dc6dfa5184 Successfully built 6924b206c6b9
Результаты первого шага установка только Python (docker images --all):
- Debian 9 / 513 MB
- Fedora 28 / 387 MB
- Alpine 3.8 / 82.2 MB
Шаг 2. Установка cython и tornado
Начинаем добавлять пакеты pip. Первым установим cython и tornado. Для Debian и Fedora пакеты ставятся без ошибок, а вот Alpine падает с ошибкой:unable to execute 'gcc': No such file or directory error: command 'gcc' failed with exit status 1
Придется гуглить и потом уже добавлять библиотеки сборки в Alpine, чтобы pip успешно собрал их из исходного текста. Затем запускать сборку докера снова, затем опять искать зависимости, читать форумы stackoverflow и issues в github и ждать и ждать и ждать.

Поскольку в следующих шагах мы начнём добавлять математические и графические библиотеки в наш образ runtime Python, и чтобы слишком не увеличивать текст данной статьи, я приведу финальные зависимости для Alpine linux:
apk add --no-cache --virtual .build-deps \ gfortran \ build-base \ openblas-dev \ bzip2-dev \ coreutils \ dpkg-dev dpkg \ expat-dev \ gcc \ gdbm-dev \ libc-dev \ libffi-dev \ libnsl-dev \ libressl \ libressl-dev \ libtirpc-dev \ linux-headers \ make \ ncurses-dev \ pax-utils \ readline-dev \ sqlite-dev \ tcl-dev \ tk \ tk-dev \ xz-dev \ zlib-dev \ libxml2-dev \ libxslt-dev \ musl-dev \ libgcc \ curl \ jpeg-dev \ zlib-dev \ freetype-dev \ lcms2-dev \ openjpeg-dev \ tiff-dev \ tk-dev \ tcl-dev \ && ln -s /usr/include/locale.h /usr/include/xlocale.h
- Debian 9 / 534 MB
- Fedora 28 / 407 MB
- Alpine 3.8 / 144 MB
Шаг 3. Добавляем математику numpy scipy
- Debian 9 / 763 MB
- Fedora 28 / 626 MB
- Alpine 3.8 / 404 MB MB
Шаг 4. Добавляем графический стек websocket-client pytest pandas bokeh pillow
Dockerfile-Alpine
FROM scratch ADD alpine3.8.tar.gz / CMD ["/bin/sh"] ENV RELEASE="v3.8" ENV MIRROR="http://dl-cdn.alpinelinux.org/alpine" ENV PACKAGES="alpine-baselayout,busybox,alpine-keys,apk-tools,libc-utils" #ENV BUILD_OPTIONS=(-b -s -t UTC -r $RELEASE -m $MIRROR -p $PACKAGES) ENV TAGS=(alpine:3.8) # # NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" # # PLEASE DO NOT EDIT IT DIRECTLY. # #https://github.com/docker-library/python/blob/master/3.6/alpine3.7/Dockerfile # ensure local python is preferred over distribution python ENV PATH /usr/local/bin:$PATH # http://bugs.python.org/issue19846 # > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. ENV LANG C.UTF-8 # install ca-certificates so that HTTPS works consistently # the other runtime dependencies for Python are installed later RUN apk add --no-cache ca-certificates ENV GPG_KEY 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D ENV PYTHON_VERSION 3.6.6 RUN set -ex \ && apk add --no-cache --virtual .fetch-deps \ gnupg \ libressl \ tar \ xz \ \ && wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ && wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ && export GNUPGHOME="$(mktemp -d)" \ && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY" \ && gpg --batch --verify python.tar.xz.asc python.tar.xz \ && rm -rf "$GNUPGHOME" python.tar.xz.asc \ && mkdir -p /usr/src/python \ && tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \ && rm python.tar.xz \ \ && apk add --no-cache --virtual .build-deps \ bzip2-dev \ coreutils \ dpkg-dev dpkg \ expat-dev \ gcc \ gdbm-dev \ libc-dev \ libffi-dev \ libnsl-dev \ libressl \ libressl-dev \ libtirpc-dev \ linux-headers \ make \ ncurses-dev \ pax-utils \ readline-dev \ sqlite-dev \ tcl-dev \ tk \ tk-dev \ xz-dev \ zlib-dev \ # add build deps before removing fetch deps in case there's overlap && apk del .fetch-deps \ \ && cd /usr/src/python \ && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ && ./configure \ --build="$gnuArch" \ --enable-loadable-sqlite-extensions \ --enable-shared \ --with-system-expat \ --with-system-ffi \ --without-ensurepip \ && make -j "$(nproc)" \ # set thread stack size to 1MB so we don't segfault before we hit sys.getrecursionlimit() # https://github.com/alpinelinux/aports/commit/2026e1259422d4e0cf92391ca2d3844356c649d0 EXTRA_CFLAGS="-DTHREAD_STACK_SIZE=0x100000" \ && make install \ \ && runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )" \ && apk add --virtual .python-rundeps $runDeps \ && apk del .build-deps \ \ && find /usr/local -depth \ \( \ \( -type d -a \( -name test -o -name tests \) \) \ -o \ \( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \ \) -exec rm -rf '{}' + \ && rm -rf /usr/src/python # make some useful symlinks that are expected to exist RUN cd /usr/local/bin \ && ln -s idle3 idle \ && ln -s pydoc3 pydoc \ && ln -s python3 python \ && ln -s python3-config python-config # if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value '<VERSION>'" ENV PYTHON_PIP_VERSION 10.0.1 RUN set -ex; \ \ apk add --no-cache --virtual .fetch-deps libressl; \ \ wget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \ \ apk del .fetch-deps; \ \ python get-pip.py \ --disable-pip-version-check \ --no-cache-dir \ "pip==$PYTHON_PIP_VERSION" \ ; \ pip --version; \ \ find /usr/local -depth \ \( \ \( -type d -a \( -name test -o -name tests \) \) \ -o \ \( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \ \) -exec rm -rf '{}' +; \ rm -f get-pip.py apk add --no-cache --virtual .build-deps \ gfortran \ build-base \ openblas-dev \ bzip2-dev \ coreutils \ dpkg-dev dpkg \ expat-dev \ gcc \ gdbm-dev \ libc-dev \ libffi-dev \ libnsl-dev \ libressl \ libressl-dev \ libtirpc-dev \ linux-headers \ make \ ncurses-dev \ pax-utils \ readline-dev \ sqlite-dev \ tcl-dev \ tk \ tk-dev \ xz-dev \ zlib-dev \ libxml2-dev \ libxslt-dev \ musl-dev \ libgcc \ curl \ jpeg-dev \ zlib-dev \ freetype-dev \ lcms2-dev \ openjpeg-dev \ tiff-dev \ tk-dev \ tcl-dev \ && ln -s /usr/include/locale.h /usr/include/xlocale.h \ && pip install cython tornado websocket-client pytest numpy pandas scipy bokeh pillow \ && apk del .build-deps \ && apk add --no-cache libstdc++ openblas zlib jpeg openjpeg tiff tk tcl musl libxml2 libxslt xz zlib libstdc++ openblas \ && pip list CMD ["python3"]
- Debian 9 / 905 MB
- Fedora 28 / 760 MB
- Alpine 3.8 / 650 MB
В качестве бонуса, попробуем в Alpine 3.8 скомпилировать ещё не вышедший для докера Python 3.7.
Новая версия Python 3.7 представлена 27 июня 2018 года
Код компиляции возьмём из:
github.com/docker-library/python/tree/bbbc37fff3411a34deef30dd9b34dc938fe7b134/3.7-rc/alpine3.7
Завершение компиляции 3.7
Package Version ---------------- ------- atomicwrites 1.1.5 attrs 18.1.0 bokeh 0.13.0 Cython 0.28.3 Jinja2 2.10 MarkupSafe 1.0 more-itertools 4.2.0 numpy 1.14.5 packaging 17.1 pandas 0.23.1 Pillow 5.1.0 pip 10.0.1 pluggy 0.6.0 py 1.5.4 pyparsing 2.2.0 pytest 3.6.2 python-dateutil 2.7.3 pytz 2018.4 PyYAML 4.1 scipy 1.1.0 setuptools 39.2.0 six 1.11.0 tornado 5.0.2 websocket-client 0.48.0 wheel 0.31.1 ---> 3d18b8c27cd9 Removing intermediate container f546e004b79f Step 18/18 : CMD python3 ---> Running in 23c5aea50a0d ---> d5385e425064 Removing intermediate container 23c5aea50a0d Successfully built d5385e425064 real 41m17,619s
Размер Alpine 3.8 с Python 3.7 с текущим списком пакетов pip 656 MB
Итоги
Python
- Debian 9 / больше в 6.24х / +430 Mb
- Fedora 28 / больше в 4,7х / +304 Mb
Python tornado cython
- Debian 9 / больше в 3,71х / +390 Mb
- Fedora 28 / больше в 2,82x / +263 Mb
Python tornado cython numpy scipy
- Debian 9 / больше в 1,88 раз / +359 Mb
- Fedora 28 / больше в 1.54 раз / +222 Mb
Python tornado cython numpy scipy websocket-client pytest pandas bokeh pillow
- Debian 9 / больше в 1,39 раз / +255 Mb
- Fedora 28 / больше в 1.16 раз / +110 Mb
При использовании пустого runtime Python, дистрибутив Alpine linux лидер по минимальному размеру. При увеличени количества библиотек pip до tornado+cython+numpy+scipy Alpine все ещё дает заметную экономию в размере на жёстком диске. Одако как только в пакетах появляются графические утилиты для работы с данными для Python, разница практически исчезает.
При большом количестве графических пакетов, оптимальнее выбрать дистрибутив Fedora, чем заниматься компиляцией пакетов в Alpine (компиляция может длиться 1-2 часа), и в результате получить экономию в один или два десятка процентов места на жёстком диске.
UPDATE1: Тестирование проводилось на Fedora Atomic Host: release 28 (Twenty Eight), Version: 2018.5
UPDATE2: Проверка занимаемого места на диске, была проведена по данному билду hub.docker.com/r/flytrue/python-runtime-docker/tags
$docker pull flytrue/python-runtime-docker:alpine-full $docker save flytrue/python-runtime-docker:alpine-full -o alpine-full.tar $ls -lh alpine-full.tar -rw-------. 1 fedora fedora 631M июн 29 08:38 alpine-full.tar $ docker images --all|grep alpine docker.io/flytrue/python-runtime-docker alpine-full f37154658671 19 hours ago 650 MB
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Какой образ для Python вы используете
76.77%Официальный образ Python из https://hub.docker.com/119
0.65%Неофициальный образ Python из https://hub.docker.com/1
22.58%Собираю сам35
Проголосовали 155 пользователей. Воздержались 85 пользователей.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
На основе какой системы собраны ваши Docker образы для Python
62.66%Alpine151
2.9%Arch linux7
20.33%Debian49
2.07%Fedora5
0.41%SUSE, openSUSE, SLES1
33.2%Ubuntu80
9.54%RHEL, Oracle Linux, CentOS, Scientific Linux23
7.05%Не знаю17
Проголосовал 241 пользователь. Воздержались 103 пользователя.
