Comprender las características de la imagen oficial de Python Docker

La imagen oficial de Python Docker es bastante popular. Por cierto, yo mismo he recomendado una de sus variaciones como imagen base. Pero muchos programadores no entienden exactamente cómo funciona. Esto puede generar confusión y varios problemas. En este post voy a hablar sobre cómo se creó esta imagen, cómo puede ser útil, su uso adecuado y sus limitaciones. Específicamente, analizaré una variación aquí (en el estado representado por el Dockerfile con fecha del 19 de agosto de 2020 ) y me detendré en los detalles más importantes a lo largo del camino.







python:3.8-slim-buster



Leyendo el Dockerfile



▍Imagen básica



Comencemos con una imagen base:



FROM debian:buster-slim


Resulta que la imagen base python:3.8-slim-busteres Debian GNU / Linux 10, la versión estable actual de Debian, también conocida como Buster (las versiones de Debian llevan el nombre de personajes de Toy Story). Buster es, si alguien está interesado, el perro de Andy.



Entonces, en el corazón de la imagen que nos interesa está la distribución de Linux, que garantiza su funcionamiento estable. Las correcciones de errores se publican periódicamente para esta distribución. La variante tiene slimmenos paquetes instalados que la variante regular. Allí, por ejemplo, no hay compiladores.



▍Variables de entorno



Ahora echemos un vistazo a las variables de entorno. El primero asegura que se agregue /usr/local/binlo antes posible a $PATH.



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


La imagen está diseñada para que Python esté instalado en /usr/local. Como resultado, esta construcción asegura que los ejecutables instalados se utilizarán de forma predeterminada.



A continuación, echemos un vistazo a la configuración de idioma:



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


Hasta donde yo sé, el Python 3 moderno, de forma predeterminada y sin esta configuración, usa UTF-8. Así que no estoy seguro de si esta línea es necesaria en el Dockerfile en cuestión estos días.



También hay una variable de entorno que contiene información sobre la versión actual de Python:



ENV PYTHON_VERSION 3.8.5


También hay una variable de entorno con clave GPG en el Dockerfile, que se utiliza para verificar el código fuente de Python cargado.



▍Dependencias de tiempo de ejecución



Python necesita algunos paquetes adicionales para funcionar:



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


El primer paquete ,, ca-certificatescontiene una lista de certificados CA estándar. El navegador utiliza algo similar para validar las URL . Esto permite que Python wgety otras herramientas validen los certificados proporcionados por los servidores.



El segundo paquete, netbaseque realiza la instalación en /etcvarios archivos, es necesario para configurar el mapeo de ciertos nombres a ciertos puertos y protocolos. Por ejemplo, /etc/serviceses responsable de configurar la correspondencia de los nombres de los servicios, como https, con los números de puerto. En este caso, lo es 443/tcp.



▍Instalar Python



Ahora se está instalando el kit de herramientas de compilación. Es decir, la fuente de Python se descarga y compila, y luego se desinstalan los paquetes Debian innecesarios:



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


Están sucediendo muchas cosas aquí, pero lo más importante es esto:



  1. Python está instalado en /usr/local.
  2. Se eliminan todos los archivos .pyc.
  3. Los paquetes, en particular, gccy otros del tipo que se necesitaban para compilar Python, se eliminan cuando ya no son necesarios.


Debido a que todo esto sucede en un solo comando RUN, como resultado, el compilador no se almacena en ninguna de las capas, lo que ayuda a mantener un tamaño de imagen compacto.



Aquí puede notar que Python necesita una biblioteca para compilar libbluetooth-dev. Esto me pareció inusual, así que decidí resolverlo. Resulta que Python puede crear sockets Bluetooth, pero solo si se compila con esta biblioteca.



▍Configuración de enlace simbólico



El siguiente paso /usr/local/bin/python3es asignar un enlace simbólico /usr/local/bin/python, que permite invocar a Python de diferentes formas:



#     ,     
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



El administrador de paquetes piptiene su propio programa de lanzamiento que difiere del programa de lanzamiento de Python. Por ejemplo, este Dockerfile instala Python 3.8.5, lanzado en julio de 2020. Y pip 20.2.2 se lanzó en agosto, después del lanzamiento de Python, pero el Dockerfile está diseñado para tener una nueva versión 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


Después de completar estas operaciones, como antes, se eliminan todos los archivos .pyc.



▍Punto de entrada de la imagen



Como resultado, el punto de entrada a la imagen se especifica en el Dockerfile:



CMD ["python3"]


Usando en su CMDlugar, ENTRYPOINTnosotros, al lanzar la imagen, obtenemos acceso a Python por defecto:



$ 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.
>>>


Pero, si es necesario, puede especificar otros archivos ejecutables al iniciar la imagen:



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


Salir



Esto es lo que aprendimos al analizar el Dockerfile de la imagen oficial de Python slim-buster.



▍La imagen incluye Python



Si bien esto puede parecer obvio, vale la pena prestar atención a cómo se incluye exactamente Python en la imagen. Es decir, esto se hace instalándolo en /usr/local.



Los programadores que usan esta imagen a veces cometen el mismo error, que es reinstalar la versión Debian de Python:



FROM python:3.8-slim-buster

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


Cuando ejecute este comando, RUNPython se instalará nuevamente, pero /usrno en /usr/local. Y esta no suele ser la versión de Python que está instalada en /usr/local. Y el programador que usó el archivo Docker anterior probablemente no necesite dos versiones diferentes de Python en la misma imagen. Esta es principalmente la razón de la confusión.



Y si alguien realmente necesita una versión Debian de Python, entonces sería mejor usarla como imagen base debian:buster-slim.



▍ La imagen incluye la última versión de pip



Por ejemplo, la versión más reciente de Python 3.5 fue en noviembre de 2019, pero la imagen de Docker python:3.5-slim-busterincluye la pipque salió en agosto de 2020. Esto (generalmente) es bueno, ya que significa que tenemos las últimas correcciones de errores y mejoras de rendimiento. Esto también significa que podemos beneficiarnos de la compatibilidad con opciones de ruedas más nuevas.



▍ Todos los archivos .pyc se eliminan de la imagen



Si desea acelerar un poco el arranque del sistema, puede compilar de forma independiente el código fuente de la biblioteca estándar en formato .pyc. Esto se hace usando el módulo compileall .



▍Image no instala actualizaciones de seguridad de Debian



Aunque las imágenes de base debian:buster-slim, y pythonse actualiza con frecuencia, hay una cierta diferencia entre el lanzamiento de las actualizaciones de seguridad y convertirlos en imágenes. Por lo tanto, debe instalar actualizaciones de seguridad de forma independiente para la distribución básica de Linux.



¿Qué imágenes de Docker usas para ejecutar código Python?






All Articles