¿Por qué se necesitan años para instalar Pandas en Alpine Linux?

103

Me di cuenta de que instalar Pandas y Numpy (su dependencia) en un contenedor Docker usando el sistema operativo base Alpine vs. CentOS o Debian lleva mucho más tiempo. Creé una pequeña prueba a continuación para demostrar la diferencia horaria. Aparte de los pocos segundos que Alpine tarda en actualizar y descargar las dependencias de compilación para instalar Pandas y Numpy, ¿por qué setup.py tarda unas 70 veces más que en la instalación de Debian?

¿Hay alguna forma de acelerar la instalación usando Alpine como imagen base o hay otra imagen base de tamaño comparable a Alpine que sea mejor para usar con paquetes como Pandas y Numpy?

Dockerfile.debian

FROM python:3.6.4-slim-jessie

RUN pip install pandas

Cree una imagen de Debian con Pandas y Numpy:

[PandasDockerTest] time docker build -t debian-pandas -f Dockerfile.debian . --no-cache
    Sending build context to Docker daemon  3.072kB
    Step 1/2 : FROM python:3.6.4-slim-jessie
     ---> 43431c5410f3
    Step 2/2 : RUN pip install pandas
     ---> Running in 2e4c030f8051
    Collecting pandas
      Downloading pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl (26.2MB)
    Collecting numpy>=1.9.0 (from pandas)
      Downloading numpy-1.14.1-cp36-cp36m-manylinux1_x86_64.whl (12.2MB)
    Collecting pytz>=2011k (from pandas)
      Downloading pytz-2018.3-py2.py3-none-any.whl (509kB)
    Collecting python-dateutil>=2 (from pandas)
      Downloading python_dateutil-2.6.1-py2.py3-none-any.whl (194kB)
    Collecting six>=1.5 (from python-dateutil>=2->pandas)
      Downloading six-1.11.0-py2.py3-none-any.whl
    Installing collected packages: numpy, pytz, six, python-dateutil, pandas
    Successfully installed numpy-1.14.1 pandas-0.22.0 python-dateutil-2.6.1 pytz-2018.3 six-1.11.0
    Removing intermediate container 2e4c030f8051
     ---> a71e1c314897
    Successfully built a71e1c314897
    Successfully tagged debian-pandas:latest
    docker build -t debian-pandas -f Dockerfile.debian . --no-cache  0.07s user 0.06s system 0% cpu 13.605 total

Dockerfile.alpine

FROM python:3.6.4-alpine3.7

RUN apk --update add --no-cache g++

RUN pip install pandas

Crea una imagen alpina con Pandas y Numpy:

[PandasDockerTest] time docker build -t alpine-pandas -f Dockerfile.alpine . --no-cache
Sending build context to Docker daemon   16.9kB
Step 1/3 : FROM python:3.6.4-alpine3.7
 ---> 4b00a94b6f26
Step 2/3 : RUN apk --update add --no-cache g++
 ---> Running in 4b0c32551e3f
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
(1/17) Upgrading musl (1.1.18-r2 -> 1.1.18-r3)
(2/17) Installing libgcc (6.4.0-r5)
(3/17) Installing libstdc++ (6.4.0-r5)
(4/17) Installing binutils-libs (2.28-r3)
(5/17) Installing binutils (2.28-r3)
(6/17) Installing gmp (6.1.2-r1)
(7/17) Installing isl (0.18-r0)
(8/17) Installing libgomp (6.4.0-r5)
(9/17) Installing libatomic (6.4.0-r5)
(10/17) Installing pkgconf (1.3.10-r0)
(11/17) Installing mpfr3 (3.1.5-r1)
(12/17) Installing mpc1 (1.0.3-r1)
(13/17) Installing gcc (6.4.0-r5)
(14/17) Installing musl-dev (1.1.18-r3)
(15/17) Installing libc-dev (0.7.1-r0)
(16/17) Installing g++ (6.4.0-r5)
(17/17) Upgrading musl-utils (1.1.18-r2 -> 1.1.18-r3)
Executing busybox-1.27.2-r7.trigger
OK: 184 MiB in 50 packages
Removing intermediate container 4b0c32551e3f
 ---> be26c3bf4e42
