angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Verkleinern der Größe eines Python Anwendungsbildes Docker mit Hilfe von Python wheels

Mit Docker mehrstufigen Builds und Python wheels wir erarbeiten die Abhängigkeiten in der ersten Stufe und installieren sie in der zweiten Stufe.

10 März 2019
post main image

Bei der Verwendung von Docker möchten wir, dass die Größe des Dockerbildes minimal ist. Warum? Viele Gründe. Der Speicherbedarf ist besonders groß, wenn viele Python Flask Websites auf einem ISPConfig3-Server laufen, zum Glück können wir ab Docker 17.05 Builds multistage verwenden. Damit reduzieren wir unsere Bildgröße von 376MB auf 211MB!

Nachfolgend finden Sie zwei Bilder, die wir verwenden können:

> Docker-Bilder

python 3.6-alpine 1837080c5e87 5 weeks ago 74.4MB
python 3.6.7 1ec4d11819ad 2 months ago 918MB

74 MB vs. 918 MB ist ein großer Unterschied. Natürlich ist die geringe Größe mit Kosten verbunden. Viele Programme/Skripte wurden aus dem alpinen Bild entfernt, so dass wir manchmal auf Probleme stoßen können.

Alpine Beim Debuggen können wir auf sehr einfache Weise Programme wie telnet und netstat hinzufügen:

> apk add busybox-extras hinzufügen

Für diese Blog-App Flask ist die requirements.txt:

alembic==1.0.0
asn1crypto==0.24.0
Babel==2.6.0
beautifulsoup4==4.6.3
cffi==1.11.5
Click==7.0
cryptography==2.3.1
Flask==1.0.2
Flask-Babel==0.11.2
Flask-Login==0.4.1
Flask-Session==0.3.1
Flask-WTF==0.14.2
gunicorn==19.9.0
html2text==2018.1.9
idna==2.7
itsdangerous==0.24
Jinja2==2.10
Mako==1.0.7
MarkupSafe==1.0
Pillow==5.3.0
pycparser==2.19
PyMySQL==0.9.2
python-dateutil==2.7.3
python-editor==1.0.3
python-magic==0.4.15
python-slugify==1.2.6
pytz==2018.5
six==1.11.0
SQLAlchemy==1.2.12
Unidecode==1.0.22
Werkzeug==0.14.1
WTForms==2.2.1

Bei der Verwendung von python:3.6-alpin stoßen wir auf Fehler beim Aufbau unseres Bildes. In diesem Fall, für Kaffee und Kissen:

  • Fehler'Kein funktionierender Compiler gefunden'.
  • Die Header oder Bibliotheksdateien konnten für jpeg nicht gefunden werden, eine notwendige Abhängigkeit bei der Kompilierung von Pillow aus der Quelle.

Die Lösungen sind hier und beinhalten das Hinzufügen weiterer Programme/Codes zum Bild:

  • CFFI-Abhängigkeiten blähen das Docker Bild #458
    https://github.com/gliderlabs/docker-alpine/issues/458 Zur DockerDatei
    hinzufügen: RUN apk add --no-cache curl python3 pkgconfig python3-dev openssl-dev libffi-dev musl-dev make gcc
  • Wie installiert man Kissen-, psycopg-, pylibmc-Pakete in python:alpine image
    h
    ttps://blog.sneawo.com/blog/2017/09/07/how-to-install-pillow-psycopg-pylibmc-packages-in-pythonalpine-image/ Zur DockerDatei hinzufügen: RUN apk add --no-cache jpeg-dev zlib-dev zlib-dev

Leider ist unser Image inzwischen auf 376MB angewachsen!

Neuschreibdatei Dockerfür mehrstufige Anwendungen

Ab Docker 17.05 können wir mehrstufige Builds verwenden. Die Idee ist, dass wir einige Dienstprogramme erstellen und das Ergebnis dann in das endgültige Containerbild kopieren.

Neu schreiben der DockerDatei aus:

FROM python:3.6-alpine
MAINTAINER Peter Mooring peterpm@xs4all.nl peter@petermooring.com

# create and set working directory
RUN mkdir -p /home/flask/app/web
WORKDIR /home/flask/app/web

