Sytuacja kobiet w IT w 2024 roku
31.03.20204 min
Thomas Kluiters

Thomas KluitersSoftware EngineerIMC financial markets

Jak stworzyć najmniejsze obrazy Dockera

Sprawdź, jak można stworzyć jak najmniejsze obrazy Dockera oraz zobacz, co jest w tutaj możliwe na przykładzie Pythona.

Jak stworzyć najmniejsze obrazy Dockera

Nie dawał mi ostatnio spokoju pewien problem: jak bardzo można pomniejszyć obraz Dockera i być jednocześnie w stanie uruchomić wszystkie nasze aplikacje? Zanim się jednak skupimy na szczegółach i wprowadzimy do artykułu techniczną nomenklaturę, to chciałbym jeszcze wyjaśnić, dlaczego ten problem jest taki interesujący, nie tylko dla mnie, ale również dla Was.

Rozmiar ma znaczenie

Zmniejszając liczbę elementów, które umieszczamy w obrazie Dockera, zmniejszamy też liczbę możliwych luk bezpieczeństwa pozostających w jego kontenerze. Umożliwia to dodatkowo, aby obrazy były czystsze i zawierały tylko to, co potrzebne do uruchomienia aplikacji.

Istnieje również mała zaleta dotycząca szybszego pobierania obrazów, ale nie jest to moim zdaniem istotne.

Uwaga: obrazy Alpine są domyślnie bardzo małe i pewnie Ci wystarczą, jeżeli zależy Ci na rozmiarze. 

Obrazy “Distroless”

Projekt Distroless oferuje szereg podstawowych obrazów, które nie zawierają menedżerów pakietów, powłok systemowych ani innych narzędzi, których normalnie można się spodziewać w wierszu poleceń. W związku z tym nie możemy używać takich menedżerów pakietów, jak pip i apt:

FROM gcr.io/distroless/python3
RUN  pip3 install numpy

Plik Dockera używający obrazu Python 3 Distroless

Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM gcr.io/distroless/python3
 ---> 556d570d5c53
Step 2/2 : RUN  pip3 install numpy
 ---> Running in dbfe5623f125
/bin/sh: 1: pip3: not found

Brak menedżera pip

Normalnie ten problem dałoby się rozwiązać za pomocą wieloetapowego procesu budowania:

FROM python:3 as builder
RUN  pip3 install numpy

FROM gcr.io/distroless/python3
COPY --from=builder /usr/local/lib/python3.7/site-packages /usr/local/lib/python3.5/

WIeloetapowy proces budowania w użyciu

Rozmiar uzyskanego obrazu wynosi 130 MB. Nieźle! Dla porównania, domyślny obraz Pythona ma rozmiar 929 MB, a wyszczuplony wariant, czyli 3.7-slim, ma 179 MB. Obraz 3.7-alpine ma 98,6 MB, a distroless image użyty w powyższym przykładzie - 50,9 MB.

Można teraz zauważyć, że w poprzednim przykładzie skopiowaliśmy cały katalog /usr/local/lib/python3.7/site-packages, który może zawierać niepotrzebne zależności. Jest jednak jasne, że rozbieżność w wielkości wszystkich podstawowych obrazów Pythona jest różna.

W momencie pisania tego tekstu Google distroless nie obsługuje wielu obrazów: Java i Python są eksperymentalne, no i Python istnieje tylko dla wersji 2.7 i 3.5.

Obrazy Minuscule

Powróćmy do mojej obsesji na punkcie małych obrazów.

Początkowo chciałem zobaczyć, jak powstały obrazy distroless. Projekt wykorzystuje narzędzie do budowania o nazwie bazel od Google'a, ale skonfigurowanie bazela i napisanie własnych zdjęć wymagało trochę pracy (i bądźmy szczerzy, odkrywanie Ameryki może być bardzo pouczające i dobrze się będziemy przy tym bawić). Chciałem móc łatwiej tworzyć mniejsze obrazy. Tworzenie obrazu powinno być niezwykle proste i wręcz trywialne. Bez plików konfiguracyjnych, z tylko jedną linijką w terminalu: just build an image for <application>.

Jeśli chcesz budować własne obrazy, powinieneś znać unikalny obraz Dokera o nazwie scratch. Jest to „pusty” obraz - nie zawiera żadnych plików (domyślnie jest to aż 77 bajtów).

FROM scratch

Scratch

Pomysł polega na tym, że kopiujemy dowolne zależności z naszego hosta i albo używamy tych zależności w Dockerfile (np. kopiujemy w apt i instalujemy zależności od początku), albo zostawiamy to na później i czekamy, aż zmaterializuje się plik Dockera.

To daje nam pełną kontrolę nad tym, co zawieramy w naszym kontenerze, a więc również nad rozmiarem obrazu. Teraz potrzebujemy sposobu na zebranie tych zależności. Istniejące narzędzia, takie jak apt, które umożliwiają pobieranie pakietów, ale są one ograniczone tylko do bieżącej maszyny i, w końcu, nie będą obsługiwały systemu Windows ani MacOS.

Postanowiłem więc zbudować własne narzędzie, które byłoby w stanie automatycznie stworzyć najmniejszy możliwy obraz do uruchomienia dowolnej aplikacji. Użyję pakietów Ubuntu/Debian, pobiorę je (bezpośrednio uzyskując dostęp do serwerów pakietów) i rekurencyjnie znajdę ich zależności. Narzędzie zawsze powinno pobierać najnowszą stabilną wersję pakietu, poprawiając bezpieczeństwo.

Nazwałem moje narzędzie fetchy. Działa przez wiersz poleceń, ale oferuje również API.

Aby stworzyć obraz z fetchy (weźmy tutaj Pythona), wystarczy użyć CLI w następujący sposób: fetchy dockerize python. Możesz zostać poproszony o podanie docelowego systemu operacyjnego, ponieważ nasze narzędzie może na razie używać tylko pakietów Debiana/Ubuntu. Niektóre zależności w ogóle mogą nie być w naszym kontekście używane, więc możemy je wykluczyć. Na przykład: Python zależy od Perla, ale będze bez niego działał.

Rezultat

Obraz Pythona wygenerowany przez polecenie fetchy dockerize python3.5 ma tylko 35 MB (wydaje mi się, że można go jeszcze bardziej zmniejszyć, ale to zagadnienie na przyszłość), Wygląda na to, że udało nam się zmniejszyć obraz o 15 MB.

Tutaj możesz wyświetlić wszystkie obrazy: https://hub.docker.com/r/fetchy/
Projekt można znaleźć tutaj: https://github.com/ThomasKluiters/fetchy

Pracuję obecnie nad integracją innych menedżerów pakietów z Fetchy - w ten sposób nie trzeba będzie używać już wieloetapowego procesu budowania.

Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>