Alpine erstellt Docker-Bauten unter Python 50-mal langsamer, und die Images sind doppelt so schwer

Alpine erstellt Docker-Bauten unter Python 50-mal langsamer, und die Images sind doppelt so schwer

Alpine Linux — häufig empfohlen als Basisimage für Docker. Ihnen wird gesagt, dass die Verwendung von Alpine Ihre Builds kleiner macht und der Build-Prozess schneller ist.

Aber wenn Sie Alpine Linux für Python-Anwendungen verwenden, dann:

  • Macht Ihre Builds deutlich langsamer.
  • Macht Ihre Images größer.
  • Verschwendet Ihre Zeit.
  • Und kann letztendlich zu Laufzeitfehlern führen.


Lassen Sie uns betrachten, warum Alpine empfohlen wird, aber warum Sie es dennoch nicht zusammen mit Python verwenden sollten.

Warum empfehlen die Leute Alpine?

Angenommen, wir benötigen gcc als Teil unseres Images und möchten Alpine Linux mit Ubuntu 18.04 hinsichtlich der Build-Geschwindigkeit und der Endgröße des Images vergleichen.

Zunächst laden wir zwei Images herunter und vergleichen ihre Größe:

$ 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

Wie Sie sehen, ist das Basisimage für Alpine viel kleiner. Lassen Sie uns jetzt versuchen, gcc zu installieren und beginnen wir mit 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/*

Das Schreiben perfekter Dockerfiles geht über diesen Artikel hinaus.

Messen wir die Build-Geschwindigkeit:

$ 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 Sekunden zuvor   150MB

Wiederholen Sie dasselbe für Alpine (Dockerfile):

FROM alpine
RUN apk add --update gcc

Bauen wir, schauen wir auf die Zeit und die Größe des Builds:

$ 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 Sekunden zuvor    105MB

Wie versprochen, bauen sich die von Alpine basierenden Images schneller und sind selbst kleiner: 15 Sekunden statt 30 und die Größe des Images beträgt 105MB gegenüber 150MB. Das ist ziemlich gut!

Aber wenn wir zur Erstellung einer Python-Anwendung wechseln, sieht es nicht so rosig aus.

Python-Image

Python-Anwendungen verwenden häufig pandas und matplotlib. Daher ist eine Option, das offizielle Image auf Debian-Basis zu verwenden, indem wir folgendes Dockerfile verwenden:

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

Bauen wir es:

$ 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

Wir erhalten ein Image mit einer Größe von 363 MB.
Wird es mit Alpine besser? Lassen Sie uns das versuchen:

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 Überprüfen Sie die Protokolle für die vollständige Befehlsausgabe.
Der Befehl '/bin/sh -c pip install matplotlib pandas' gab einen ungleich Null-Code zurück: 1

Was passiert?

Alpine unterstützt keine Wheels

Wenn Sie sich das Docker-Image ansehen, das auf Debian basiert, werden Sie sehen, dass es matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64 herunterlädt.whl.

Das ist das Binary für Wheel. Alpine lädt hingegen den Quellcode `matplotlib-3.1.2.tar.gz` herunter, da es die standardmäßigenwheelsnicht unterstützt. Warum? Die meisten Linux-Distributionen verwenden die GNU-Version (glibc) der Standard-C-Bibliothek, die für jedes in C geschriebene Programm, einschließlich Python, erforderlich ist. Alpine verwendet jedoch `musl`, und da diese Binärdateien für `glibc` gedacht sind, sind sie einfach keine Option..

Daher müssen Sie, wenn Sie Alpine verwenden, jeden in C geschriebenen Code in jedem Python-Paket kompilieren.

Ach ja, die Liste aller Abhängigkeiten, die kompiliert werden müssen, müssen Sie selbst suchen.

In diesem Fall erhalten wir Folgendes:
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

Und die Build-Zeit beträgt…

… 25 Minuten 57 Sekunden! Und die Größe des Images beträgt 851MB.

… 25 Minuten 57 Sekunden! Und die Bildgröße beträgt 851MB.

Alpine-basierte Images benötigen viel länger zum Erstellen, sie sind auch größer und Sie müssen außerdem alle Abhängigkeiten suchen. Man kann natürlich die Größe des Builds reduzieren, indem man Multi-Stage-Bauten , aber das bedeutet, dass man noch mehr Arbeit leisten muss.

Das ist noch nicht alles!

Alpine kann die Ursache für unerwartete Bugs zur Laufzeit sein.

  • In der Theorie ist musl mit glibc kompatibel, aber in der Praxis können die Unterschiede viele Probleme verursachen. Und wenn sie auftreten, werden sie sicherlich unangenehm sein. Hier sind einige Probleme, die auftreten können:
  • Alpine hat standardmäßig eine kleinere Thread-Stack-Größe, was zu Fehlern in Python
  • Einige Benutzer haben festgestellt, dass Python-Anwendungen langsamer laufen wegen der Art, wie musl Speicher zuweist (was sich von glibc unterscheidet).
  • Einer der Benutzer einen Fehler beim Datumsformat gefunden

Diese Fehler wurden sicherlich bereits behoben, aber wer weiß, wie viele es noch gibt.

Verwenden Sie keine Alpine-Images für Python.

Wenn Sie sich nicht mit großen und langen Builds, der Suche nach Abhängigkeiten und potenziellen Fehlern herumschlagen möchten, verwenden Sie Alpine Linux nicht als Basis-Image. Die Wahl eines guten Basis-Images.

Quelle: habr.com

60GB SSD 8Gb DDR4