Step 3/3 : RUN pip install pandas
 ---> Running in 36f6024e5e2d
Collecting pandas
  Downloading pandas-0.22.0.tar.gz (11.3MB)
Collecting python-dateutil>=2 (from pandas)
  Downloading python_dateutil-2.6.1-py2.py3-none-any.whl (194kB)
Collecting pytz>=2011k (from pandas)
  Downloading pytz-2018.3-py2.py3-none-any.whl (509kB)
Collecting numpy>=1.9.0 (from pandas)
  Downloading numpy-1.14.1.zip (4.9MB)
Collecting six>=1.5 (from python-dateutil>=2->pandas)
  Downloading six-1.11.0-py2.py3-none-any.whl
Building wheels for collected packages: pandas, numpy
  Running setup.py bdist_wheel for pandas: started
  Running setup.py bdist_wheel for pandas: still running...
  Running setup.py bdist_wheel for pandas: still running...
  Running setup.py bdist_wheel for pandas: still running...
  Running setup.py bdist_wheel for pandas: still running...
  Running setup.py bdist_wheel for pandas: still running...
  Running setup.py bdist_wheel for pandas: still running...
  Running setup.py bdist_wheel for pandas: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/e8/ed/46/0596b51014f3cc49259e52dff9824e1c6fe352048a2656fc92
  Running setup.py bdist_wheel for numpy: started
  Running setup.py bdist_wheel for numpy: still running...
  Running setup.py bdist_wheel for numpy: still running...
  Running setup.py bdist_wheel for numpy: still running...
  Running setup.py bdist_wheel for numpy: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/9d/cd/e1/4d418b16ea662e512349ef193ed9d9ff473af715110798c984
Successfully built pandas numpy
Installing collected packages: six, python-dateutil, pytz, numpy, pandas
Successfully installed numpy-1.14.1 pandas-0.22.0 python-dateutil-2.6.1 pytz-2018.3 six-1.11.0
Removing intermediate container 36f6024e5e2d
 ---> a93c59e6a106
Successfully built a93c59e6a106
Successfully tagged alpine-pandas:latest
docker build -t alpine-pandas -f Dockerfile.alpine . --no-cache  0.54s user 0.33s system 0% cpu 16:08.47 total
moku
fuente
1
.apk ahora disponible, por lo que no es necesario compilar desde el código fuente - pkgs.alpinelinux.org/packages?name= * pandas & branch = edge
jtlz2
1
@ jtlz2, pandas no está disponible en el borde de la rama de Alpine. lo cual es una lástima ...
fccoelho
@fccoelho ¡Está disponible de nuevo ahora!
jtlz2

Respuestas:

65

Las imágenes basadas en Debian se usan solo python pippara instalar paquetes con .whlformato:

  Downloading pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl (26.2MB)
  Downloading numpy-1.14.1-cp36-cp36m-manylinux1_x86_64.whl (12.2MB)

El formato WHL se desarrolló como un método más rápido y confiable de instalar software Python que la reconstrucción desde el código fuente cada vez. Los archivos WHL solo deben moverse a la ubicación correcta en el sistema de destino para ser instalados, mientras que una distribución de origen requiere un paso de compilación antes de la instalación.

Los paquetes de ruedas pandasy numpyno son compatibles con imágenes basadas en la plataforma Alpine. Es por eso que cuando los instalamos usando python pipdurante el proceso de construcción, siempre los compilamos a partir de los archivos fuente en alpine:

  Downloading pandas-0.22.0.tar.gz (11.3MB)
  Downloading numpy-1.14.1.zip (4.9MB)

y podemos ver el siguiente contenedor interior durante la construcción de la imagen:

