Wykorzystanie miejsca na dysku w pracy z Docker

Grudzień 20, 2023 | #docker

000000
001101
110000
001100

Sprawdzanie zajętości dysku przez pliki Docker-a

W trakcie pracy z Docker-em pobieramy na dysk obrazy z repozytoriów, w oparciu o które budujemy nowe obrazy albo powołujemy do życia ich instancje, czyli kontenery. Zamknięte wewnątrz kontenerów aplikacje muszą gdzieś przechowywać dane zatem definiujemy dla nich wolumeny, a wszystko to zajmuje miejsce na dysku. Docker ma wbudowane narzędzie pozwalające monitorować zajęcie przestrzeni dyskowej w kilku kategoriach:

docker system df

Przykładowy wynik tego polecenia może być następujący

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          20        9         6.858GB   3.967GB (57%)
Containers      14        0         3.762MB   3.762MB (100%)
Local Volumes   22        12        4.059GB   344.3MB (8%)
Build Cache     567       0         19.81GB   19.81GB

Poszczególne kategorie to:

  1. Obrazy (Images): Łączna ilość miejsca zajmowanego przez obrazy Docker-a na dysku, zarówno te używane, jak i nieużywane (tj. obrazy, które nie są bazą dla żadnego uruchomionego kontenera).

  2. Kontenery (Containers): Całkowita ilość miejsca zajmowana przez kontenery, w tym aktywne (uruchomione) i nieaktywne (zatrzymane).

  3. Woluminy (Volumes): Łączne wykorzystanie miejsca przez woluminy Docker, które są używane do trwałego przechowywania danych.

  4. Cache Budowania (Build Cache): Miejsce zajmowane przez cache budowania Docker, które zawiera pośrednie etapy obrazów utworzone podczas budowania obrazów.

Użycie pamięci dyskowej przez obrazy Docker-a

Pojedynczy obraz Dockera potrafi sporo ważyć. Jeszcze kilka lat temu pracując z kontenerami trzeba było na ich potrzeby zabezpieczyć od kilku do nawet kilkudziesięciu gigabajtów przestrzeni dyskowej. Na przestrzeni lat obrazy były dostosowywane do specyficznych zastosowań, chudły okrajane ze zbędnych funkcjonalności i teraz jest z tym dużo lepiej.

Przede wszystkim musimy sobie zdać sprawę z tego, że ...

Kontener to nie maszyna wirtualna (VM) więc obrazy Dockera mogą być mniejsze

Kontener Dockera nie jest maszyną wirtualną.


UWAGA!

Z małym zastrzeżeniem. Dotyczy to czystego Docker Engine w systemie Linux. Docker Desktop używa maszyn wirtualnych (VM) pod spodem. W przypadku systemów Mac i Windows, Docker Desktop instaluje lekką maszynę wirtualną LinuxKit, którą Docker zarządza dla użytkownika. Na Windows, VM ta działa pod WSL2 (Windows Subsystem for Linux 2), umożliwiając wszystkim dystrybucjom WSL2 dostęp do Docker'a. Na Mac, obecnie przechodzą od wcześniejszej implementacji z HyperKit do nowego frameworku wirtualizacji Apple, aby uruchomić tę VM. Także na Linux Docker Desktop działa w maszynie wirtualnej dlatego używając Dockera w tej wersji nie znajdziesz plików, o których wspominam w tym artykule w ich standardowych lokalizacjach, ale wciąż możesz używać podanych tu komend.


Chociaż kontenery i maszyny wirtualne służą do izolacji środowisk aplikacji, działają one w zasadniczo różny sposób. Dlatego też obrazy Dockera np. Ubuntu, zawierają jedynie podstawowe elementy systemu operacyjnego a nie są one pełnymi wersjami, takimi jak te instalowane na fizycznych lub wirtualnych maszynach. W konsekwencji, ważą mniej, ale wciąż sporo.

Główne typy obrazów Docker, biorąc pod uwagę ich wielkość

Obrazy możemy podzielić według różnych kryteriów ale najprostszym jest to czy obraz został ściągnięty z rejestrów i dostajemy go z dobrodziejstwem inwentarza, czy też został przez nas zbudowany bazując na innym obrazie lub od podstaw.

