Pull to refresh

Так ли мал Alpine 3.8 Docker для Python 3 runtime

Reading time8 min
Views29K
Совсем недавно произошёл релиз минималистичного 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:
  1. Система, ее обновление. И Python3 с обновлённым pip (10 версии)
  2. п.1 + tornado cython
  3. п.2 + numpy-scipy
  4. п.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):
  1. Debian 9 / 513 MB
  2. Fedora 28 / 387 MB
  3. 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

  1. Debian 9 / 534 MB
  2. Fedora 28 / 407 MB
  3. Alpine 3.8 / 144 MB


Шаг 3. Добавляем математику numpy scipy


  1. Debian 9 / 763 MB
  2. Fedora 28 / 626 MB
  3. 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"]



  1. Debian 9 / 905 MB
  2. Fedora 28 / 760 MB
  3. 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
Only registered users can participate in poll. Log in, please.
Какой образ для Python вы используете
76.62% Официальный образ Python из https://hub.docker.com/118
0.65% Неофициальный образ Python из https://hub.docker.com/1
22.73% Собираю сам35
154 users voted. 85 users abstained.
Only registered users can participate in poll. Log in, please.
На основе какой системы собраны ваши Docker образы для Python
62.92% Alpine151
2.92% Arch linux7
20.42% Debian49
2.08% Fedora5
0.42% SUSE, openSUSE, SLES1
33.33% Ubuntu80
9.58% RHEL, Oracle Linux, CentOS, Scientific Linux23
6.67% Не знаю16
240 users voted. 102 users abstained.
Tags:
Hubs:
+5
Comments13

Articles

Change theme settings