# install package dependencies
COPY requirements.txt ./
# Solve 'No working compiler found' error, 
# see: https://github.com/gliderlabs/docker-alpine/issues/458
RUN apk add --no-cache curl python3 pkgconfig python3-dev openssl-dev libffi-dev musl-dev make gcc \
# Solve 'The headers or library files could not be found for jpeg, a required dependency when compiling Pillow from source.', 
# see https://blog.sneawo.com/blog/2017/09/07/how-to-install-pillow-psycopg-pylibmc-packages-in-pythonalpine-image/
    jpeg-dev zlib-dev \
    libmagic \
  && pip install --no-cache-dir -r ./requirements.txt \
  && rm -rf /var/cache/apk/*

# copy app code into container
COPY . ./

# create group and user used in this container
RUN addgroup flaskgroup && adduser -D flaskuser -G flaskgroup
RUN chown -R flaskuser:flaskgroup /home/flask

USER flaskuser

zu:

FROM python:3.6-alpine as base
MAINTAINER Peter Mooring peterpm@xs4all.nl peter@petermooring.com

RUN mkdir /svc
WORKDIR /svc
COPY requirements.txt .

# install package dependencies
# COPY requirements.txt /requirements.txt, requirements.txt already copied 
# Solve 'No working compiler found' error, 
# see: https://github.com/gliderlabs/docker-alpine/issues/458
# Solve 'The headers or library files could not be found for jpeg, a required dependency when compiling Pillow from source.', 
# see https://blog.sneawo.com/blog/2017/09/07/how-to-install-pillow-psycopg-pylibmc-packages-in-pythonalpine-image/


RUN rm -rf /var/cache/apk/* && \
    rm -rf /tmp/*

RUN apk update

# Instead, I run python setup.py bdist_wheel first, then run pip wheel -r requirements.txt for pypi packages.

RUN apk add --update \
    curl \
    python3 \ 
    pkgconfig \ 
    python3-dev \
    openssl-dev \ 
    libffi-dev \ 
    musl-dev \
    make \ 
    gcc \
    jpeg-dev zlib-dev \
    libmagic \
    && rm -rf /var/cache/apk/* \
    && pip wheel -r requirements.txt --wheel-dir=/svc/wheels

# the wheels are now here: /svc/wheels

FROM python:3.6-alpine

RUN apk add --no-cache \
    jpeg-dev zlib-dev \
    libmagic

COPY --from=base /svc /svc

WORKDIR /svc
RUN pip install --no-index --find-links=/svc/wheels -r requirements.txt

# after installation, remove wheels, does not free up space, probably because we are in new layer, too bad is some 20MB
#RUN rm -R *

# create and set working directory
RUN mkdir -p /home/flask/app/web
WORKDIR /home/flask/app/web

# copy app code into container
COPY . ./

# create group and user used in this container
RUN addgroup flaskgroup && adduser -D flaskuser -G flaskgroup && chown -R flaskuser:flaskgroup /home/flask

USER flaskuser

Das Python wheels Verzeichnis /svc/wheels:

-rw-r--r-- 1 root root 8098645 Feb 15 12:56 Babel-2.6.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 81299 Feb 15 12:56 Click-7.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 91364 Feb 15 12:56 Flask-1.0.2-py2.py3-none-any.whl
-rw-r--r-- 1 root root 9267 Feb 15 12:56 Flask_Babel-0.11.2-py2.py3-none-any.whl
-rw-r--r-- 1 root root 4936158 Feb 15 12:56 Flask_CKEditor-0.4.2-py2.py3-none-any.whl
-rw-r--r-- 1 root root 15935 Feb 15 12:57 Flask_Login-0.4.1-py2.py3-none-any.whl
-rw-r--r-- 1 root root 7535 Feb 15 12:56 Flask_Session-0.3.1-py2.py3-none-any.whl
-rw-r--r-- 1 root root 14903 Feb 15 12:56 Flask_WTF-0.14.2-py2.py3-none-any.whl
-rw-r--r-- 1 root root 126381 Feb 15 12:56 Jinja2-2.10-py2.py3-none-any.whl
-rw-r--r-- 1 root root 76583 Feb 15 12:57 Mako-1.0.7-py3-none-any.whl
-rw-r--r-- 1 root root 29273 Feb 15 12:57 MarkupSafe-1.0-cp36-cp36m-linux_x86_64.whl
-rw-r--r-- 1 root root 1101554 Feb 15 12:57 Pillow-5.3.0-cp36-cp36m-linux_x86_64.whl
-rw-r--r-- 1 root root 47758 Feb 15 12:56 PyMySQL-0.9.2-py2.py3-none-any.whl
-rw-r--r-- 1 root root 1144841 Feb 15 12:57 SQLAlchemy-1.2.12-cp36-cp36m-linux_x86_64.whl
-rw-r--r-- 1 root root 235421 Feb 15 12:56 Unidecode-1.0.22-py2.py3-none-any.whl
-rw-r--r-- 1 root root 166353 Feb 15 12:56 WTForms-2.2.1-py2.py3-none-any.whl
-rw-r--r-- 1 root root 322863 Feb 15 12:56 Werkzeug-0.14.1-py2.py3-none-any.whl
-rw-r--r-- 1 root root 158276 Feb 15 12:56 alembic-1.0.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 101571 Feb 15 12:56 asn1crypto-0.24.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 90375 Feb 15 12:56 beautifulsoup4-4.6.3-py3-none-any.whl
-rw-r--r-- 1 root root 385610 Feb 15 12:56 cffi-1.11.5-cp36-cp36m-linux_x86_64.whl
-rw-r--r-- 1 root root 813672 Feb 15 12:57 cryptography-2.3.1-cp36-cp36m-linux_x86_64.whl
-rw-r--r-- 1 root root 112930 Feb 15 12:56 gunicorn-19.9.0-py2.py3-none-any.whl
-rw-r--r-- 1 root root 21118 Feb 15 12:56 html2text-2018.1.9-py3-none-any.whl
-rw-r--r-- 1 root root 58213 Feb 15 12:56 idna-2.7-py2.py3-none-any.whl
-rw-r--r-- 1 root root 10622 Feb 15 12:57 itsdangerous-0.24-py3-none-any.whl
-rw-r--r-- 1 root root 111031 Feb 15 12:57 pycparser-2.19-py2.py3-none-any.whl
-rw-r--r-- 1 root root 211414 Feb 15 12:56 python_dateutil-2.7.3-py2.py3-none-any.whl
-rw-r--r-- 1 root root 6686 Feb 15 12:57 python_editor-1.0.3-py3-none-any.whl
-rw-r--r-- 1 root root 5543 Feb 15 12:56 python_magic-0.4.15-py2.py3-none-any.whl
-rw-r--r-- 1 root root 4595 Feb 15 12:57 python_slugify-1.2.6-py2.py3-none-any.whl
-rw-r--r-- 1 root root 510974 Feb 15 12:56 pytz-2018.5-py2.py3-none-any.whl
-rw-r--r-- 1 root root 10702 Feb 15 12:56 six-1.11.0-py2.py3-none-any.whl

Zusammenfassung

Vorher: 376M, danach: 211MB.

Wir haben dies in mehreren Stufen getan:

Stufe 1:

  • Erstellen Python wheels für requirements.txt

Stufe 2:

  • Kopieren Python wheels von Stufe 1
  • Installieren Sie Abhängigkeiten mit Hilfe von Python wheels
  • Andere Dinge tun, wie z.B. Benutzer anlegen, Code kopieren, etc.

Leider können wir nicht mit dem Verzeichnis /svc/wheels aus stage1 arbeiten oder dieses Verzeichnis aus dem Ergebnis entfernen. Das würde weitere 25 Millionen Euro einsparen!

Links / Impressum

Building Minimal Docker Containers for Python Applications
https://blog.realkinetic.com/building-minimal-docker-containers-for-python-applications-37d0272c52f3

How do I reduce a python (docker) image size using a multi-stage build? (**python specific**)
https://stackoverflow.com/questions/48543834/how-do-i-reduce-a-python-docker-image-size-using-a-multi-stage-build-pytho

Leveraging Docker multi-stage builds in Python development
https://www.merixstudio.com/blog/docker-multi-stage-builds-python-development/

Lighter Python images using multi-stage Dockerfile
https://lekum.org/post/multistage-dockerfile/

Smaller Python Docker Containers with Multi-Stage Builds and Python Wheels
https://softwarejourneyman.com/docker-python-install-wheels.html

Use multi-stage builds
https://docs.docker.com/develop/develop-images/multistage-build/

Mehr erfahren

Docker Wheels

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare (1)

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.

avatar

You can use
RUN pip install --no-cache /wheels/* \
&& rm -rf /wheels/*
to delete wheels