
Optymalizacja obrazów Dockera
Obrazy dla kontenerów Dockera mogą być naprawdę ciężkie. W internecie można znaleźć przykłady image'ów ważących nawet po 5 lub więcej gigabajtów. Jest to problem, zarówno dla developerów, używających Dockera do lokalnego developmentu, jak i dla osób odpowiedzialnych za setup aplikacji w środowiskach testowych i produkcyjnych. W artykule zostanie poruszony temat zmniejszania rozmiaru obrazów Dockera.
CEO
24 lip 2023
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.
Powiązane case studies


Aplikacja treningowa na iOS i Android dopasowująca się do postępów użytkownika
Klient: Fit Paradise
Branża: Fitness / FitTech

Platforma edukacyjna generująca materiały do nauki programowania z ChatGPT
Klient: Klient (Aplikacja webowa do nauki programowania)
Branża: Edukacja / EdTech
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-bullseyeważy około 330MB, gdy3.10-alpinezajmuje tylko 18MB - Node
20.5.0-bookworm-slimważy 76MB, natomiast20.5.0-alpinejuż 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.
FAQ
Najczęstsze pytania
- Obraz Dockera to blueprint kontenera symulującego oddzielny system operacyjny — musi zawierać system operacyjny (najczęściej Linux), biblioteki konieczne do uruchomienia serwisu oraz sam serwis. W przypadku aplikacji Node.js problemem jest rozmiar folderu node_modules z zależnościami, a w Pythonie same obrazy typu Linux Debian potrafią ważyć 300-400MB. Nie zawsze potrzebujemy tego wszystkiego na etapie deployu.
- Na początek warto pozbyć się niepotrzebnych plików — dokumentacji w markdown, plików konfiguracyjnych środowiska lokalnego czy plików DevOps jak sam Dockerfile. Odpowiednie pliki można umieścić w .dockerignore, aby daemon Dockera nie kopiował ich do obrazu podczas budowy, lub w samym Dockerfile kopiować tylko wybrane pliki.
- Z pomocą przychodzi Linux Alpine — obraz bazowy ważący jedynie 5MB, świetna baza dla obrazów, które powinny zawierać tylko to, co muszą. Dla porównania: Python 3.10-bullseye waży około 330MB, gdy 3.10-alpine tylko 18MB, a Node 20.5.0-bookworm-slim waży 76MB wobec 52MB wersji alpine. Dobrze sprawdzają się też obrazy z dopiskiem slim, choć Alpine pozostaje najlżejszą opcją.
- Nie ma sensu instalowanie zależności niepotrzebnych w obrazie produkcyjnym. W Node z Yarnem biblioteki developerskie dodaje się z flagą --dev, dzięki czemu lądują w devDependencies i są ignorowane przy instalacji z opcją --production. W projektach Pythona z pip dobrym pomysłem jest wydzielenie części zależności z requirements.txt do requirements_dev.txt, aby przy budowie obrazu instalować tylko niezbędne paczki.
- Multi-stage build to metoda budowania obrazu z wieloma obrazami pośrednimi — można stworzyć warstwę budowy zawierającą zależności potrzebne do kompilacji oraz osobną warstwę deploya z samym kodem i zależnościami do uruchomienia aplikacji. Pliki między etapami kopiuje się poleceniem COPY --from, co pozwala zaoszczędzić masę miejsca w ostatecznym obrazie — zastosowanie tych metod może zmniejszyć wagę obrazu nawet o kilkadziesiąt procent.
Blog
Powiązane artykuły
Jak Cleavr upraszcza wdrażanie aplikacji
Cleavr to platforma, która znacząco upraszcza wdrażanie aplikacji i zarządzanie środowiskami serwerowymi, eliminując wiele typowych problemów związanych z DevOps. Dzięki automatyzacji procesów, gotowym integracjom i intuicyjnemu interfejsowi, nawet złożone wdrożenia stają się szybkie i bezstresowe. Narzędzie wspiera popularne technologie i frameworki, zapewniając elastyczność zarówno dla freelancerów, jak i zespołów developerskich.
Appium: narzędzie do automatycznego testowania aplikacji mobilnych
Appium to narzędzie, które zdobywa coraz więcej uznania w branży IT. Umożliwia efektywne automatyczne testowanie aplikacji mobilnych zarówno na systemach iOS, jak i Android. Jego celem jest wsparcie deweloperów w eliminowaniu błędów i optymalizacji funkcjonowania aplikacji. Poznaj siłę Appium!
Visual studio code - jak w pełni wykorzystać potencjał tego edytora kodu?
Visual Studio Code to jeden z najpopularniejszych edytorów kodu na rynku, który oferuje szeroki zestaw funkcji, które ułatwiają pracę programistom. Edytor ten jest dostępny na różne platformy, jest lekki, szybki i oferuje wiele rozszerzeń, które pozwalają na dostosowanie go do indywidualnych potrzeb.
CDN-first Architecture: Nowy standard dla aplikacji webowych
Wraz z rosnącymi wymaganiami użytkowników i globalnym charakterem aplikacji webowych tradycyjne architektury przestają nadążać za tempem zmian. Coraz wyraźniej widać, że kluczowym czynnikiem przewagi staje się niskie opóźnienie i możliwość błyskawicznego skalowania. W odpowiedzi na te potrzeby powstało podejście CDN-first Architecture, w którym krawędź sieci staje się głównym miejscem wykonywania logiki aplikacyjnej i przechowywania danych.
Edge Caching – rozwiązanie dla stron o dużym ruchu
Edge Caching to jedna z kluczowych technologii, które pozwalają dużym i dynamicznie rozwijającym się stronom internetowym zachować wysoką wydajność mimo rosnącego ruchu. Dzięki przeniesieniu procesów obsługi treści bliżej użytkownika możliwe jest znaczące skrócenie czasu ładowania oraz odciążenie serwera głównego. W czasach, gdy każda sekunda decyduje o konwersjach, pozycjach w Google i doświadczeniu użytkownika, optymalizacja infrastruktury staje się niezbędna.
Fastly: Przegląd nowoczesnej platformy CDN
Fastly to dynamiczna platforma CDN (Content Delivery Network), oferująca szybką i bezproblemową obsługę zawartości. Jej innowacyjne podejście do przechowywania danych to czynnik, który zachęca do bliższego zapoznania się z jej funkcjonalnościami. W artykule przeprowadzimy przegląd najważniejszych cech i możliwości Fastly oraz zaprezentujemy argumenty, dlaczego warto ją poznać.




