SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor

Jak zwolnić miejsce na dysku VPS używanym przez Docker

Dowiedz się, jak bezpiecznie usunąć zbędne obrazy, kontenery i cache Dockera. Sprawdź, które warstwy zajmują najwięcej miejsca i odzyskaj przestrzeń bez utraty cennych danych.

Zidentyfikuj zajętość dysku przed przystąpieniem do czyszczenia

Docker zajmuje miejsce na dysku VPS w czterech obszarach: obrazy, zatrzymane kontenery, pamięć podręczna budowania oraz wolumeny lokalne. Uruchom najpierw docker system df, aby sprawdzić, który z nich zajmuje najwięcej miejsca, a następnie wykonaj najbardziej precyzyjne czyszczenie. Kolejność ma znaczenie, ponieważ ostatnie polecenie w tym przewodniku, docker volume prune -a, usuwa dane bez możliwości ich przywrócenia.

Zacznij od systemu plików, a nie od Dockera.

df -h /
sudo du -xh --max-depth=1 /var/lib/docker | sort -h

df wskazuje skalę problemu. du pokazuje lokalizację zajętego miejsca. Flaga -x ogranicza du do jednego systemu plików, dzięki czemu nie podąża ona za punktami montowania do oddzielnych wolumenów i nie zlicza ich dwukrotnie. Istotnych jest pięć katalogów: overlay2 przechowuje warstwy obrazów i kontenerów, volumes zawiera dane wolumenów, containers przechowuje metadane kontenerów i pliki dzienników, buildkit zawiera pamięć podręczną budowania, a image przechowuje metadane warstw.