/ # ps aux
PID   USER     TIME   COMMAND
    1 root       0:00 /bin/sh -c pip install pandas
    7 root       0:04 {pip} /usr/local/bin/python /usr/local/bin/pip install pandas
   21 root       0:07 /usr/local/bin/python -c import setuptools, tokenize;__file__='/tmp/pip-build-en29h0ak/pandas/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n
  496 root       0:00 sh
  660 root       0:00 /bin/sh -c gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -DTHREAD_STACK_SIZE=0x100000 -fPIC -Ibuild/src.linux-x86_64-3.6/numpy/core/src/pri
  661 root       0:00 gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -DTHREAD_STACK_SIZE=0x100000 -fPIC -Ibuild/src.linux-x86_64-3.6/numpy/core/src/private -Inump
  662 root       0:00 /usr/libexec/gcc/x86_64-alpine-linux-musl/6.4.0/cc1 -quiet -I build/src.linux-x86_64-3.6/numpy/core/src/private -I numpy/core/include -I build/src.linux-x86_64-3.6/numpy/core/includ
  663 root       0:00 ps aux

Si modificamos Dockerfileun poco:

FROM python:3.6.4-alpine3.7
RUN apk add --no-cache g++ wget
RUN wget https://pypi.python.org/packages/da/c6/0936bc5814b429fddb5d6252566fe73a3e40372e6ceaf87de3dec1326f28/pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl
RUN pip install pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl

obtenemos el siguiente error:

Step 4/4 : RUN pip install pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl
 ---> Running in 0faea63e2bda
pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl is not a supported wheel on this platform.
The command '/bin/sh -c pip install pandas-0.22.0-cp36-cp36m-manylinux1_x86_64.whl' returned a non-zero code: 1

Desafortunadamente, la única forma de instalar pandasuna imagen de Alpine es esperar hasta que finalice la compilación.

Por supuesto, si desea usar la imagen de Alpine con pandasCI, por ejemplo, la mejor manera de hacerlo es compilarla una vez, enviarla a cualquier registro y usarla como imagen base para sus necesidades.

EDITAR: Si desea usar la imagen de Alpine con pandas, puede extraer mi imagen de la ventana acoplable nickgryg / alpine-pandas . Es una imagen de Python precompilada pandasen la plataforma Alpine. Debería ahorrarle tiempo.

Nickgryg
fuente
3
Bueno, eso es muy malo. Sin embargo, parece que six, pytz y python-dateutil están descargando paquetes .whl en Alpine. ¿Eso significa que es posible construir ruedas para pandas y numpy para Alpine, pero que simplemente no está sucediendo actualmente?
moku
No, no es posible construir ruedas para pandasy nampysobre plataforma alpina. Esas ruedas no lo soportan. Mostré eso en la respuesta, cuando intenté instalar pandasdesde su paquete de ruedas en una imagen alpina.
nickgryg
@Nickolay ¿Existe una forma alternativa de reciclar una pandascompilación que se ha construido alpiney luego almacenada en caché? (esto podría estar alojado en algún lugar localmente)
jtlz2
2
La razón por la que esto es así es porque estas ruedas contienen binarios compilados a partir de c / c ++ y vinculados con glibc, pero alpine no tiene glibc, sino que usa musl, lo que significa que los nuevos binarios deben compilarse y vincularse contra musl.
ThisGuyCantEven
36

RESPUESTA: A PARTIR DEL 9/03/2020, ¡PARA PYTHON 3, TODAVÍA NO ES!

Aquí hay un Dockerfile completo en funcionamiento:

FROM python:3.7-alpine
RUN echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk add --update --no-cache py3-numpy py3-pandas@testing

La compilación es muy sensible a los números exactos de la versión de Python y Alpine; hacerlos mal parece provocar el error de Max Levy, so:libpython3.7m.so.1.0 (missing)pero lo anterior ahora funciona para mí.

