Alpine compila compilações Docker para Python 50 vezes mais lentas e imagens são 2 vezes mais pesadas

Alpine compila compilações Docker para Python 50 vezes mais lentas e imagens são 2 vezes mais pesadas

Alpine Linux é frequentemente recomendado como imagem base para Docker. Disseram a você que usar o Alpine tornará suas construções menores e seu processo de construção mais rápido.

Mas se você usar Alpine Linux para aplicativos Python, então:

  • Torna suas construções muito mais lentas
  • Torna suas imagens maiores
  • Desperdiçando seu tempo
  • E no final pode causar erros em tempo de execução


Vejamos por que o Alpine é recomendado, mas por que você ainda não deve usá-lo com Python.

Por que as pessoas recomendam Alpine?

Vamos supor que precisamos do gcc como parte de nossa imagem e queremos comparar o Alpine Linux com o Ubuntu 18.04 em termos de velocidade de construção e tamanho final da imagem.

Primeiro, vamos baixar duas imagens e comparar seus tamanhos:

$ docker pull --quiet ubuntu:18.04
docker.io/library/ubuntu:18.04
$ docker pull --quiet alpine
docker.io/library/alpine:latest
$ docker image ls ubuntu:18.04
REPOSITORY          TAG        IMAGE ID         SIZE
ubuntu              18.04      ccc6e87d482b     64.2MB
$ docker image ls alpine
REPOSITORY          TAG        IMAGE ID         SIZE
alpine              latest     e7d92cdc71fe     5.59MB

Como você pode ver, a imagem base do Alpine é muito menor. Vamos agora tentar instalar o gcc e começar com o Ubuntu:

FROM ubuntu:18.04
RUN apt-get update && 
    apt-get install --no-install-recommends -y gcc && 
    apt-get clean && rm -rf /var/lib/apt/lists/*

Escrever o Dockerfile perfeito está além do escopo deste artigo.

Vamos medir a velocidade de montagem:

$ time docker build -t ubuntu-gcc -f Dockerfile.ubuntu --quiet .
sha256:b6a3ee33acb83148cd273b0098f4c7eed01a82f47eeb8f5bec775c26d4fe4aae

real    0m29.251s
user    0m0.032s
sys     0m0.026s
$ docker image ls ubuntu-gcc
REPOSITORY   TAG      IMAGE ID      CREATED         SIZE
ubuntu-gcc   latest   b6a3ee33acb8  9 seconds ago   150MB

Repetimos o mesmo para Alpine (Dockerfile):

FROM alpine
RUN apk add --update gcc

Montamos, veja o tempo e tamanho da montagem:

$ time docker build -t alpine-gcc -f Dockerfile.alpine --quiet .
sha256:efd626923c1478ccde67db28911ef90799710e5b8125cf4ebb2b2ca200ae1ac3

real    0m15.461s
user    0m0.026s
sys     0m0.024s
$ docker image ls alpine-gcc
REPOSITORY   TAG      IMAGE ID       CREATED         SIZE
alpine-gcc   latest   efd626923c14   7 seconds ago   105MB

Como prometido, as imagens baseadas em Alpine são construídas mais rapidamente e são menores: 15 segundos em vez de 30 e o tamanho da imagem é de 105 MB contra 150 MB. É muito bom!

Mas se mudarmos para a construção de um aplicativo Python, então nem tudo será tão otimista.

Imagem Python

Os aplicativos Python geralmente usam pandas e matplotlib. Portanto, uma opção é pegar a imagem oficial baseada no Debian usando este Dockerfile:

FROM python:3.8-slim
RUN pip install --no-cache-dir matplotlib pandas

Vamos coletar:

$ docker build -f Dockerfile.slim -t python-matpan.
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM python:3.8-slim
 ---> 036ea1506a85
Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas
 ---> Running in 13739b2a0917
Collecting matplotlib
  Downloading matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl (13.1 MB)
Collecting pandas
  Downloading pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl (10.4 MB)
...
Successfully built b98b5dc06690
Successfully tagged python-matpan:latest

real    0m30.297s
user    0m0.043s
sys     0m0.020s

Obtemos uma imagem de 363 MB de tamanho.
Faremos melhor com a Alpine? Vamos tentar:

FROM python:3.8-alpine
RUN pip install --no-cache-dir matplotlib pandas

$ docker build -t python-matpan-alpine -f Dockerfile.alpine .                                 
Sending build context to Docker daemon  3.072kB                                               
Step 1/2 : FROM python:3.8-alpine                                                             
 ---> a0ee0c90a0db                                                                            
Step 2/2 : RUN pip install --no-cache-dir matplotlib pandas                                                  
 ---> Running in 6740adad3729                                                                 
Collecting matplotlib                                                                         
  Downloading matplotlib-3.1.2.tar.gz (40.9 MB)                                               
    ERROR: Command errored out with exit status 1:                                            
     command: /usr/local/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/
tmp/pip-install-a3olrixa/matplotlib/setup.py'"'"'; __file__='"'"'/tmp/pip-install-a3olrixa/matplotlib/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'rn'"'"', '"'"'n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-a3olrixa/matplotlib/pip-egg-info                              

...
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
The command '/bin/sh -c pip install matplotlib pandas' returned a non-zero code: 1

O que está acontecendo?

Alpine não suporta rodas

Se você olhar a compilação, que é baseada no Debian, verá que ela baixa matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.enquanto.

Este é um binário para roda. Alpine baixa as fontes `matplotlib-3.1.2.tar.gz` já que não suporta padrão rodas.

Por que? A maioria das distribuições Linux usa a versão GNU (glibc) da biblioteca padrão C, que é de fato exigida por todos os programas escritos em C, incluindo Python. Mas Alpine usa `musl`, e como esses binários são projetados para `glibc`, eles simplesmente não são uma opção.

Portanto, se você usar Alpine, precisará compilar todo o código escrito em C em cada pacote Python.

Ah, sim, você mesmo terá que procurar a lista de todas essas dependências que precisam ser compiladas.
Neste caso obtemos isto:

FROM python:3.8-alpine
RUN apk --update add gcc build-base freetype-dev libpng-dev openblas-dev
RUN pip install --no-cache-dir matplotlib pandas

E o tempo de construção leva...

... 25 minutos e 57 segundos! E o tamanho da imagem é 851 MB.

As imagens baseadas em Alpine demoram muito mais para serem construídas, são maiores e você ainda precisa procurar todas as dependências. É claro que você pode reduzir o tamanho da montagem usando construções de vários estágios mas isso significa que ainda mais trabalho precisa ser feito.

Isso não é tudo!

Alpine pode causar bugs inesperados em tempo de execução

  • Em teoria, o musl é compatível com o glibc, mas na prática as diferenças podem causar muitos problemas. E se forem, provavelmente serão desagradáveis. Aqui estão alguns problemas que podem surgir:
  • Alpine tem um tamanho de pilha de threads menor por padrão, o que pode levar a erros em Python
  • Alguns usuários descobriram que Aplicativos Python são mais lentos por causa da maneira como musl aloca memória (diferente de glibc).
  • Um dos usuários encontrou um erro ao formatar a data

Certamente estes erros já foram corrigidos, mas quem sabe quantos mais serão.

Não use imagens Alpine para Python

Se você não quer se preocupar com compilações grandes e demoradas, procurando dependências e possíveis erros, não use o Alpine Linux como imagem base. Escolhendo uma boa imagem base.

Fonte: habr.com

Adicionar um comentário