
В этом материале я собираюсь поговорить о том, как создан этот образ, о том, какую он может принести пользу, о его правильном использовании и о его ограничениях. В частности, я разберу тут его вариант
python:3.8-slim-buster
(в состоянии,
представленном файлом Dockerfile от
19 августа 2020 года) и по ходу дела остановлюсь на самых
важных деталях.Читаем файл Dockerfile
Базовый образ
Начнём с базового образа:
FROM debian:buster-slim
Оказывается, что базовым образом для
python:3.8-slim-buster
является Debian GNU/Linux 10
текущий стабильный релиз Debian, известный ещё как Buster (релизы
Debian называют именами персонажей из Истории игрушек).
Бастер это, если кому интересно, собака Энди.Итак, в основе интересующего нас образ лежит дистрибутив Linux, который гарантирует его стабильную работу. Для этого дистрибутива периодически выходят исправления ошибок. В варианте
slim
установлено меньше пакетов, чем в обычном
варианте. Там, например, нет компиляторов.Переменные среды
Теперь взглянем на переменные среды. Первая обеспечивает как можно более раннее добавление
/usr/local/bin
в
$PATH
.
# обеспечивает выбор локальной версии python, а не версии, входящей в состав дистрибутиваENV PATH /usr/local/bin:$PATH
Образ устроен так, что установка Python выполняется в
/usr/local
. В результате данная конструкция
обеспечивает то, что по умолчанию будут использоваться
установленные исполняемые файлы.Далее взглянем на настройки языка:
# http://bugs.python.org/issue19846# > В настоящий момент настройка "LANG=C" в Linux *полностью выводит из строя Python 3*, а это плохо.ENV LANG C.UTF-8
Насколько я знаю, современный Python 3, по умолчанию, и без этой настройки, использует UTF-8. Поэтому я не уверен в том, что в наши дни в исследуемом Dockerfile нужна эта строка.
Здесь есть и переменная окружения, содержащая сведения о текущей версии Python:
ENV PYTHON_VERSION 3.8.5
В Dockerfile есть ещё переменная окружения с GPG-ключом, используемая для верификации загружаемого исходного кода Python.
Зависимости времени выполнения
Python, для работы, нужны некоторые дополнительные пакеты:
RUN apt-get update && apt-get install -y --no-install-recommends \ca-certificates \netbase \&& rm -rf /var/lib/apt/lists/*
Первый пакет,
ca-certificates
, содержит список
сертификатов стандартных центров сертификации. Нечто подобное
используется браузером для проверки
-адресов. Это позволяет Python, wget
и другим
инструментам проверять сертификаты, предоставляемые серверами.Второй пакет,
netbase
, выполняет установку в
/etc
нескольких файлов, необходимых для настройки
соответствия определённых имён с некоторыми портами и протоколами.
Например, /etc/services
отвечает за настройку
соответствия имён сервисов, вроде https
, с номерами
портов. В данном случае это 443/tcp
.Установка Python
Теперь выполняется установка набора инструментальных средств компиляции. А именно, загружается и компилируется исходный код Python, после чего деинсталлируются ненужные пакеты Debian:
RUN set -ex \\&& savedAptMark="$(apt-mark showmanual)" \&& apt-get update && apt-get install -y --no-install-recommends \dpkg-dev \gcc \libbluetooth-dev \libbz2-dev \libc6-dev \libexpat1-dev \libffi-dev \libgdbm-dev \liblzma-dev \libncursesw5-dev \libreadline-dev \libsqlite3-dev \libssl-dev \make \tk-dev \uuid-dev \wget \xz-utils \zlib1g-dev \# с релиза Stretch "gpg" больше по умолчанию в дистрибутив не входит$(command -v gpg > /dev/null || echo 'gnupg dirmngr') \\&& 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 --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY" \&& gpg --batch --verify python.tar.xz.asc python.tar.xz \&& { command -v gpgconf > /dev/null && gpgconf --kill all || :; } \&& 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 \\&& cd /usr/src/python \&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \&& ./configure \--build="$gnuArch" \--enable-loadable-sqlite-extensions \--enable-optimizations \--enable-option-checking=fatal \--enable-shared \--with-system-expat \--with-system-ffi \--without-ensurepip \&& make -j "$(nproc)" \LDFLAGS="-Wl,--strip-all" \&& make install \&& rm -rf /usr/src/python \\&& find /usr/local -depth \\( \\( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \-o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name '*.a' \) \) \-o \( -type f -a -name 'wininst-*.exe' \) \\) -exec rm -rf '{}' + \\&& ldconfig \\&& apt-mark auto '.*' > /dev/null \&& apt-mark manual $savedAptMark \&& find /usr/local -type f -executable -not \( -name '*tkinter*' \) -exec ldd '{}' ';' \| awk '/=>/ { print $(NF-1) }' \| sort -u \| xargs -r dpkg-query --search \| cut -d: -f1 \| sort -u \| xargs -r apt-mark manual \&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \&& rm -rf /var/lib/apt/lists/* \\&& python3 --version
Тут происходит много всего, но самое важное это следующее:
- Python устанавливается в
/usr/local
. - Удаляются все .pyc-файлы.
- Пакеты, в частности
gcc
и прочие подобные, которые были нужны для компиляции Python, удаляются после того, как необходимость в них пропадает.
Из-за того, что всё это происходит в единственной команде
RUN
, в итоге компилятор не сохраняется ни в одном из
слоёв, что помогает поддерживать компактный размер образа.Тут можно обратить внимание на то, что Python для компиляции нужна библиотека
libbluetooth-dev
. Мне это показалось
необычным, поэтому я решил в этом разобраться. Как оказалось,
Python может создавать Bluetooth-сокеты, но только в том случае,
если он скомпилирован с использованием этой библиотеки.Настройка символьных ссылок
На следующем шаге работы
/usr/local/bin/python3
назначается символьная ссылка /usr/local/bin/python
,
что позволяет вызывать Python разными способами:
# создание некоторых полезных символьных ссылок, присутствие которых ожидается в системеRUN cd /usr/local/bin \&& ln -s idle3 idle \&& ln -s pydoc3 pydoc \&& ln -s python3 python \&& ln -s python3-config python-config
Установка pip
У менеджера пакетов
pip
имеется собственный график
выхода релизов, отличающийся от графика релизов Python. Например, в
этом Dockerfile выполняется установка Python 3.8.5, выпущенного в
июле 2020. А pip 20.22.2 вышел в августе, уже после выхода Python,
но Dockerfile устроен так, чтобы была бы установлена свежая версия
pip
:
# если эту переменную назвать "PIP_VERSION", то pip выдаёт ошибку: "ValueError: invalid truth value '<VERSION>'"ENV PYTHON_PIP_VERSION 20.2.2# https://github.com/pypa/get-pipENV PYTHON_GET_PIP_URL https://github.com/pypa/get-pip/raw/5578af97f8b2b466f4cdbebe18a3ba2d48ad1434/get-pip.pyENV PYTHON_GET_PIP_SHA256 d4d62a0850fe0c2e6325b2cc20d818c580563de5a2038f917e3cb0e25280b4d1RUN set -ex; \\savedAptMark="$(apt-mark showmanual)"; \apt-get update; \apt-get install -y --no-install-recommends wget; \\wget -O get-pip.py "$PYTHON_GET_PIP_URL"; \echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum --check --strict -; \\apt-mark auto '.*' > /dev/null; \[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \rm -rf /var/lib/apt/lists/*; \\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 -name idle_test \) \) \-o \\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \\) -exec rm -rf '{}' +; \rm -f get-pip.py
После выполнения этих операций, как и прежде, удаляются все .pyc-файлы.
Точка входа в образ
В итоге в Dockerfile указывается точка входа в образ:
CMD ["python3"]
Используя
CMD
вместо ENTRYPOINT
мы,
запуская образ, по умолчанию получаем доступ к python:
$ docker run -it python:3.8-slim-busterPython 3.8.5 (default, Aug 4 2020, 16:24:08)[GCC 8.3.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>>
Но, при необходимости, можно указывать при запуске образа и другие исполняемые файлы:
$ docker run -it python:3.8-slim-buster bashroot@280c9b73e8f9:/#
Итоги
Вот что мы узнали, разобрав Dockerfile официального Python-образа
slim-buster
.В состав образа входит Python
Хотя это и может показаться очевидным, стоит обратить внимание на то, как именно Python включён в состав образа. А именно, сделано это путём его самостоятельной установки в
/usr/local
.Программисты, использующие этот образ, порой совершают одну и ту же ошибку, которая заключается в повторной установке Debian-версии Python:
FROM python:3.8-slim-buster# Делать этого не нужно:RUN apt-get update && apt-get install python3-dev
При выполнении этой команды
RUN
Python будет
установлен ещё раз, но в /usr
, а не в
/usr/local
. И это, как правило, будет не та версия
Python, которая установлена в /usr/local
. А
программисту, который воспользовался вышеприведённым Docker-файлом,
вероятно,
не нужны две разные версии Python в одном и том же образе. Это,
в основном, является причиной путаницы.А если же кому-то и правда нужна Debian-версия Python, то лучше будет использовать в качестве базового образа
debian:buster-slim
.В образ входит самая свежая версия pip
Например, самый свежий релиз Python 3.5 состоялся в ноябре 2019, но Docker-образ
python:3.5-slim-buster
включает в себя
pip
, который вышел в августе 2020. Это (обычно)
хорошо, так как означает, что в нашем распоряжении оказываются
самые свежие исправления ошибок и улучшения производительности.
Это, кроме того, значит, что мы можем пользоваться поддержкой более
новых вариантов колёс.Из образа удаляются все .pyc-файлы
Если хочется немного ускорить загрузку системы, то можно самостоятельно скомпилировать исходный код стандартной библиотеки в формат .pyc. Делается это с помощью модуля compileall.
Образ не выполняет установку обновлений безопасности Debian
Хотя базовые образы
debian:buster-slim
и
python
часто обновляются, имеется определённый
промежуток между моментами выхода обновлений безопасности Debian и
включением их в образы. Поэтому нужно самостоятельно устанавливать
обновления безопасности для базового дистрибутива Linux.Какими Docker-образами вы пользуетесь для выполнения Python-кода?