Mi Dockerfile actualizado está disponible en https://gist.github.com/jtlz2/b0f4bc07ce2ff04bc193337f2327c13b


[Actualización anterior:]

RESPUESTA: ¡NO ES!

En cualquier Alpine Dockerfile puede simplemente hacer *

RUN apk add py2-numpy@community py2-scipy@community py-pandas@edge

Esto se debe a que numpy, scipyy ahora pandastodos están disponibles precompilados en alpine:

https://pkgs.alpinelinux.org/packages?name=*numpy

https://pkgs.alpinelinux.org/packages?name=*scipy&branch=edge

https://pkgs.alpinelinux.org/packages?name=*pandas&branch=edge

Una forma de evitar la reconstrucción cada vez, o el uso de una capa de Docker, es usar un .apkpaquete / Alpine Linux nativo precompilado , por ejemplo

https://github.com/sgerrand/alpine-pkg-py-pandas

https://github.com/nbgallery/apks

Puede compilar estos .apks una vez y usarlos en cualquier lugar de su Dockerfile que desee :)

Esto también le ahorra tener que hornear todo lo demás en la imagen de Docker antes del hecho, es decir, la flexibilidad para preconstruir cualquier imagen de Docker que desee.

PD: he puesto un código auxiliar de Dockerfile en https://gist.github.com/jtlz2/b0f4bc07ce2ff04bc193337f2327c13b que muestra aproximadamente cómo construir la imagen. Estos incluyen los pasos importantes (*):

RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
RUN apk update
RUN apk add --update --no-cache libgfortran
jtlz2
fuente
2
¿Parece que se eliminó recientemente? pkgs.alpinelinux.org/package/edge/testing/x86/py-pandas
jtlz2
1
@ChrisWedgwood Están trabajando activamente en ello - ver github.com/alpinelinux/aports/pull/6330
jtlz2
1
@ChrisWedgwood Trabajando de nuevo, ¡uf!
jtlz2
1
@ jtlz2 Cambié a 3.7-slim-buster y todo salió bien allí pythonspeed.com/articles/base-image-python-docker-images
xristian
9

ATENCIÓN
Mira la respuesta de @ jtlz2 con la última actualización

ANTICUADO

Entonces, los paquetes py3-pandas y py3-numpy se movieron al repositorio de prueba alpine, por lo que puede descargarlo agregando estas líneas a su Dockerfile:

RUN echo "http://dl-8.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
  && apk update \
  && apk add py3-numpy py3-pandas

¡Espero que ayude a alguien!

Enlaces de paquetes alpinos:
- py3-pandas
- py3-numpy

Información de muelles de repositorios alpinos .

Stefanitsky
fuente
¡Esto funcionó para mí! ¡Gracias por proporcionar una respuesta actualizada!
Stratus3D
2
Corregido en mi respuesta
jtlz2
1
@ jtlz2 genial, gracias, pero me mudé al debian buster en lugar de alpine y no intenté instalarlo de nuevo con alpine, pero de todos modos, gracias por la respuesta, también arreglé mi respuesta
stefanitsky
1
Solo para tener en cuenta que py3-pandas no está disponible para 3.11.x, solo está en la versión 'edge' en el momento en que escribo este comentario. editar: Obviamente dice que en la publicación anterior, me perdí esa referencia antes, lo siento.
podrido
5

Solo voy a reunir algunas de estas respuestas en una sola respuesta y agregar un detalle que creo que se perdió. La razón por la que ciertas bibliotecas de Python, particularmente las bibliotecas de datos y matemáticas optimizadas, tardan tanto en construirse en alpine es porque las ruedas pip de estas bibliotecas incluyen binarios precompilados de c / c ++ y vinculados a glibcun conjunto común de bibliotecas estándar de c. Debian, Fedora, CentOS todos (normalmente) usan glibc, pero alpine, para mantenerse livianos, usa en su musl-libclugar. Los binarios de c / c ++ construidos en un glibcsistema no funcionarán en un sistema sin ellos glibcy lo mismo ocurre musl.