Pobierając oficjalne obrazy z rejestru możemy zakładać, że zostały one przygotowane z pełną pieczołowitością (co nie oznacza, że nie ma w nich żadnych podatności) i starannością. Nie mamy wpływu na ich rozmiar ale możemy wybierać jaką chcemy wersję.

  1. Obrazy Bazowe (Base Images)

    1. Obrazy Pełne (Full Images):

      Zawierają pełny system operacyjny, często z dodatkowymi narzędziami i bibliotekami np. ubuntu, debian, centos.

    2. Obrazy Minimalistyczne (Minimal Images):

      Zawierają minimalny zestaw komponentów niezbędnych do uruchomienia systemu operacyjnego np. alpine, busybox. Są znacznie mniejsze od pełnych obrazów, co czyni je popularnym wyborem w środowiskach produkcyjnych, zwłaszcza dla mikroserwisów i aplikacji rozproszonych.

  2. Obrazy Języków Programowania

    1. Obrazy SDK/Development:

      Zawierają kompilatory, narzędzia budowania, debuggery i inne narzędzia deweloperskie dla określonego języka programowania np. node:x-sdk, openjdk:x-jdk. (pod x podstaw nr wersji)

    2. Obrazy Runtime:

      Zawierają tylko środowisko uruchomieniowe i niezbędne biblioteki dla określonego języka lub frameworka np. node:x-alpine, openjdk:x-jre. Są mniejsze, ponieważ nie zawierają narzędzi deweloperskich, tylko to, co jest potrzebne do uruchomienia aplikacji.

  3. Obrazy Aplikacji

    1. Obrazy "Fat":

      Zawierają aplikację wraz z pełnym systemem operacyjnym i wszystkimi jej zależnościami. Są większe i mogą być mniej bezpieczne ze względu na większą liczbę składowych.

    2. Obrazy "Slim" lub "Lightweight":

      Zawierają aplikację i jej bezpośrednie zależności, ale z mniejszą liczbą składowych systemu operacyjnego. Są chudsze i często bardziej bezpieczne, ponieważ zawierają mniej komponentów, co zmniejsza powierzchnię ataku.

  4. Obrazy Distroless

    Specjalne obrazy stworzone przez Google, które zawierają aplikacje i ich bezpośrednie zależności, ale bez pełnego systemu operacyjnego. Są bardzo małe i zabezpieczone, ponieważ nie zawierają zbędnych narzędzi czy składowych systemu w tym także shell-a co minimalizuje potencjalne wektory ataków.

Wybór odpowiedniego obrazu zależy od wielu czynników, takich jak wymagania aplikacji, potrzeby bezpieczeństwa, zasoby systemowe oraz procesy CI/CD. Mniejsze obrazy są szybsze w pobieraniu i uruchamianiu, co jest korzystne w środowiskach produkcyjnych i ciągłej integracji/dostawy, ale czasami konieczne jest użycie większych obrazów ze względu na konkretne wymagania aplikacji lub środowiska uruchomieniowego dlatego też ilość miejsca na dysku nie może być jedynym kryterium.

Wyświetlając listę obrazów ...

docker images ls

Mamy możliwość podejrzenia tagu obrazu, który zdradza nam też jego rodzaj, jak również rozmiaru.

REPOSITORY         TAG                  IMAGE ID       CREATED         SIZE
xxx/blog/django    latest               8cd00a60f287   6 days ago      299MB
<none>             <none>               bc95922ac24b   6 days ago      47.8MB
xxx/blog/postgres  latest               292262f248ae   6 days ago      211MB
redis              7-alpine             d2d4688fcebe   11 days ago     41MB
traefik            v2.10                64586c703ab1   12 days ago     153MB
nginx              1.25.3-alpine-slim   5473339cf3c6   2 weeks ago     11.9MB
python             3.10.4-alpine3.15    abd9fc366a36   18 months ago   47.8MB
traefik            v2.6                 22c6901de2be   19 months ago   102MB
postgres           14.2-alpine          ebf01b748a56   20 months ago   211MB
wzgapi_api         latest               49d82317a617   3 years ago     444MB
certbot/certbot    latest               bbf174584317   4 years ago     102MB
postgres           9.6-alpine           5152558331dc   4 years ago     38.6MB
nginx              1.15.8-alpine        315798907716   4 years ago     17.8MB
python             3.6-alpine           1837080c5e87   4 years ago     74.4MB

Obrazy <none> Docker-a

Przyglądając się uważnie powyższej liście obrazów możemy dostrzec pozycję, która od razu budzi nasz niepokój

