Alpine compilează imagini Docker pentru Python de 50 de ori mai încet, iar imaginile sunt de două ori mai mari

Alpine compilează imagini Docker pentru Python de 50 de ori mai încet, iar imaginile sunt de două ori mai mari

Alpine Linux este adesea recomandat ca imagine de bază pentru Docker. Vi se spune că utilizarea Alpine va face imaginile mai mici și procesul de compilare mai rapid.

Dar dacă folosiți Alpine Linux pentru aplicațiile Python, atunci el:

  • Vă încetinește multă compilarea
  • Vă crește dimensiunea imaginilor
  • Vă consumă timpul
  • Și în cele din urmă poate cauza erori în runtime


Să vedem de ce se recomandă Alpine, dar de ce nu ar trebui să-l utilizați împreună cu Python.

De ce oamenii recomandă Alpine?

Să presupunem că avem nevoie de gcc ca parte a imaginii noastre și dorim să comparăm Alpine Linux cu Ubuntu 18.04, în ceea ce privește viteza de compilare și dimensiunea finală a imaginii.

Pentru început, să descărcăm cele două imagini și să le comparăm dimensiunea:

$ 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

După cum vedeți, imaginea de bază pentru Alpine este mult mai mică. Să încercăm acum să instalăm gcc și să începem cu 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/*

Scrierea unui Dockerfile perfect depășește cadrul acestui articol

Să măsurăm viteza de compilare:

$ 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

Repetăm totul pentru Alpine (Dockerfile):

FROM alpine
RUN apk add --update gcc

Compilăm, vedem timpul și dimensiunea compilării:

$ 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

Așa cum s-a promis, imaginile pe baza Alpine se compilează mai repede și sunt mai mici: 15 secunde în loc de 30 și dimensiunea imaginii 105MB față de 150MB. Este destul de bine!

Dar dacă trecem la compilarea unei aplicații Python, lucrurile nu mai sunt atât de optimiste.

Imaginea Python

Aplicațiile Python folosesc adesea pandas și matplotlib. Prin urmare, una dintre opțiuni este să luăm imaginea oficială bazată pe Debian, folosind un astfel de Dockerfile:

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

O compilăm:

$ docker build -f Dockerfile.slim -t python-matpan.
Trimiterea contextului de construcție către daemon-ul Docker  3.072kB
Pasul 1/2 : FROM python:3.8-slim
 ---> 036ea1506a85
Pasul 2/2 : RUN pip install --no-cache-dir matplotlib pandas
 ---> Se rulează în 13739b2a0917
Colectarea matplotlib
  Descărcare matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl (13.1 MB)
Colectarea pandas
  Descărcare pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl (10.4 MB)
...
Construcție realizată cu succes b98b5dc06690
Etichetată cu succes python-matpan:latest

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

Obținem o imagine de dimensiunea 363MB.
Va fi mai bun cu Alpine? Hai să încercăm:

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

$ docker build -t python-matpan-alpine -f Dockerfile.alpine .                                 
Trimiterea contextului de construcție către daemon-ul Docker  3.072kB                                               
Pasul 1/2 : FROM python:3.8-alpine                                                             
 ---> a0ee0c90a0db                                                                            
Pasul 2/2 : RUN pip install --no-cache-dir matplotlib pandas                                                  
 ---> Se rulează în 6740adad3729                                                                 
Colectarea matplotlib                                                                         
  Descărcare matplotlib-3.1.2.tar.gz (40.9 MB)                                               
    EROARE: Comanda a eșuat cu codul de ieșire 1:                                            
     comanda: /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                              

...
EROARE: Comanda a eșuat cu codul de ieșire 1: python setup.py egg_info Consultați jurnalele pentru ieșirea completă a comenzii.
Comanda '/bin/sh -c pip install matplotlib pandas' a returnat un cod non-zero: 1

Ce se întâmplă?

Alpine nu suportă wheels

Dacă te uiți la build-ul bazat pe Debian, vei observa că descarcă matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl.

Acesta este un binar pentru wheel. Alpine descarcă sursele `matplotlib-3.1.2.tar.gz`, deoarece nu suportă standardul wheels.

De ce? Majoritatea distribuțiilor Linux folosesc versiunea GNU (glibc) a bibliotecii C standard, care este de fapt necesară pentru fiecare program scris în C, inclusiv Python. Dar Alpine folosește `musl`, și deoarece acele binare sunt destinate `glibc`, pur și simplu nu sunt o opțiune.

Prin urmare, dacă folosești Alpine, trebuie să compilezi tot codul scris în C din fiecare pachet Python.

Ah, da, lista tuturor acestor dependențe care trebuie compilate va trebui să o cauți singur.
În acest caz obținem următoarele:

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

Și timpul de build durează...

… 25 de minute și 57 de secunde! Iar dimensiunea imaginii este 851MB.

Imaginile bazate pe Alpine se compilează mult mai lent, sunt mai mari și va trebui să căutați toate dependențele. Puteți, desigur, să reduceți dimensiunea compilării folosind construcții multi-stage dar asta înseamnă că va trebui să faceți și mai multă muncă.

Asta nu e tot!

Alpine poate fi cauza unor bug-uri neașteptate în runtime

  • În teorie, musl este compatibil cu glibc, dar în practică, diferențele pot cauza multe probleme. Și dacă există, atunci cu siguranță neplăcute. Iată câteva probleme care pot apărea:
  • Alpine are, în mod implicit, o dimensiune mai mică a stivei de thread-uri, ceea ce poate duce la erori în Python
  • Unii utilizatori au descoperit că aplicațiile Python funcționează mai lent din cauza modului în care musl alocă memorie (care diferă de glibc).
  • Un utilizator a descoperit o eroare la formatarea datei

Cu siguranță, aceste erori au fost deja corectate, dar cine știe câte mai sunt.

Nu folosiți imagini Alpine pentru Python

Dacă nu doriți să vă ocupați cu construcții mari și lungi, căutarea de dependențe și potențiale erori — nu folosiți Alpine Linux ca și imagine de bază. Alegerea unei imagini de bază bune.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster