Porady dotyczące platformy Docker: Oczyść komputer ze śmieci

Porady dotyczące platformy Docker: Oczyść komputer ze śmieci

Hej Habro! Zwracam uwagę na tłumaczenie artykułu „Wskazówki dotyczące platformy Docker: oczyść komputer lokalny” autor Luca Juggery’ego.

Dzisiaj porozmawiamy o tym, jak Docker wykorzystuje przestrzeń dyskową komputera hosta, a także dowiemy się, jak uwolnić to miejsce ze skrawków nieużywanych obrazów i kontenerów.


Porady dotyczące platformy Docker: Oczyść komputer ze śmieci

Całkowite zużycie

Docker to fajna rzecz, pewnie mało kto dzisiaj w to wątpi. Zaledwie kilka lat temu ten produkt dał nam zupełnie nowy sposób budowania, dostarczania i uruchamiania dowolnego środowiska, pozwalając nam znacznie zaoszczędzić zasoby procesora i pamięci RAM. Oprócz tego (a dla niektórych będzie to najważniejsza rzecz) Docker pozwolił nam niesamowicie uprościć i ujednolicić zarządzanie cyklem życia naszych środowisk produkcyjnych.

Jednak wszystkie te rozkosze współczesnego życia mają swoją cenę. Kiedy uruchamiamy kontenery, pobieramy lub tworzymy własne obrazy i wdrażamy złożone ekosystemy, musimy płacić. A płacimy między innymi miejscem na dysku.

Jeśli nigdy nie zastanawiałeś się, ile miejsca Docker faktycznie zajmuje na Twoim komputerze, możesz być niemile zaskoczony wynikami tego polecenia:

$ docker system df

Porady dotyczące platformy Docker: Oczyść komputer ze śmieci

To pokazuje użycie dysku Dockera w różnych kontekstach:

  • obrazy – całkowity rozmiar obrazów, które zostały pobrane z repozytoriów obrazów i zbudowane w Twoim systemie;
  • kontenery – całkowita ilość miejsca na dysku zajmowana przez uruchomione kontenery (czyli całkowity wolumen warstw odczytu i zapisu wszystkich kontenerów);
  • wolumeny lokalne – objętość magazynu lokalnego zamontowanego na kontenerach;
  • build cache – pliki tymczasowe generowane w procesie budowania obrazu (za pomocą narzędzia BuildKit, dostępnego od wersji Dockera 18.09).

Założę się, że po tym prostym transferze zapragniesz oczyścić dysk ze śmieci i przywrócić do życia cenne gigabajty (uwaga: zwłaszcza jeśli co miesiąc płacisz czynsz za te gigabajty).

Użycie dysku przez kontenery

Za każdym razem, gdy tworzysz kontener na komputerze-hoście, w katalogu /var/lib/docker tworzonych jest kilka plików i katalogów, wśród których warto zwrócić uwagę na następujące:

  • Katalog /var/lib/docker/containers/container_ID – w przypadku korzystania ze standardowego sterownika logowania, w tym miejscu zapisywane są dzienniki zdarzeń w formacie JSON. Zbyt szczegółowe logi, a także logi, których nikt nie czyta ani w żaden inny sposób nie przetwarza, często powodują zapełnienie dysków.
  • Katalog /var/lib/docker/overlay2 zawiera warstwy kontenera do odczytu i zapisu (overlay2 jest preferowanym sterownikiem w większości dystrybucji Linuksa). Jeśli kontener przechowuje dane w swoim systemie plików, to właśnie w tym katalogu zostaną umieszczone.

Wyobraźmy sobie system, na którym zainstalowany jest nieskazitelny Docker, który nigdy nie był zaangażowany w uruchamianie kontenerów ani budowanie obrazów. Raport wykorzystania miejsca na dysku będzie wyglądał następująco:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         0          0          0B         0B
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Uruchommy jakiś kontener, na przykład NGINX:

$ docker container run --name www -d -p 8000:80 nginx:1.16

Co się dzieje z dyskiem:

  • obrazy zajmują 126 MB, to ten sam NGINX, który uruchomiliśmy w kontenerze;
  • kontenery zajmują śmieszne 2 bajty.

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          2B         0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Wnioskując z wniosku, nie mamy jeszcze miejsca, które moglibyśmy zwolnić. Ponieważ 2 bajty to zupełnie niepoważna sprawa, wyobraźmy sobie, że nasz NGINX niespodziewanie zapisał gdzieś 100 megabajtów danych i utworzył w sobie plik test.img o dokładnie takim rozmiarze.

$ docker exec -ti www 
  dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]

Przeanalizujmy ponownie wykorzystanie miejsca na dysku na hoście. Zobaczymy, że kontener (kontenery) zajmuje tam 100 megabajtów.

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          1          104.9MB    0B (0%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Myślę, że Twój dociekliwy mózg już zastanawia się, gdzie znajduje się nasz plik test.img. Poszukajmy tego:

$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img

Nie wchodząc w szczegóły, możemy zauważyć, że plik test.img jest dogodnie zlokalizowany na poziomie odczytu i zapisu, kontrolowanym przez sterownik overlay2. Jeśli zatrzymamy nasz kontener, gospodarz powie nam, że tę przestrzeń w zasadzie można zwolnić:

# Stopping the www container
$ docker stop www

# Visualizing the impact on the disk usage
$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          1          126M       0B (0%)
Containers     1          0          104.9MB    104.9MB (100%)
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Jak możemy to zrobić? Usunięcie kontenera, co pociągnie za sobą wyczyszczenie odpowiedniej przestrzeni na poziomie odczytu i zapisu.

Za pomocą poniższego polecenia możesz za jednym zamachem usunąć wszystkie zainstalowane kontenery i wyczyścić dysk ze wszystkich utworzonych przez nie plików do odczytu i zapisu:

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

Total reclaimed space: 104.9MB

W ten sposób uwolniliśmy 104,9 megabajtów, usuwając kontener. Ponieważ jednak nie używamy już wcześniej pobranego obrazu, staje się on również kandydatem do usunięcia i zwolnienia naszych zasobów:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         1          0          126M       126M (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Uwaga: dopóki obraz jest używany przez co najmniej jeden kontener, nie będziesz mógł skorzystać z tej sztuczki.

Podkomenda Prine, której użyliśmy powyżej, ma wpływ tylko na zatrzymane kontenery. Jeśli chcemy usunąć nie tylko zatrzymane, ale i uruchomione kontenery, powinniśmy skorzystać z jednego z poniższych poleceń:

# Historical command
$ docker rm -f $(docker ps –aq)

# More recent command
$ docker container rm -f $(docker container ls -aq)

Uwagi dodatkowe: jeśli użyjesz parametru -rm podczas uruchamiania kontenera, to po jego zatrzymaniu całe zajmowane przez niego miejsce na dysku zostanie zwolnione.

Korzystanie z obrazów dysków

Kilka lat temu obraz o rozmiarze kilkuset megabajtów był całkowicie normalny: obraz Ubuntu ważył 600 megabajtów, a obraz Microsoft .Net ważył kilka gigabajtów. W tych niepewnych czasach pobranie tylko jednego obrazu mogło spowodować duże straty w zakresie wolnego miejsca na dysku, nawet jeśli poziomy były dzielone między obrazami. Dziś – chwała wielkim – obrazy ważą znacznie mniej, ale mimo to można szybko uzupełnić dostępne zasoby, jeśli nie zachowa się pewnych środków ostrożności.

Istnieje kilka rodzajów obrazów, które nie są bezpośrednio widoczne dla użytkownika końcowego:

  • obrazy pośrednie, na podstawie których zbierane są inne obrazy – nie można ich usunąć w przypadku korzystania z kontenerów opartych na tych „innych” obrazach;
  • obrazy wiszące to obrazy pośrednie, do których nie odwołuje się żaden z uruchomionych kontenerów - można je usunąć.
  • Za pomocą następującego polecenia możesz sprawdzić wiszące obrazy w swoim systemie:

$ docker image ls -f dangling=true
REPOSITORY  TAG      IMAGE ID         CREATED             SIZE
none      none   21e658fe5351     12 minutes ago      71.3MB

Możesz je usunąć w następujący sposób:

$ docker image rm $(docker image ls -f dangling=true -q)

Możemy także użyć komendy Prune:

$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f

Total reclaimed space: 12.9kB

Jeśli nagle chcemy całkowicie usunąć wszystkie obrazy (a nie tylko wiszące) za pomocą jednego polecenia, możemy to zrobić:

$ docker image rm $(docker image ls -q)

Użycie dysku według woluminów

Woluminy służą do przechowywania danych poza systemem plików kontenera. Na przykład, jeśli chcemy zapisać wyniki aplikacji, aby wykorzystać je w inny sposób. Typowym przykładem są bazy danych.

Uruchommy kontener MongoDB, zamontujmy wolumen zewnętrzny względem kontenera i przywróćmy z niego kopię zapasową bazy danych (mamy ją dostępną w pliku bck.json):

# Running a mongo container
$ docker run --name db -v $PWD:/tmp -p 27017:27017 -d mongo:4.0

# Importing an existing backup (from a huge bck.json file)
$ docker exec -ti db mongoimport 
  --db 'test' 
  --collection 'demo' 
  --file /tmp/bck.json 
  --jsonArray

Dane będą znajdować się na komputerze hosta w katalogu /var/lib/docker/volumes. Ale dlaczego nie na poziomie odczytu i zapisu kontenera? Ponieważ w pliku Dockerfile obrazu MongoDB katalog /data/db (w którym MongoDB domyślnie przechowuje swoje dane) jest zdefiniowany jako wolumin.

Porady dotyczące platformy Docker: Oczyść komputer ze śmieci

Uwaga dodatkowa: wiele obrazów, które muszą generować dane, wykorzystuje woluminy do przechowywania tych danych.

Gdy zagramy wystarczająco dużo z MongoDB i zatrzymamy (a może nawet usuniemy) kontener, wolumen nie zostanie usunięty. Będzie nadal zajmował nasze cenne miejsce na dysku, dopóki wyraźnie go nie usuniemy za pomocą takiego polecenia:

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

Cóż, albo możemy użyć znanego nam już podkomenda Prune:

$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da

Total reclaimed space: 25.82GB
luc@saturn:~$

Używanie dysku do pamięci podręcznej kompilacji obrazu

W Dockerze 18.09 proces tworzenia obrazu uległ pewnym zmianom dzięki narzędziu BuildKit. To zwiększa szybkość procesu i optymalizuje przechowywanie danych i zarządzanie bezpieczeństwem. Nie będziemy tutaj rozważać wszystkich szczegółów tego wspaniałego narzędzia, skupimy się jedynie na tym, jak rozwiązuje ono problemy związane z wykorzystaniem miejsca na dysku.

Załóżmy, że mamy całkowicie prostą aplikację Node.Js:

  • plik Index.js uruchamia prosty serwer HTTP, który odpowiada linią na każde otrzymane żądanie:
  • plik package.json definiuje zależności, z których do uruchomienia serwera HTTP używany jest tylko expressjs:

$ cat index.js
var express = require('express');
var util    = require('util');
var app = express();
app.get('/', function(req, res) {
  res.setHeader('Content-Type', 'text/plain');
  res.end(util.format("%s - %s", new Date(), 'Got Request'));
});
app.listen(process.env.PORT || 80);

$ cat package.json
    {
      "name": "testnode",
      "version": "0.0.1",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "dependencies": {
        "express": "^4.14.0"
      }
    }

Plik Dockerfile służący do budowania obrazu wygląda następująco:

FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]

Zbudujmy obraz w zwykły sposób, bez użycia BuildKit:

$ docker build -t app:1.0 .

Jeśli sprawdzimy wykorzystanie miejsca na dysku, zobaczymy, że tylko obraz podstawowy (node:13-alpine) i obraz docelowy (app:1.0) zajmują miejsce:

TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         2          0          109.3MB    109.3MB (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    0          0          0B         0B

Zbudujmy drugą wersję naszej aplikacji, korzystając z BuildKit. Aby to zrobić, wystarczy ustawić zmienną DOCKER_BUILDKIT na 1:

$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .

Jeśli teraz sprawdzimy użycie dysku, zobaczymy, że pamięć podręczna kompilacji (buid-cache) jest tam teraz zaangażowana:

$ docker system df
TYPE           TOTAL      ACTIVE     SIZE       RECLAIMABLE
Images         2          0          109.3MB    109.3MB (100%)
Containers     0          0          0B         0B
Local Volumes  0          0          0B         0B
Build Cache    11         0          8.949kB    8.949kB

Aby to wyczyścić, użyj następującego polecenia:

$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi

Total reclaimed space: 8.949kB

Wyczyść wszystko!

Przyjrzeliśmy się więc oczyszczeniu miejsca na dysku zajmowanego przez kontenery, obrazy i woluminy. Pomaga nam w tym podkomenda Prune. Ale można go również użyć na poziomie systemu dokowanego i wyczyści wszystko, co się da:

$ 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]

Jeśli z jakiegoś powodu oszczędzasz miejsce na swoim komputerze Docker, okresowe uruchamianie tego polecenia powinno stać się Twoim nawykiem.

Źródło: www.habr.com

Dodaj komentarz