Dzisiaj chcę pokazać Ci sposoby, które pomogą Ci zmniejszyć rozmiar i skrócić czas budowania obrazów Dockerowych.

Poniżej wypisałem 5 najważniejszych kwestii, a wszystko dokładnie tłumaczę w nowym filmie, o tutaj 👇

Kolejność poleceń w Dockerfile

Jak pewnie pamiętasz, obrazy w Dockerze złożone są z warstw. Każda następna warstwa zależy od poprzedniej. Dzięki temu nie musimy za każdym razem budować całego obrazu od nowa, jeżeli zmieni się tylko jedna z warstw.

Tylko że… jest jedna ważna konsekwencja takiego układu. Co prawda, nie musimy przebudowywać wszystkich warstw. Musimy przebudować tę warstwę, która się zmieniła i wszystkie kolejne. Jeżeli zmienia się tylko ostatnia warstwa, to nie ma żadnego problemu. Gorzej, gdy zmienia się jedna z pierwszych.

Ważne jest zatem ułożenie warstw – czyli też poleceń w Dockerfile – w kolejności od najczęściej do najrzadziej się zmieniających. Oczywiście jeżeli jakieś polecenia zależą od poprzednich, to muszą się znaleźć za nimi.

Jak to sensownie zrobić? Możesz posłużyć się przybliżoną regułą, że najrzadziej zmieniają się wymagane paczki/biblioteki w systemie operacyjnym (np. instalowane przez apt-get), następnie biblioteki dla twojego programu (np. paczki pythonowe z pip), a najczęściej będzie się zmieniał sam kod źródłowy twojej aplikacji.

Dlatego warto czasem rozbić budowanie obrazu na dwa oddzielne kroki: najpierw zainstalujemy biblioteki, a dopiero później skopiujemy do niego kod źródłowy.

Łączenie poleceń

Drugą konsekwencją budowy warstwowej obrazu jest to, że rozmiar obrazu może tylko rosnąć przy dodawaniu nowych warstw. Jeżeli w jednej warstwie dodasz plik do obrazu, a w innej go usuniesz, to rozmiar obrazu będzie taki, jakby ten plik nadal znajdował się w obrazie.

Jeżeli tylko możesz tego uniknąć, nie dodawaj do obrazu plików tymczasowych. Połącz polecenie tworzące (o ile nie kopiujesz go z build contextu przez COPY lub ADD), polecenie wykorzystujące i polecenie usuwające taki plik. Możesz to zrobić przy pomocy operatora && w Bashu. Jeżeli jeszcze dodasz na koniec polecenie usunięcia tego pliku tymczasowego, to Docker nie zapisze go do warstwy. Dzięki temu rozmiar obrazu będzie mniejszy.

Na przykład, zamiast:

RUN echo "value=1" > config.tmp
RUN my_script.sh
RUN rm config.tmp

można napisać:

RUN echo "value=1" > config.tmp && \
    my_script.sh && \
    rm config.tmp

Obrazy typu slim

Zwyczajny obraz debian, często używany do tworzenia kontenerów, zajmuje po rozpakowaniu ok. 114MB. Ale wystarczy, że zastąpisz go obrazem z tagiem -slim (np. debian:stable-slim), a zaoszczędzisz ok. 40MB praktycznie nie tracąc na funkcjonalności.

Edycje slim usuwają część najrzadziej potrzebnych programów, które normalnie są dołączone do obrazu Debiana. Tego typu wersje możemy spotkać też dla innych obrazów, takich jak na przykład python:3.8-slim.

Obraz alpine

A żeby pójść jeszcze o krok dalej, możesz użyć dystrybucji Linuxa Alpine, jako bazy dla Twoich obrazów. Jego bazowy obraz ma zaledwie 5.6MB!

Jest tylko kilka rzeczy, na które musisz uważać:

Po pierwsze, trzeba zmienić polecenia instalacji paczek z apt-get na apk, bo tak nazywa się manager pakietów w Alpine. Mogą różnić się też same nazwy pakietów.

Po drugie, Alpine korzysta z alternatywnej implementacji biblioteki C, która czasami może być niekompatybilna z niektórymi programami.

I po trzecie, korzystanie z Alpine może nie być dobrym pomysłem, jeżeli budujesz kontenery z Pythonem. Po prostu, pip (manager paczek dla Pythona) nie dostarcza gotowych binarek dla natywnych rozszerzeń, tak jak dla standardowej implementacji GNU libC. Przez to zawsze są one kompilowane od zera, co może czasem zająć bardzo długo.

Plik .dockerignore

O tym mówiłem już w poprzednich filmach, ale warto powtórzyć. Docker jest zbudowany w architekturze klient-serwer. Gdy poprosisz Dockera o zbudowanie obrazu, cały katalog, który podasz – domyślnie: ten, w którym jest Dockerfile – zostaje przesłany przez klienta (polecenie docker build) do serwera (dockerd) przez HTTP (!).

Jeżeli ten katalog jest bardzo duży, to wysyłanie potrafi trochę potrwać. Zwłaszcza trzeba uważać na foldery z bibliotekami, paczkami (np. node_modules, które czasem zajmuje kilkaset megabajtów!).

Żeby nie przesyłać niepotrzebnych śmieci, możesz stworzyć specjalny plik o nazwie .dockerignore. Pliki i katalogi, które w nim podasz, nie będą wysyłane do Dockera.

Więcej?

Wszystkie zagadnienia, które tu wypisałem, tłumaczę dokładnie na filmie. Link na górze posta 👆