Alpine buduje obrazy Docker dla Pythona 50 razy wolniej, a obrazy są 2 razy cięższe

Alpine buduje obrazy Docker dla Pythona 50 razy wolniej, a obrazy są 2 razy cięższe

Alpine Linux — często zalecany jako podstawowy obraz dla Dockera. Mówi się, że użycie Alpine sprawi, że twoje obrazy będą mniejsze, a proces budowy szybszy.

Jednak jeśli używasz Alpine Linux do aplikacji Python, to on:

  • Znacznie spowalnia budowę twoich obrazów
  • Powoduje, że twoje obrazy stają się większe
  • Marnuje twój czas
  • I w efekcie może prowadzić do błędów w czasie działania


Zobaczmy, dlaczego zaleca się Alpine, ale dlaczego nie warto go używać w połączeniu z Pythonem.

Dlaczego ludzie polecają Alpine?

Załóżmy, że potrzebujemy gcc jako część naszego obrazu i chcemy porównać Alpine Linux z Ubuntu 18.04, pod względem prędkości budowy i ostatecznego rozmiaru obrazu.

Na początek, pobierzmy dwa obrazy i porównajmy ich rozmiar:

$ 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

Jak widać, podstawowy obraz dla Alpine jest znacznie mniejszy. Teraz spróbujmy zainstalować gcc, zaczynając od 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/*

Pisanie idealnych Dockerfile wykracza poza zakres tego artykułu

Mierzmy prędkość budowy:

$ 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

Powtarzamy to samo dla Alpine (Dockerfile):

FROM alpine
RUN apk add --update gcc

Budujemy, sprawdzamy czas i rozmiar budowy:

$ 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

Jak obiecano, obrazy oparte na Alpine budują się szybciej i same w sobie są mniejsze: 15 sekund zamiast 30 i rozmiar obrazu 105MB w porównaniu do 150MB. To całkiem dobrze!

Jednak jeśli przełączymy się na budowę aplikacji Python, sytuacja nie wygląda tak różowo.

Obraz Python

Aplikacje Python często wykorzystują pandas i matplotlib. Dlatego jednym z wariantów jest użycie oficjalnego obrazu opartego na Debianie, używając takiego Dockerfile:

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

Budujemy go:

$ docker build -f Dockerfile.slim -t python-matpan.
Wysyłanie kontekstu budowy do demona Docker  3.072kB
Krok 1/2 : FROM python:3.8-slim
 ---> 036ea1506a85
Krok 2/2 : RUN pip install --no-cache-dir matplotlib pandas
 ---> Uruchamianie w 13739b2a0917
Zbieranie matplotlib
  Pobieranie matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl (13.1 MB)
Zbieranie pandas
  Pobieranie pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl (10.4 MB)
...
Pomyślnie zbudowano b98b5dc06690
Pomyślnie otagowano python-matpan:latest

czas rzeczywisty    0m30.297s
czas użytkownika    0m0.043s
czas systemowy     0m0.020s

Otrzymujemy obraz o rozmiarze 363MB.
Czy uzyskamy lepsze rezultaty z Alpine? Spróbujmy:

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

$ docker build -t python-matpan-alpine -f Dockerfile.alpine .                                 
Wysyłanie kontekstu budowy do demona Docker  3.072kB                                               
Krok 1/2 : FROM python:3.8-alpine                                                             
 ---> a0ee0c90a0db                                                                            
Krok 2/2 : RUN pip install --no-cache-dir matplotlib pandas                                                  
 ---> Uruchamianie w 6740adad3729                                                                 
Zbieranie matplotlib                                                                         
  Pobieranie matplotlib-3.1.2.tar.gz (40.9 MB)                                               
    BŁĄD: Komenda zakończyła się błędem z kodem wyjścia 1:                                            
     komenda: /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                              

...
BŁĄD: Komenda zakończyła się błędem z kodem wyjścia 1: python setup.py egg_info Sprawdź logi, aby zobaczyć pełne wyjście komendy.
Komenda '/bin/sh -c pip install matplotlib pandas' zwróciła kod inny niż zero: 1

Co się dzieje?

Alpine nie wspiera wheels

Jeśli spojrzysz na budowę, która opiera się na Debianie, zobaczysz, że pobiera matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl.

To jest binarka dla wheel. Alpine pobiera źródła `matplotlib-3.1.2.tar.gz`, ponieważ nie wspiera standardowego wheels.

Dlaczego? Większość dystrybucji Linuksa używa wersji GNU (glibc) standardowej biblioteki C, która jest w rzeczywistości niezbędna każdemu programowi napisanym w C, w tym Pythonowi. Ale Alpine używa `musl`, a ponieważ te binarki są przeznaczone dla `glibc`, po prostu nie są opcją.

Dlatego, jeśli używasz Alpine, musisz kompilować cały kod napisany w C w każdym pakiecie Pythona.

Ach, tak, lista wszystkich takich zależności, które trzeba skompilować, będzie musiała być wyszukana przez siebie.
W tym przypadku otrzymujemy coś takiego:

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

A czas budowy trwa...

… 25 minut 57 sekund! A rozmiar obrazu wynosi 851MB.

Obrazy oparte na Alpine są znacznie dłużej budowane, same w sobie są większe, a dodatkowo musisz szukać wszystkich zależności. Oczywiście można zmniejszyć rozmiar budowy, korzystając z budów wieloetapowych , ale oznacza to, że trzeba wykonać jeszcze więcej pracy.

To jeszcze nie wszystko!

Alpine może być przyczyną niespodziewanych błędów w czasie działania

  • Teoretycznie musl jest zgodny z glibc, ale w praktyce różnice mogą prowadzić do wielu problemów. A jeśli się pojawią, to na pewno będą nieprzyjemne. Oto niektóre problemy, które mogą wystąpić:
  • Alpine domyślnie ma mniejszy rozmiar stosu wątku, co może prowadzić do błędów w Pythonie
  • Niektórzy użytkownicy odkryli, że aplikacje Python działają wolniej z powodu tego, jak musl przydziela pamięć (różni się od glibc).
  • Jeden z użytkowników napotkał błąd podczas formatowania daty

Na pewno te błędy już zostały naprawione, ale kto wie, ile ich jeszcze jest.

Nie używaj obrazów Alpine do Pythona

Jeśli nie chcesz bawić się w długie i skomplikowane budowy, poszukiwanie zależności oraz potencjalne błędy — nie używaj Alpine Linux jako bazowego obrazu. Wybór dobrego obrazu bazowego.

Źródło: habr.com

Kup niezawodny hosting stron z ochroną DDoS, serwery VPS VDS 🔥 Kup niezawodny hosting stron z ochroną DDoS, serwery VPS VDS - ProHoster