<none>             <none>               bc95922ac24b   6 days ago      47.8MB

Co więcej wywołując komendę

docker image ls -a

Przekonujemy się że takich "anonimowych" obrazów jest dużo więcej.

Czym są obrazy Docker <none> i czy powinniśmy je usuwać?

Te obrazy to albo:

  1. Obrazy pośrednie (intermediate), do których są odniesienia z innych obrazów przez co nie można ich usunąć bez usuwania obrazów, które do nich referują. Są tworzone podczas procesu budowania obrazu. To właśnie one reprezentują poszczególne warstwy obrazu, które powstają w wyniku wykonania instrukcji zdefiniowanych w Dockerfile. Obrazy pośrednie są ukryte (od wersji Docker 1.13) przed oczami użytkownika i dopiero flaga --all lub -a pozwala je zobaczyć.

  2. Obrazy wiszące (dangling), czyli takie, do których nie ma już referencji. Można je usunąć i są widoczne domyślnie.

docker image rm $(docker image ls -f dangling=true -q)
# lub
docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N]

Skoro omówiliśmy czym są obrazy pośrednie to możemy w płynny sposób przejść do budowania obrazów?

Budowanie małych obrazów Docker

Przy okazji omawiania obrazów pośrednich zostało wspomniane, że to właśnie one reprezentują warstwy obrazu. Otóż Docker przechowuje obrazy jako serię warstw. Każda instrukcja w Dockerfile, takie jak RUN, COPY, ADD, tworzy nową warstwę. Chociaż Docker został zaprojektowany w taki sposób aby ograniczyć zajmowanie przestrzeni dyskowej, a warstwy obrazów są nakładane na siebie w sposób, który pozwala na współdzielenie plików między nimi bez ich dublowania, każda nowa warstwa zwiększa rozmiar końcowego obrazu ponieważ zawiera różnice (zmiany) w stosunku do warstw poprzednich. Dlatego ograniczenie liczby instrukcji w Dockerfile, zwłaszcza poprzez grupowanie podobnych instrukcji, może pozytywnie wpływać na wielkość końcowych obrazów Docker.

I tak zamiast używać wielu instrukcji RUN do instalacji pakietów, lepiej jest połączyć te instrukcje w jedną, używając operatora &&. To pozwala zredukować liczbę warstw i zapewnia, że wszystkie tymczasowe pliki użyte w procesie instalacji mogą być usunięte w ramach tej samej warstwy.

