Compreender os recursos da imagem oficial do Docker Python

A imagem oficial do Docker do Python é bastante popular. A propósito, eu mesmo recomendei uma de suas variações como imagem de base. Mas muitos programadores não entendem exatamente como funciona. Isso pode causar confusão e vários problemas. Neste post vou falar sobre como esta imagem foi criada, como ela pode ser útil, seu uso correto e suas limitações. Especificamente, vou dividi-lo aqui (no estado representado pelo Dockerfile datado de 19 de agosto de 2020 ) e me alongar nos detalhes mais importantes ao longo do caminho.







python:3.8-slim-buster



Ler o Dockerfile



▍Imagem básica



Vamos começar com uma imagem de base:



FROM debian:buster-slim


Acontece que a imagem base python:3.8-slim-busteré Debian GNU / Linux 10, a versão estável atual do Debian, também conhecido como Buster (as versões do Debian têm o nome de personagens do Toy Story). Buster é, se alguém estiver interessado, o cachorro de Andy.



Portanto, no centro da imagem que nos interessa está a distribuição Linux, que garante seu funcionamento estável. Correções de bugs são lançadas periodicamente para esta distribuição. A variante tem slimmenos pacotes instalados do que a variante regular. Lá, por exemplo, não existem compiladores.



▍ Variáveis ​​de ambiente



Agora vamos dar uma olhada nas variáveis ​​de ambiente. O primeiro garante que ele seja adicionado o /usr/local/binmais cedo possível $PATH.



#     python,   ,    
ENV PATH /usr/local/bin:$PATH


A imagem foi projetada para que o Python seja instalado /usr/local. Como resultado, essa construção garante que os executáveis ​​instalados serão usados ​​por padrão.



A seguir, vamos dar uma olhada nas configurações de idioma:



# http://bugs.python.org/issue19846
# >     "LANG=C"  Linux *    Python 3*,   .
ENV LANG C.UTF-8


Pelo que eu sei, o Python 3 moderno, por padrão, e sem essa configuração, usa UTF-8. Portanto, não tenho certeza se essa linha é necessária no Dockerfile em questão atualmente.



Também existe uma variável de ambiente que contém informações sobre a versão atual do Python:



ENV PYTHON_VERSION 3.8.5


Há também uma variável de ambiente com chave GPG no Dockerfile, que é usada para verificar o código-fonte Python carregado.



▍ Dependências de tempo de execução



O Python precisa de alguns pacotes adicionais para funcionar:



RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates \
    netbase \
  && rm -rf /var/lib/apt/lists/*


O primeiro pacote ,, ca-certificatescontém uma lista de certificados CA padrão. Algo semelhante é usado pelo navegador para validar -addresses . Isso permite que o Python wgete outras ferramentas validem os certificados fornecidos pelos servidores.



O segundo pacote, netbaseque realiza a instalação em /etcvários arquivos, é necessário para configurar o mapeamento de determinados nomes para determinadas portas e protocolos. Por exemplo, /etc/servicesé responsável por configurar a correspondência de nomes de serviços, como https, com números de portas. Nesse caso, é 443/tcp.



▍ Instalar Python



O kit de ferramentas de compilação agora está sendo instalado. Ou seja, o código-fonte Python é baixado e compilado e, em seguida, pacotes Debian desnecessários são desinstalados:



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


Há muita coisa acontecendo aqui, mas o mais importante é o seguinte:



  1. Python está instalado em /usr/local.
  2. Todos os arquivos .pyc são removidos.
  3. Pacotes, em particular - gcce outros do tipo que eram necessários para compilar o Python, são removidos depois que não são mais necessários.


Pelo fato de tudo isso acontecer em um único comando RUN, o resultado é que o compilador não fica armazenado em nenhuma das camadas, o que ajuda a manter o tamanho compacto da imagem.



Aqui você pode notar que o Python precisa de uma biblioteca para compilar libbluetooth-dev. Isso parecia incomum para mim, então decidi descobrir. Acontece que o Python pode criar soquetes Bluetooth, mas apenas se compilado usando esta biblioteca.



▍ Configuração de link simbólico



A próxima etapa /usr/local/bin/python3é atribuir um link simbólico /usr/local/bin/python, que permite que o Python seja chamado de diferentes maneiras:



#     ,     
RUN cd /usr/local/bin \
  && ln -s idle3 idle \
  && ln -s pydoc3 pydoc \
  && ln -s python3 python \
  && ln -s python3-config python-config


▍Instalar pip



O gerenciador de pacotes piptem sua própria programação de lançamento, que difere da programação de lançamento do Python. Por exemplo, este Dockerfile instala o Python 3.8.5, lançado em julho de 2020. E o pip 20.2.2 foi lançado em agosto, depois que o Python foi lançado, mas o Dockerfile foi projetado para ter uma nova versão instalada pip:



#     "PIP_VERSION",  pip  : "ValueError: invalid truth value '<VERSION>'"
ENV PYTHON_PIP_VERSION 20.2.2
# https://github.com/pypa/get-pip
ENV PYTHON_GET_PIP_URL https://github.com/pypa/get-pip/raw/5578af97f8b2b466f4cdbebe18a3ba2d48ad1434/get-pip.py
ENV PYTHON_GET_PIP_SHA256 d4d62a0850fe0c2e6325b2cc20d818c580563de5a2038f917e3cb0e25280b4d1

RUN 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


Depois de concluir essas operações, como antes, todos os arquivos .pyc são excluídos.



▍ Ponto de entrada da imagem



Como resultado, o ponto de entrada para a imagem é especificado no Dockerfile:



CMD ["python3"]


Usando CMD, em vez disso, ENTRYPOINTlançamos a imagem, por padrão temos acesso ao python:



$ docker run -it python:3.8-slim-buster
Python 3.8.5 (default, Aug  4 2020, 16:24:08)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>


Mas, se necessário, você pode especificar outros arquivos executáveis ​​ao iniciar a imagem:



$ docker run -it python:3.8-slim-buster bash
root@280c9b73e8f9:/#


Resultado



Aqui está o que aprendemos ao analisar o Dockerfile de imagem oficial do Python slim-buster.



▍A imagem inclui Python



Embora isso possa parecer óbvio, vale a pena prestar atenção em exatamente como o Python está incluído na imagem. Ou seja, isso é feito instalando-o em /usr/local.



Os programadores que usam essa imagem às vezes cometem o mesmo erro, que é reinstalar a versão Debian do Python:



FROM python:3.8-slim-buster

#    :
RUN apt-get update && apt-get install python3-dev


Ao executar este comando, o RUNPython será instalado novamente, mas em /usr, não em /usr/local. E esta geralmente não será a versão do Python que está instalada /usr/local. E o programador que usou o arquivo Docker acima provavelmente não precisa de duas versões diferentes do Python na mesma imagem. Este é principalmente o motivo da confusão.



E se alguém realmente precisa de uma versão Debian do Python, então seria melhor usá-la como imagem base debian:buster-slim.



▍ A imagem inclui a última versão do pip



Por exemplo, a versão mais recente do Python 3.5 foi em novembro de 2019, mas a imagem do Docker python:3.5-slim-busterinclui pipaquela que foi lançada em agosto de 2020. Isso é (geralmente) bom, pois significa que temos as últimas correções de bugs e melhorias de desempenho. Isso também significa que podemos nos beneficiar do suporte para opções de rodas mais recentes.



▍ Todos os arquivos .pyc são removidos da imagem



Se quiser acelerar um pouco a inicialização do sistema, você pode compilar independentemente o código-fonte da biblioteca padrão no formato .pyc. Isso é feito usando o módulo compileall .



▍O Image não instala atualizações de segurança do Debian



Embora as imagens de base debian:buster-slim, e pythonsão atualizados com freqüência, há um certo desfasamento entre o lançamento das atualizações de segurança do Debian e transformando-os em imagens. Portanto, você precisa instalar independentemente as atualizações de segurança para a distribuição básica do Linux.



Quais imagens Docker você usa para executar o código Python?






All Articles