Uwaga dotycząca sudo i wieloznaczników powłoki, ponieważ często powodują one stratę czasu. Katalog /var/lib/docker jest własnością root i nie jest dostępny dla zwykłego użytkownika, dlatego ls /var/lib/docker zwraca Permission denied. Polecenie typu sudo du -sh /var/lib/docker/* również zakończy się niepowodzeniem, ponieważ powłoka rozwija * przed uruchomieniem sudo, a powłoka nie ma uprawnień do odczytu tego katalogu. Każde poniższe polecenie używa find lub --max-depth zamiast wieloznacznika właśnie z tego powodu.

Teraz widok z perspektywy Dockera.

docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          24        6         12.4GB    8.91GB (71%)
Containers      31        6         1.42GB    1.39GB (97%)
Local Volumes   14        5         6.03GB    4.11GB (68%)
Build Cache     212       0         9.87GB    9.87GB

Powyższe liczby pochodzą z jednej maszyny i nie odnoszą się bezpośrednio do Twojego systemu. Zwróć uwagę na strukturę danych. TOTAL zlicza obiekty, ACTIVE wskazuje te aktualnie używane, a RECLAIMABLE to szacunkowa ilość miejsca, którą Docker może odzyskać po wyczyszczeniu danej kategorii.

Dwie kwestie dotyczące RECLAIMABLE często wprowadzają w błąd. Wartość ta zlicza współdzielone warstwy obrazów dla każdego obrazu, który z nich korzysta, więc wiersz obrazów zazwyczaj obiecuje więcej miejsca, niż faktycznie zostanie odzyskane. Ponadto nie obejmuje ona plików dzienników kontenerów, ponieważ Docker nie traktuje ich jako obiektów możliwych do usunięcia. Jeśli du wskazuje katalog znacznie większy niż wynika to z docker system df, przyczyną są pliki dzienników; sekcja na ten temat znajduje się poniżej.

Dodaj -v, aby uzyskać szczegółowe zestawienie dla każdego obiektu.

docker system df -v

Dzieli to podsumowanie na sekcje według typu obiektu. Sekcja obrazów dodaje kolumny SHARED SIZE oraz UNIQUE SIZE, co pozwala sprawdzić rzeczywisty koszt pojedynczego obrazu. Sekcja wolumenów dodaje licznik LINKS, który określa liczbę kontenerów podłączonych do danego wolumenu. Pamiętaj o LINKS, ponieważ wartość 0 jest głównym kryterium, według którego polecenia czyszczenia wolumenów kwalifikują je do usunięcia.

Obrazy typu dangling a obrazy nieużywane

Te dwa pojęcia wydają się zamienne, lecz takie nie są. Filtry działają odmiennie, ponieważ obiekty te różnią się od siebie.

Obraz typu dangling to obraz bez przypisanej etykiety (tagu). W poleceniu <none> widnieje jako docker images. Taki obraz powstaje przy każdej przebudowie: docker build -t myapp:latest . przenosi etykietę myapp:latest na nowy obraz, a stary obraz zachowuje wszystkie swoje warstwy, tracąc przy tym nazwę. Żaden obiekt się do niego nie odwołuje i żaden proces nie usuwa go automatycznie.

Obraz nieużywany to dowolny obraz, z etykietą lub bez, do którego nie odwołuje się obecnie żaden kontener. Obraz postgres:16 pobrany w zeszłym miesiącu, który nie jest aktualnie uruchomiony, jest nieużywany, ale nie jest typu dangling.

docker image prune        # dangling images only
docker image prune -a     # every image no container refers to

Drugie polecenie wymaga potwierdzenia.

WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N]

Należy uważnie przeczytać ten komunikat. „Powiązane z nimi” oznacza istniejący obiekt kontenera, niezależnie od tego, czy jest uruchomiony, czy zatrzymany. Jeśli wykonano docker compose down, kontenery zostały usunięte, więc każdy obraz używany przez te usługi staje się nieużywany, a -a usunie je wszystkie. Nie traci się niczego, czego nie można odzyskać, jednak kolejne polecenie docker compose up -d wymusi ponowne pobranie lub przebudowanie całości, co na małym serwerze VPS wiąże się z kosztami transferu i czasu budowania. Jest to jeden z praktycznych powodów, dla których warto wiedzieć co usuwa docker compose down, a co pozostawia stop, zanim wykona się operację czyszczenia.

Filtr pozwala wykluczyć z zakresu działania niedawno utworzone obrazy.

docker image prune -a --filter "until=240h"

To polecenie usuwa nieużywane obrazy utworzone ponad 240 godzin (10 dni) temu, pozostawiając nowsze bez zmian. Wartość until przyjmuje ciąg czasu w formacie Go, taki jak 240h, lub bezwzględny znacznik czasu, na przykład 2026-08-01T00:00:00.

Czym jest pamięć podręczna budowania i dlaczego rośnie bez ograniczeń

BuildKit jest domyślnym mechanizmem budowania w Docker, używanym dla docker build oraz docker compose build od wersji Docker Engine 23.0. Przechowuje on w pamięci podręcznej wynik każdego kroku każdego uruchomionego pliku Dockerfile, a dane te są składowane w /var/lib/docker/buildkit. Dzięki tej pamięci podręcznej kolejne budowanie kończy się w kilka sekund, więc mechanizm ten spełnia swoje zadanie. Problemem jest brak domyślnego mechanizmu wygaszania starych wpisów. Budując ten sam obraz pięćdziesiąt razy z krokiem COPY, który za każdym razem ulega zmianie, zachowywanych jest pięćdziesiąt zestawów warstw.

Pamięć podręczna budowania jest niewidoczna dla docker image prune. Jest to osobny typ obiektu, posiadający własne polecenie.

docker builder prune                        # dangling cache
docker builder prune -a                     # all unused cache
docker builder prune --filter until=168h    # cache untouched for 7 days

Żadne z tych działań nie wpływa na obrazy ani dane użytkownika. Jedynym kosztem wyczyszczenia pamięci podręcznej budowania jest fakt, że kolejne budowanie jednorazowo potrwa dłużej. Na serwerach VPS, gdzie obrazy są regularnie przebudowywane, Build Cache często stanowi największy wiersz w docker system df, co czyni go najbezpieczniejszym dużym elementem do usunięcia.

Polecenia prune, uszeregowane od bezpiecznych do destrukcyjnych

Należy przechodzić przez tę listę, zatrzymując się, gdy df -h / odzyska sprawność. Każde polecenie po zakończeniu wyświetla linię Total reclaimed space:.

  1. docker container prune usuwa zatrzymane kontenery. Ich warstwy zapisu również są usuwane, więc wszelkie dane zapisane przez kontener poza wolumenem zostaną utracone. Wolumeny pozostają nienaruszone.
  2. docker image prune usuwa wyłącznie nieużywane (dangling) obrazy. Jest to najbezpieczniejsze polecenie dotyczące obrazów.
  3. docker builder prune usuwa nieużywaną pamięć podręczną budowania (build cache). Kosztem jest konieczność powolnego budowania w przyszłości.
  4. docker image prune -a usuwa każdy obraz, do którego nie odwołuje się żaden kontener. Kosztem jest konieczność ponownego pobrania lub zbudowania obrazu.
  5. docker system prune wykonuje trzy pierwsze operacje jednocześnie i dodaje usuwanie nieużywanych sieci.
  6. docker volume prune usuwa nieużywane anonimowe wolumeny.
  7. docker volume prune -a usuwa nieużywane wolumeny, w tym wolumeny nazwane. Jest to polecenie, które usuwa bazy danych.

docker system prune przed uruchomieniem informuje o swoim zakresie działania.

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

Wolumeny zostały celowo pominięte w tej liście. Dodanie --volumes przywraca anonimowe wolumeny do zakresu działania. Dodanie -a rozszerza krok dotyczący obrazów z samych nieużywanych (dangling) do wszystkich nieużywanych obrazów. Pełne polecenie docker system prune -a --volumes -f na serwerze produkcyjnym to sposób, w jaki użytkownicy tracą dane podczas próby zwolnienia miejsca.

Dlaczego czyszczenie wolumenów usuwa bazę danych

To jest sekcja, którą należy przeczytać dwukrotnie.

Wolumen jest uznawany za nieużywany, gdy nie jest do niego podłączony żaden kontener. To jedyne kryterium. Docker nie sprawdza, czy wolumen jest pusty, czy plik compose nadal go deklaruje, ani czy zawiera jedyną kopię bazy danych. LINKS 0 w docker system df -v oznacza możliwość usunięcia i nic więcej.

Rozważmy dwa standardowe działania wykonane jedno po drugim. Uruchomienie docker compose down w celu czystego restartu stosu usuwa kontenery, pozostawiając nazwane wolumeny na miejscu, zgodnie z dokumentacją. Wolumen Postgres pozostaje wtedy niepodłączony. Dziesięć minut później uruchomienie docker volume prune -a w celu zwolnienia miejsca powoduje usunięcie bazy danych. Oba polecenia zadziałały poprawnie. Sekwencja ta doprowadziła do utraty danych.

Od wersji Docker Engine 23.0 (wersja API 1.42) podstawowe polecenie ma węższe działanie niż wcześniej.

WARNING! This will remove anonymous local volumes not used by at least one container.

Wolumen anonimowy to taki, który Docker utworzył automatycznie, zazwyczaj dlatego, że obraz deklaruje VOLUME, a użytkownik nie nadał mu nazwy. Zazwyczaj przechowują one dane, których nie trzeba zachowywać. Wolumen nazwany, zdefiniowany w pliku compose, jest usuwany tylko po dodaniu flagi -a. Starsze wersje Dockera usuwały oba typy za pomocą podstawowego polecenia, dlatego nie należy polegać na nawykach wyrobionych na systemach, które zostały w międzyczasie zaktualizowane. Różnica staje się zrozumiała dopiero po zapoznaniu się z różnicami między wolumenami nazwanymi a bind mounts, ponieważ bind mount nie jest wolumenem Dockera i żadne polecenie czyszczenia go nie usunie.

Sprawdź przed usunięciem. Zamień myapp_pgdata na nazwę wolumenu, który jest weryfikowany.

docker volume ls -f dangling=true
docker volume inspect myapp_pgdata
sudo ls -la /var/lib/docker/volumes/myapp_pgdata/_data

Filtr dangling=true dla wolumenu oznacza brak odniesień, a nie pustą zawartość. Wyświetlenie _data pokazuje, co faktycznie znajduje się wewnątrz. Jeśli w środku znajduje się katalog pgdata lub mysql, należy przerwać proces i wykonać kopię zapasową przed kontynuowaniem. Do takiej samej utraty danych prowadzi docker compose down -v, które usuwa wszystkie wolumeny zadeklarowane w pliku compose bez dodatkowych pytań.

Wolumen jest jedynym elementem na hoście Docker, którego nie można odtworzyć poprzez przebudowę. Dlatego dane wolumenów powinny być przechowywane w kopii zapasowej restic wykonywanej poza serwerem, gdzie błędnie wpisana flaga nie może ich usunąć.

Gdy czyszczenie nie pomaga: pliki dziennika kontenerów

Wszystko zostało wyczyszczone, docker system df nie wykazuje prawie żadnych danych możliwych do odzyskania, a dysk nadal jest pełny. Sprawdź dzienniki.

sudo find /var/lib/docker/containers -name '*-json.log' -exec du -h {} + | sort -h | tail -10

Każdy kontener zapisuje swoje standardowe wyjście i standardowy błąd do pliku JSON w /var/lib/docker/containers/. W domyślnej instalacji max-size nie jest ustawione, co oznacza brak limitów. W rezultacie kontener zablokowany w pętli awarii zapisuje dane, aż partycja zostanie zapełniona. Żadne polecenie czyszczenia nie usuwa tych plików, ponieważ kontenery, które je generują, są uruchomione, co z definicji wyklucza je z procesu usuwania.

Nie usuwaj pliku. Wykonanie rm na otwartym pliku dziennika nic nie zwolni, ponieważ Docker daemon nadal utrzymuje otwarty deskryptor pliku, a jądro systemu zachowuje przydzielone bloki do momentu zamknięcia tego uchwytu. df nie zmieni się w ogóle. Zamiast tego wykonaj obcięcie pliku (truncate), co zachowa ten sam i-węzeł (inode) i pozwoli demonowi kontynuować zapis.

sudo find /var/lib/docker/containers -name '*-json.log' -exec truncate -s 0 {} +
df -h /

To rozwiązanie tymczasowe. docker logs dla tych kontenerów teraz nie zwraca nic, a pliki natychmiast zaczynają rosnąć ponownie. Właściwym rozwiązaniem jest rotacja, która została opisana w następnej sekcji.

Mierz przed i po, za każdym razem

Nigdy nie zgaduj, jaki efekt przyniosło czyszczenie. Pobierz odczyt, wykonaj polecenie, pobierz kolejny odczyt.

df -h /
docker system df
docker builder prune -f --filter until=168h
docker image prune -f
docker system df
df -h /

Porównaj dwa wyjścia df. To jedyna liczba, która decyduje o tym, czy serwer nadal może obsługiwać ruch. docker system df wskazuje, który wiersz faktycznie uległ zmianie, a każde czyszczenie wyświetla własną wartość Total reclaimed space:.

Jeśli df nie uległo zmianie, a docker system df wskazuje na zwolnienie miejsca, oznacza to, że otwarty uchwyt pliku blokuje usunięte bloki, co jest problemem z plikiem dziennika opisanym powyżej. Jeśli obie wartości uległy zmianie, a dysk zapełnia się ponownie w ciągu doby, problemem jest przyrost danych, a nie ich usuwanie. Rozwiązaniem jest rotacja logów oraz zaplanowane zadanie.

Jak zapobiec ponownemu zapełnieniu dysku

Ogranicz rozmiar logów. Utwórz lub edytuj /etc/docker/daemon.json.

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

To ograniczenie ustala limit logów dla każdego kontenera na 30 MB. Każda wartość w log-opts musi być ciągiem znaków, nawet jeśli jest liczbą. Przed restartem sprawdź poprawność składni pliku, ponieważ błędny daemon.json uniemożliwi uruchomienie demona i spowoduje zatrzymanie wszystkich kontenerów.

python3 -m json.tool /etc/docker/daemon.json
sudo systemctl restart docker
docker info | grep -i 'logging driver'

docker info powinno teraz zwracać Logging Driver: json-file. Same limity są widoczne w sekcji LogConfig polecenia docker inspect dla kontenerów utworzonych po restarcie. Jest to istotny szczegół: ustawienie to dotyczy wyłącznie nowych kontenerów. Istniejące kontenery zachowują konfigurację, z którą zostały utworzone, dlatego należy je odtworzyć.

docker compose up -d --force-recreate

Ten sam limit można ustawić dla poszczególnych usług w pliku compose, co jest lepszym rozwiązaniem, gdy jedna generująca dużo logów usługa wymaga indywidualnego limitu.

services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Zaplanuj selektywne czyszczenie. Raz w tygodniu, ograniczając się do nieużywanych obrazów i starej pamięci podręcznej budowania. Nigdy nie umieszczaj -a ani --volumes w zadaniu harmonogramu, ponieważ zadanie uruchomione w czasie, gdy stos jest wyłączony, usunie obrazy tego stosu, a przy użyciu --volumes rozpocznie usuwanie danych.

sudo tee /etc/cron.weekly/docker-prune >/dev/null <<'EOF'
#!/bin/sh
docker image prune -f
docker builder prune -f --filter until=168h
EOF
sudo chmod +x /etc/cron.weekly/docker-prune
sudo /etc/cron.weekly/docker-prune

Ostatnia linia uruchamia skrypt ręcznie, aby sprawdzić jego wynik przed automatycznym wykonaniem. Plik musi być wykonywalny, a jego nazwa nie może zawierać kropki, ponieważ run-parts pomija pliki niewykonywalne oraz te posiadające rozszerzenie.

Monitoruj wolne miejsce. Czyszczenie po zapełnieniu dysku to działanie naprawcze. Alarm przy 80 procentach to profilaktyka.

usage=$(df --output=pcent / | tr -dc '0-9')
[ "$usage" -ge 80 ] && echo "root filesystem at ${usage} percent full"

Umieść to w cron z wybranym systemem powiadomień. Wolne miejsce to tylko połowa obrazu sytuacji, dlatego połącz alarm z monitorowaniem kondycji dysku na VPS, ponieważ awaria dysku oraz jego zapełnienie powodują zatrzymanie kontenerów, a wymagają odmiennych działań naprawczych.

Wszystkie powyższe kroki zakładają standardową instalację z głównym katalogiem danych w /var/lib/docker. Jeśli przeniesiono go za pomocą klucza data-root w daemon.json, należy podstawić własną ścieżkę w każdym poleceniu. Prawidłowe zaplanowanie układu partycji na nowym serwerze jest częścią konfiguracji Docker na VPS i jest znacznie łatwiejsze do wykonania, zanim 40 GB kontenerów znajdzie się na niewłaściwej partycji.

FAQ

Czy docker system prune usuwa moje wolumeny?

Nie. Standardowe polecenie usuwa zatrzymane kontenery, nieużywane sieci, osierocone obrazy oraz nieużywaną pamięć podręczną budowania, a monit potwierdzenia wyszczególnia dokładnie te elementy. Wolumeny są uwzględniane tylko po dodaniu flagi --volumes, a od wersji Docker Engine 23.0 flaga ta obejmuje jedynie wolumeny anonimowe, a nie nazwane. Wolumeny nazwane są usuwane za pomocą docker volume prune -a oraz docker compose down -v. To właśnie te dwa polecenia wymagają ostrożności.

Dlaczego po uruchomieniu docker prune dysk nadal jest pełny?

Istnieją dwie typowe przyczyny. Pierwszą są pliki dzienników kontenerów w /var/lib/docker/containers/, których żadne polecenie prune nie usuwa i które rosną bez ograniczeń, dopóki nie zostanie skonfigurowane max-size. Drugą przyczyną jest usunięty plik, który nadal jest otwarty przez proces: jeśli usunięto dziennik za pomocą rm podczas pracy kontenera, demon utrzymuje deskryptor pliku, a jądro nie zwalnia bloków, przez co df nie wykazuje zmian. Należy porównać wyniki sudo du -xh --max-depth=1 /var/lib/docker oraz docker system df, aby ustalić, z którym przypadkiem mamy do czynienia.

Jaka jest różnica między docker image prune a docker image prune -a?

Standardowe polecenie usuwa tylko osierocone obrazy, czyli takie, które utraciły swój tag, co niemal zawsze wynika z ponownego budowania. Wariant -a usuwa każdy obraz, do którego nie odwołuje się żaden istniejący kontener, w tym obrazy z tagami pobrane celowo. Po wykonaniu docker compose down kontenery przestają istnieć, więc -a usunie również obrazy powiązane z tym stosem. Nic nie zostaje trwale utracone, ponieważ kolejne uruchomienie wymusi ponowne pobranie lub zbudowanie obrazów, jednak przy wolnym łączu oznacza to długi czas oczekiwania.

Jak zapobiec zapełnianiu dysku przez dzienniki Docker?

Należy ustawić max-size oraz max-file w sekcji log-opts w pliku /etc/docker/daemon.json, a następnie zrestartować demona za pomocą sudo systemctl restart docker. Ustawienie to dotyczy tylko kontenerów utworzonych po restarcie, dlatego uruchomione kontenery należy odtworzyć za pomocą docker compose up -d --force-recreate. Te same dwie opcje można ustawić dla każdej usługi w pliku compose w sekcji logging, co jest zalecane, gdy jedna usługa generuje znacznie więcej logów niż pozostałe.

Czy uruchamianie docker system prune w zadaniu cron jest bezpieczne?

Standardowe polecenie docker system prune -f jest bezpieczne na hoście, na którym wszystkie stosy są uruchomione, jednak usuwa ono zatrzymane kontenery, więc usunie również kontener zatrzymany celowo w celu późniejszego restartu. Bezpieczniejszym zadaniem zaplanowanym jest docker image prune -f wraz z docker builder prune -f --filter until=168h, co zwalnia dwa najszybciej rosnące zasoby i nie wpływa na wolumeny. Nigdy nie należy planować zadań -a ani --volumes.