Grupowanie instrukcji COPY lub ADD w Dockerfile jest ograniczone, ponieważ te instrukcje są zaprojektowane do kopiowania plików i folderów z kontekstu budowy do obrazu Docker. Można jednak:

  1. Skopiować wiele plików lub folderów, podając wiele źródeł i jeden cel. COPY file1.txt file2.txt /destination/
  2. Korzystanie z Wildcards (Symboli Wieloznacznych) COPY *.txt /destination/
  3. Zorganizować pliki w folderach źródłowych w taki sposób, aby kopiować całe foldery `COPY ./source/ /destination/

Warto jest też wspomnieć w tym miejscu o pliku .dockerignore do wykluczenia niepotrzebnych plików i folderów z kontekstu budowy, co zmniejsza czas budowania i rozmiar obrazu.

Odpowiednie grupowanie i ograniczenie liczby instrukcji w Dockerfile może znacząco zmniejszyć rozmiar końcowego obrazu Docker, poprawić wydajność procesu budowania oraz ułatwić zarządzanie obrazami. To ważna praktyka w optymalizacji obrazów Docker, szczególnie w środowiskach produkcyjnych, gdzie zarówno rozmiar obrazów, jak i czas potrzebny na ich budowanie i wdrażanie, są krytycznymi czynnikami.

O warstwach będzie jeszcze mowa przy okazji omawiania Build Cache tymczasem przejdźmy do kontenerów.

Konsumpcja miejsca na dysku przez kontenery Docker-a

Powołanie nowej instancji obrazu, jaką jest kontener też nie jest obojętne z punktu widzenia zarządzania miejscem na dysku.

Kontener Docker-a tworzy własną warstwę zapisu

Każdy kontener gdy jest uruchomiony, tworzy warstwę zapisu na wierzchu warstw tylko do odczytu obrazu bazowego. Wszystkie zmiany dokonywane w kontenerze, takie jak instalacja nowych pakietów, zapisywanie nowych plików czy modyfikacja konfiguracji, są przechowywane w tej warstwie.

W zależności od używanego sterownika lokalizacja fizyczna warstw na hoście może być różna ale dla najpopularniejszego sterownika Overlay2 używanego w wielu dystrybucjach linuxa, zarówno warstwy obrazów jak i warstwy zapisu kontenerów trzymane są w folderze /var/lib/docker/overlay2. Jeśli kontener będzie przechowywał dane we własnym systemie plików, to będą one magazynowane właśnie w tym katalogu.

Warstwa zapisu nie znika po zatrzymaniu kontenera, co oznacza, że wszelkie dane w niej zawarte nadal zajmują miejsce na dysku.

Logi kontenera Docker potrafią szybko rosnąć

Zatrzymane kontenery mogą również przechowywać logi i inne dane tymczasowe, które były generowane podczas ich działania. Logi kontenera - przy założeniu, że kontener używa domyślnego sterownika logowania json-file lub local - trzymane są w folderze /var/lib/docker/container/[CONTAINER_ID] gdzie [CONTAINER_ID] to unikalny identyfikator kontenera.

Dobór sterownika logowania i jego konfiguracja ma ogromny wpływ na wyczerpywanie się miejsca na dysku. Nie będę w tym miejscu omawiał wszystkich sterowników logowania Docker, gdyż wiele z nich umożliwia przesyłanie logów do systemów zewnętrznych albo jest dostępna jedynie w wersji Docker Enterprise Edition. Wspomnę jedynie o tym, że domyślny sterownik json-file warto zastąpić nowszym local gdyż oferuje lepszą kontrolę nad zarządzaniem logami, w tym:

  • Rotację logów - możliwość ustawienia limitów dotyczących liczby i rozmiaru plików logów, dzięki czemu unika się zapełnienia dysku przez nieograniczone logi.

  • Wydajność - użycie bardziej wydajnego formatu zapisu, co jest korzystne, zwłaszcza w środowiskach z dużą ilością logów.

Można skonfigurować sterownik local dla poszczególnych kontenerów podczas ich uruchamiania lub globalnie dla całego środowiska Docker. Oto przykład konfiguracji sterownika local dla kontenera:

docker run --log-driver=local --log-opt max-size=10m --log-opt max-file=3 my-image

W tym przykładzie, logi będą rotowane, gdy osiągną rozmiar 10MB, a zachowane zostaną tylko trzy najnowsze pliki logów.

Pliki stanu kontenera

Docker przechowuje również pewne metadane i pliki stanu dla każdego kontenera, które zajmują dodatkowe, choć zazwyczaj niewielkie, miejsce na dysku.

Usuwanie niepotrzebnych kontenerów Docker-a

Niepotrzebne kontenery usuwamy komendą:

docker rm [CONTAINER_ID]

albo hurtowo

docker container prune  
WARNING! This will remove all stopped containers.  
Are you sure you want to continue? [y/N]

UWAGA!

Jest bardzo ważne aby nie spieszyć się z usuwaniem nieaktywnych kontenerów, zwłaszcza jeśli ich zatrzymanie nastąpiło w nieplanowany sposób, gdyż dopóki kontener istnieje, jest możliwość sprawdzenia w logach przebiegu jego pracy.


Jeśli chcemy usunąć wszystkie kontenery możemy skorzystać z polecenia

docker container rm -f $(docker container ls -aq)

Dobrze jest też wyrobić sobie nawyk natychmiastowego usuwania kontenerów "jednorazowego" użycia tworzonych tylko po to aby wykonać pojedynczą operację, czy zdebugować coś wewnątrz kontenera. Sprawę ułatwia flaga --rm ponieważ w przypadku jej użycia zatrzymanie kontenera oznacza natychmiastowe, automatyczne usunięcie kontenera.

docker run --rm -it python:3 python

Woluminy Docker i zapotrzebowanie na przestrzeń dyskową

Woluminy Docker-a są przechowywane poza strukturą plików samego kontenera po to aby zachować ich trwałość nawet po usunięciu kontenera.

Fizycznie zapisywane są na hoście w specjalnie wyznaczonym katalogu (pomijając woluminy typu bind mounts - czyli podmontowane katalogi lub pliki hosta do katalogów lub plików wewnątrz kontenera). Domyślna lokalizacja tego katalogu zależy od systemu operacyjnego i konfiguracji Docker, ale w większości przypadków znajduje się ona w:

/var/lib/docker/volumes/

Możesz uzyskać dostęp do tych danych bezpośrednio z poziomu systemu hosta, co jest szczególnie przydatne dla celów backupu, monitoringu lub gdy potrzebujesz bezpośrednio zarządzać danymi, ale stanowi też potencjalne zagrożenie. Bezpośrednia modyfikacja danych w katalogu wolumenów Docker może prowadzić do nieoczekiwanych problemów więc lepiej wystrzegać się takich praktyk.

Trwałość podstawowy atrybut woluminów Docker-a

Trwałość danych jest szczególnie kluczowa bo o ile kontenery mają charakter efemeryczny czyli łatwo jest je uruchomić, zatrzymać i usunąć, skalować itd. o tyle utrata danych często wiąże się z brakiem możliwości ich odtworzenia chyba że wcześniej zadbaliśmy o kopię zapasową. Woluminy mogą być współdzielone przez wiele kontenerów i nawet jeśli nie są aktualnie używane można uruchomić kontener, który zacznie z nich korzystać.

Anonimowe czy nazwane woluminy Dockera?

W Dockerze istnieją dwa główne typy woluminów: nazwane (named volumes) i nienazwane (anonymous volumes). Obie te formy służą do przechowywania danych poza systemem plików kontenera i nie ma między nimi różnic jeśli chodzi o obciążenie dysku, ale wspominam o tym bo różnią się sposobem zarządzania i możliwościami wykorzystania.

Woluminy anonimowe są automatycznie tworzone bez konieczności wcześniejszego definiowania i zapewniają wyższy poziom izolacji danych kontenera od hosta. Z drugiej jednak strony trudniej jest je śledzić, powiązać z jakimś konkretnym kontenerem i nimi zarządzać. Dlatego w przypadku ich użycia jest dużo większe ryzyko pozostawienia na dysku "osieroconych" woluminów.

Dlatego w przypadku usuwania kontenerów, o których wiemy że używają woluminów anonimowych dobrze jest to zrobić w jednym kroku korzystając z flagi -v.

docker rm -v [nazwa_lub_id_kontenera]

UWAGA !

Zwróć proszę uwagę, że opcja -v zapewnia, że wszystkie anonimowe woluminy powiązane z kontenerem zostaną usunięte wraz z nim, ale woluminy nazwane, które zostały utworzone i zamontowane w kontenerze, nie zostaną usunięte.

W Docker Compose działa to inaczej

docker-compose down -v

Usunięte zostaną zarówno woluminy anonimowe jak i nazwane.


Poza specyficznymi sytuacjami powinno się właściwie zawsze korzystać z woluminów nazwanych. Są łatwe w identyfikacji i zarządzaniu, trwałe, przenośne i można je podmontować do wielu kontenerów.

Te powody zadecydowały o tym, że komenda ...

docker system prune  
WARNING! This will remove:  
- all stopped containers  
- all networks not used by at least one container  
- all dangling images  
- all dangling build cache  
  
Are you sure you want to continue? [y/N]

... nie usuwa woluminów nawet jak w bieżącym momencie nie ma podpiętego do nich kontenera.

Użycie opcji --volumes umożliwia usunięcie nieużywanych woluminów ale tylko anonimowych podobnie jak polecenie.

docker volume prune
WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N]

W przypadku konieczności usunięcia wszystkich woluminów bez względu na to czy są używane i czy są nazwane zawsze można użyć polecenia

docker volume rm -f $(docker volume ls -q)

Docker build cache

Docker build cache to mechanizm używany przez Docker podczas budowania obrazów, który ma na celu przyspieszenie tego procesu. Cache ten przechowuje wyniki poszczególnych instrukcji z Dockerfile, aby w przypadku ponownego budowania obrazu uniknąć powtarzania tych samych kroków, jeśli nie nastąpiły żadne zmiany.

Podobnie jak warstwy obrazów tak samo cache zależy od użycia instrukcji RUN, COPY i ADD. Szczególnie dwie ostatnie są istotne ponieważ mechanizm wykrywania zmian sprawdza sumy kontrolne plików, które są dodawane do obrazu. Jeśli pliki te się zmienią, cache dla tych instrukcji i wszystkich kolejnych nie będzie używany. Odpowiednia kolejność instrukcji w Dockerfile może pomóc w lepszym wykorzystaniu cache. Na przykład, zmiany w instrukcjach dotyczących kodu aplikacji powinny występować po instrukcjach instalacji zależności, aby zmiany w kodzie nie unieważniały cache zależności.

Mogłoby się wydawać, że mechanizm buforowania jest tożsamy z mechanizmem warstw ale są między nimi istotne różnice.

  • Build cache jest specyficzny dla procesu budowania obrazów i służy do przyspieszenia tego procesu. Dane w nim są tymczasowe i mogą być usunięte bez wpływu na działające kontenery.
  • Warstwy obrazów i kontenerów Docker są używane do przechowywania i zarządzania danymi obrazów i kontenerów w czasie ich działania i są trwałe, dopóki obrazy lub kontenery są przechowywane.

Można wymusić budowanie obrazu bez użycia cache za pomocą opcji --no-cache.

docker image build --no-cache .

Jest to przydatne, gdy chcesz mieć pewność, że wszystkie kroki są wykonywane od nowa.

Można też wyczyścić cache poleceniem

docker builder prune

Polecam zapoznać się z opcjami tego polecenia w szczególności z flagami --filter i --keep-storage bo dają nam większą elastyczność i kontrolę na przechowywanym buforem.

Reasumując

Polecenie docker system df jest przydatne do monitorowania i zarządzania wykorzystaniem przestrzeni dyskowej przez Docker, umożliwiając identyfikację i usuwanie nieużywanych obrazów, kontenerów czy woluminów, co pozwala na optymalizację wykorzystania zasobów.

Poprawnie przygotowane obrazy Docker-a zawierają tylko niezbędne do działania pliki, a nowoczesne sterowniki magazynowania, jak Overlay2, efektywniej zarządzają warstwami obrazów Docker-a dzięki czemu zajmują one mniej miejsca na dysku.

Dobór obrazu bazowego jest kluczowy z punktu widzenia szybkości budowania ale też rozmiaru powstałego na jego podstawie obrazu. Odpowiednie grupowanie i ograniczenie liczby instrukcji w Dockerfile może znacząco zmniejszyć rozmiar końcowego obrazu Docker-a, poprawić wydajność procesu budowania oraz ułatwić zarządzanie obrazami. To ważna praktyka w optymalizacji obrazów Docker, szczególnie w środowiskach produkcyjnych, gdzie zarówno rozmiar obrazów, jak i czas potrzebny na ich budowanie i wdrażanie, są krytycznymi czynnikami.

Kontenery - także te zatrzymane - również zajmują miejsce na dysku z uwagi na warstwę zapisu i inne skojarzone z nimi dane w tym m.in. logi aplikacji. Dobór odpowiedniego sterownika może ułatwić narzucenie ograniczeń w tej kwestii. Jest to o tyle istotne, że przyrost logów następuje w trakcie działania kontenerów i jeśli nie jest monitorowane może nas zaskoczyć nagłym brakiem miejsca na serwerze.

Najważniejsza jest trwałość danych ale też łatwość zarządzania nimi dlatego zazwyczaj najlepszym pomysłem jest używanie woluminów nazwanych, które zmniejszają ryzyko pozostawienia na dysku osieroconych zbiorów danych.

Docker build cache jest ważnym narzędziem w ekosystemie Docker, pozwalającym na szybsze iteracje podczas rozwoju i budowania obrazów, a także efektywne zarządzanie zasobami systemowymi. Usunięcie cache jest bezpieczną operacją ale może wpłynąć negatywnie na szybkość budowania obrazów

Warto monitorować obłożenie dysku i raz na jakiś czas czyścić go ze zbędnych plików. Dobrym pomysłem wydaje się cykliczne uruchamianie np. w cronie polecenia

docker system prune -f

Usuwanie cykliczne volumenów - nawet anonimowych - wydaje się ryzykownym pomysłem dlatego nie dodałem flagi --volumes

Akceptuję Ta strona zapisuje niewielkie pliki tekstowe, nazywane ciasteczkami (ang. cookies) na Twoim urządzeniu w celu lepszego dostosowania treści oraz dla celów statystycznych. Możesz wyłączyć możliwość ich zapisu, zmieniając ustawienia Twojej przeglądarki. Korzystanie z tej strony bez zmiany ustawień oznacza zgodę na przechowywanie cookies w Twoim urządzeniu.