Совсем недавно произошёл релиз минималистичного 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.62% Официальный образ Python из https://hub.docker.com/118
0.65% Неофициальный образ Python из https://hub.docker.com/1
22.73% Собираю сам35
Проголосовали 154 пользователя. Воздержались 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 пользователя.