Pip busca primero una rueda con los binarios correctos, si no puede encontrar uno, intenta compilar los binarios de la fuente c / c ++ y los vincula con musl. En muchos casos, esto ni siquiera funcionará a menos que tenga los encabezados de Python python3-devo compile herramientas como make.

Ahora, el lado positivo, como otros han mencionado, hay apkpaquetes con los binarios adecuados proporcionados por la comunidad, usarlos le ahorrará el (a veces largo) proceso de construcción de los binarios.

ThisGuyCantEven
fuente
5

Un consejo realmente honesto aquí, cambie a una imagen basada en Debian y luego todos sus problemas desaparecerán.

Alpine para aplicaciones de Python no funciona bien.

Aquí hay un ejemplo de mi dockerfile:

FROM python:3.7.6-buster

RUN pip install pandas==1.0.0
RUN pip install sklearn
RUN pip install Django==3.0.2
RUN pip install cx_Oracle==7.3.0
RUN pip install excel
RUN pip install djangorestframework==3.11.0

El python:3.7.6-busteres más apropiado en este caso, además, no se necesita ninguna dependencia adicional en el sistema operativo.

Siga un artículo útil y reciente: https://pythonspeed.com/articles/alpine-docker-python/ :

No use Alpine Linux para imágenes de Python A menos que desee tiempos de compilación mucho más lentos, imágenes más grandes, más trabajo y la posibilidad de errores oscuros, querrá evitar Alpine Linux como imagen base. Para obtener algunas recomendaciones sobre lo que debe usar, consulte mi artículo sobre cómo elegir una buena imagen base.

Flávio Henrique
fuente
1
Puede reducir el número de capas en su imagen, es decir, EJECUTAR pip install <packegeA> && pip install <packageB> y así sucesivamente en lugar de usar un bloque de comandos RUN. Afecta el rendimiento de tu compilación :)
p0l00ck
También puede utilizar pip --no-cachepara reducir un poco más la huella. Lo que realmente debería hacer es ponerlos línea por línea en un requirements.txtarchivo ypip install --no-cache -r requirements.txt
ThisGuyCantEven
1

Esto funcionó para mí:

FROM python:3.8-alpine
RUN echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk add --update --no-cache py3-numpy py3-pandas@testing
ENV PYTHONPATH=/usr/lib/python3.8/site-packages

COPY . /app
WORKDIR /app

RUN pip install -r requirements.txt

EXPOSE 5003 
ENTRYPOINT [ "python" ] 
CMD [ "app.py" ]

La mayor parte del código aquí es de la respuesta de jtlz2 de este mismo hilo y Faylixe de otro hilo.

Resulta que la versión más ligera de pandas se encuentra en el repositorio de Alpine, py3-numpypero no se instala en la misma ruta de archivo desde donde Python lee las importaciones de forma predeterminada. Por lo tanto, debe agregar el ENV. También tenga en cuenta la versión alpina.

Bishwas Mishra
fuente
0

pandasse considera un paquete respaldado por la comunidad, por lo que las respuestas que apuntan edge/testingno funcionarán ya que Alpine no admite oficialmente pandas como paquete principal (todavía funciona, simplemente no es compatible con los desarrolladores principales de Alpine).

Prueba este Dockerfile:

FROM python:3.8-alpine
RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
&& apk add py3-pandas@community

Esto también funciona para la imagen vainilla de Alpine, usando FROM alpine:3.12.

Nivel Z4
fuente
-1

alpine lleva mucho tiempo instalar pandas y el tamaño de la imagen también es enorme. Probé la versión python: 3.8-slim-buster de la imagen base de Python. La creación de la imagen fue muy rápida y el tamaño de la imagen fue inferior a la mitad en comparación con la imagen del acoplador de pitón alpino

https://github.com/dguyhasnoname/k8s-cluster-checker/blob/master/Dockerfile

Mukund Sharma
fuente