Skąd takie rozmiary?

Obraz Dockera to tak naprawdę blueprint kontenera, który symuluje oddzielny system operacyjny w celach wirtualizacji. Musi zatem przede wszystkim zawierać system operacyjny (najczęśniej Linux), biblioteki konieczne do uruchomienia serwisu, oraz sam serwis (build lub kod źródłowy). Jak się jednak okazuje - nie zawsze potrzebujemy tego wszystkiego w kontenerze na końcowym etapie, jakim jest deploy.

 

Czy szukasz wykonawcy projektów IT ?
logo

Co waży najwięcej?

To tak naprawdę zależy od środowiska i runtime'u naszej aplikacji. W przypadku aplikacji używających Node.js problemem na pewno będzie rozmiar folderu node_modules zawierającego zależności naszego projektu. Jeśli natomiast mówimy o Pythonie, to same obrazy typu Linux Debian potrafią ważyć nawet 300-400MB. 

 

Po pierwsze - porządek

Na początek można pozbyć się z obrazu niepotrzebnych plików, czyli takich, których nie używa ani proces kompilacji, ani runtime, ani developer w terminalu kontenera. Na pewno chcemy się pozbyć dokumentacji napisanej w markdown (chyba że np. nasze API ją zwraca), plików konfiguracyjnych środowiska lokalnego (np. wirtualne środowisko w Pythonie) czy plików związanych z DevOps, jak sam Dockerfile lub konfiguracje serwisów w usługach typu AWS czy Heroku. Możemy odpowiednie pliki umieścić w pliku .dockerignore, tak, aby daemon Dockera nie kopiował ich do obrazu w czasie budowy, lub w samym Dockerfile kopiować tylko wybrane pliki.

 

Po drugie - obraz bazowy

No dobrze, pozbyliśmy się kliku megabajtów z naszego obrazu, ale co jeśli sam obraz bazowy waży na przykład 0.5GB? Z pomocą przychodzi Linux Alpine, czyli obraz bazowy Linuxa, ważący jedynie 5MB. Jest to świetna baza dla obrazów, które powinny zawierać tylko to, co muszą. Z https://hub.docker.com jesteśmy w stanie pobrać obrazy Pythona, Node'a, czy Golang'a, które ważą o wiele mniej dzięki temu, że bazują na Linux Alpine. Dla porównania:

  • Python 3.10-bullseye waży około 330MB, gdy 3.10-alpine zajmuje tylko 18MB
  • Node 20.5.0-bookworm-slim waży 76MB, natomiast 20.5.0-alpine już tylko 52MB

Dobrze sprawdzą się również obrazy z dopiskiem slim. Alpine pozostaje natomiast najlżejszą opcją w większości przypadków.

 

Po trzecie - zależności

Jak już wiemy, zależności stanowią znaczącą część rozmiaru naszego obrazu. Dlatego nie ma sensu instalowanie zależności, które nie są nam potrzebne w obrazie produkcyjnym. Tutaj powinniśmy się skupić na komendzie do instalacji zależności, zawartej w naszym Dockerfile. Tutaj fix będzie się różnić w zależności od języka i package managera, którego używamy. W przypadku Node'a i Yarna, mamy rozróżnienie na zależności zwykłe i developerskie (np. biblioteki do testów). Jeśli dodajemy bibliotekę potrzebną developerom, powinniśmy używać 

yarn add <dependency> --dev

Wtedy instalowana biblioteka ląduje pod kluczem devDependencies w package.json i jest ignorowana, jeśli podczas zwykłej instalacji Yarnem, przekażemy opcję --production, w ten sposób:

yarn install --production

W przypadku projektów z użyciem Pythona i domyślnego managera (pip), dobrym pomysłem może być wydzielenie części zależności z requirements.txt do requirements_dev.txt, tak aby przy budowie obrazu instalować tylko niezbędne paczki.

 

Po czwarte - multi-stage build

Daemon Dockera oferuje nam ciekawą funkcję - mianowicie multi-stage build. Jest to metoda budowania obrazu z wieloma obrazami pośrednimi. Takim sposobem jesteśmy w stanie stworzyć warstwę budowy, zawierającą zależności potrzebne do budowy i instalacji, oraz osobną warstwę deploya, która zawiera tylko kod i zależności potrzebne do uruchomienia aplikacji w kontenerze. Oto jak to się robi w Dockerfile:

FROM <obraz>:<tag> as build-stage
... tutaj dokonujemy instalacji tymczasowych zależności, kompilujemy kod itd.

FROM <obraz>:<tag>
... tutaj kopiujemy tylko potrzebne pliki z build-stage
CMD [...]

Pliki między etapami kopiujemy za pomocą: 

COPY --from=<nazwa etapu, tutaj build-stage> <src> <dest>

To pozwala nam zaoszczędzić masę miejsca w ostatecznym obrazie.

 

Wnioski

Zastosowanie powyższych sposobów powinno zmniejszyć wagę obrazu Dockera nawet o kilkadziesiąt procent. Jest jeszcze wiele innych metod, jednak bardzo często zależą one od użytych technologii, czy dokładnych wymagań danego projektu. Jak jednak widać, odpowiednia konfiguracja pozwala zaoszczędzić masę miejsca na dysku, czy w chmurze.

Nasza oferta

Powiązane artykuły

Zobacz wszystkie artykuły powiązane z #devops