Docker Tips: Oczyść swoją maszynę z bałaganu

Docker Tips: Oczyść swoją maszynę z bałaganu

Cześć, Habr! Przedstawiam wam tłumaczenie artykułu "Porady dotyczące Dockera: Oczyść swoją lokalną maszynę" autora Luc Juggery.

Dziś porozmawiamy o tym, jak Docker wykorzystuje przestrzeń dyskową hosta, a także dowiemy się, jak zwolnić tę przestrzeń z nieużywanych obrazów i kontenerów.


Docker Tips: Oczyść swoją maszynę z bałaganu

Całkowite zużycie

Docker to niesamowite narzędzie, o którym pewnie niewielu wątpi dzisiaj. Zaledwie kilka lat temu ten produkt zapewnił nam zupełnie nowy sposób na budowanie, dostarczanie i uruchamianie dowolnego środowiska, znacząco oszczędzając zasoby procesora i pamięci RAM. Oprócz tego (a dla niektórych to może być nawet najważniejsze) Docker niezwykle uprościł i zunifikował zarządzanie cyklem życia używanych środowisk roboczych.

Jednak za wszystkie te zalety nowoczesnego życia trzeba płacić. Gdy uruchamiamy kontenery, pobieramy lub tworzymy własne obrazy, wdrażamy skomplikowane ekosystemy, musimy płacić. I płacimy, między innymi, przestrzenią dyskową.

Jeśli nigdy się nie zastanawiałeś, ile miejsca naprawdę zajmuje twój Docker, możesz być nieprzyjemnie zaskoczony wynikiem tego polecenia:

$ docker system df

Docker Tips: Oczyść swoją maszynę z bałaganu

Tutaj pokazane jest użycie dysku przez Dockera w różnych aspektach:

  • obrazy (images) – całkowity rozmiar obrazów pobranych z repozytoriów obrazów oraz zbudowanych w twoim systemie;
  • kontenery (containers) – całkowita ilość przestrzeni dyskowej używanej przez uruchomione kontenery (mając na myśli całkowitą ilość warstw zapisu dla wszystkich kontenerów);
  • lokalne wolumeny (local volumes) – objętość lokalnych magazynów zamontowanych w kontenerach;
  • cache budowy (build cache) – pliki tymczasowe, generowane podczas budowy obrazów (przy użyciu narzędzia BuildKit, dostępnego od wersji Dockera 18.09).

Mogę się założyć, że już po tym prostym wyliczeniu masz ochotę na wyczyszczenie dysku z niepotrzebnych plików i odzyskanie drogocennych gigabajtów (przyp. red.: szczególnie, jeśli co miesiąc płacisz za te gigabajty).

Zużycie dysku przez kontenery

Za każdym razem, gdy tworzysz kontener na hoście, w katalogu /var/lib/docker tworzone są różne pliki i katalogi, wśród których warto wyróżnić następujące:

  • Katalog /var/lib/docker/containers/ID_kontenera – przy użyciu standardowego sterownika logowania, to tutaj zapisywane są dzienniki zdarzeń w formacie JSON. Zbyt szczegółowe logi, a także logi, które nikt nie czyta i nie przetwarza w żaden inny sposób, często prowadzą do przepełnienia dysków.
  • Katalog /var/lib/docker/overlay2 – zawiera warstwy odczytu-zapisu kontenerów (overlay2 to preferowany sterownik w większości dystrybucji Linuksa). Jeśli kontener zapisuje dane w swoim systemie plików, to właśnie w tym katalogu będą one przechowywane.

Wyobraźmy sobie system, na którym zainstalowany jest nieskalany Docker, który nigdy nie uruchamiał kontenerów ani nie budował obrazów. Jego raport o wykorzystaniu przestrzeni dyskowej 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 (images) zajmują 126 MB, to ten sam NGINX, który uruchomiliśmy w kontenerze;
  • kontenery (containers) 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

Z wyjścia wynika, że nie mamy jeszcze miejsca, które moglibyśmy zwolnić. Ponieważ 2 bajty to zupełnie niewielka wartość, wyobraźmy sobie, że nasz NGINX niespodziewanie napisał gdzieś 100 Megabajtów danych i stworzył wewnętrznie 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]

Ponownie sprawdźmy zużycie przestrzeni dyskowej na hoście. Zobaczymy, że kontener (containers) 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 umysł już zadaje sobie pytanie, gdzie znajduje się nasz plik test.img. Poszukajmy go:

$ 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 wdając się w szczegóły, można zauważyć, że plik test.img jest wygodnie umiejscowiony na poziomie odczytu-zapisu, zarządzanym przez sterownik overlay2. Jeśli jednak zatrzymamy nasz kontener, host zasugeruje nam, że to miejsce 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ć? Usuwając kontener, co spowoduje oczyszczenie odpowiedniej przestrzeni na poziomie odczytu-zapisu.

Za pomocą następującego polecenia możesz usunąć wszystkie zainstalowane kontenery za jednym razem i oczyścić swój dysk z wszystkich utworzonych przez nie plików na poziomie odczytu-zapisu:

$ docker container prune
OSTRZEŻENIE! To usunie wszystkie zatrzymane kontenery.
Czy na pewno chcesz kontynuować? [y/N] y
Usunięte kontenery:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2

Całkowita odzyskana przestrzeń: 104.9MB

Zatem uwolniliśmy 104,9 Megabajta, usuwając kontener. Ale ponieważ już nie używamy wcześniej pobranego obrazu, staje się on również kandydatem do usunięcia i uwolnienia naszych zasobów:

$ docker system df
TYPU           CAŁKOWITE  AKTYWNE    ROZMIAR    MOŻLIWE DO ODZYSKANIA
Obrazy        1          0          126M       126M (100%)
Kontenery     0          0          0B         0B
Lokalne Wolumeny 0      0          0B         0B
Cache Budowy  0          0          0B         0B

Uwaga: dopóki obraz jest używany przez chociaż jeden kontener, nie będziesz mógł zastosować tego triku.

Podkomenda prune, którą użyliśmy powyżej, działa tylko na zatrzymanych kontenerach. Jeśli chcemy usunąć nie tylko zatrzymane, ale także uruchomione kontenery, należy użyć jednego z tych poleceń:

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

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

Notatki na marginesie: jeżeli podczas uruchamiania kontenera używasz parametru —rm, to przy jego zatrzymaniu zostanie zwolniona cała przestrzeń dyskowa, którą zajmował.

Użycie dysku przez obrazy

Kilka lat temu rozmiar obrazu wynoszący kilka set megabajtów był całkowicie normalny: obraz Ubuntu ważył 600 Megabajtów, a obraz Microsoft .Net – kilka Gigabajtów. W tamtych zamierzchłych czasach pobranie jednego tylko obrazu mogło znacząco zaszkodzić twojej wolnej przestrzeni na dysku, nawet jeśli dzieliłeś poziomy między obrazami. Dziś – chwała wielkim – obrazy ważą znacznie mniej, ale nawet w tym przypadku można szybko zapełnić dostępne zasoby, jeśli nie podejmujesz pewnych środków ostrożności.

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

  • obraz pośredni, na podstawie którego zbudowane są inne obrazy – nie mogą być usunięte, jeśli używasz kontenerów opartych na tych "innych" obrazach.
  • obrazom wiszącym – to pośrednie obrazy, na które nie wskazuje żaden działający kontener – mogą być usunięte.
  • Za pomocą następującej komendy możesz sprawdzić, czy w twoim systemie znajdują się obrazy wiszące:

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

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

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

Możemy również użyć subkomendy prune:

$ docker image prune
OSTRZEŻENIE! To usunie wszystkie obrazy wiszące.
Czy na pewno chcesz kontynuować? [y/N] y
Usunięte obrazy:
usunięto: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
usunięto: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
usunięto: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
usunięto: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
usunięto: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f

Całkowita odzyskana przestrzeń: 12.9kB

Jeśli nagle chcemy usunąć wszystkie obrazy (a nie tylko wiszące) jednym poleceniem, możemy to zrobić tak:

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

Użycie dysku przez wolumeny

Wolumeny (volumes) są używane do przechowywania danych poza systemem plików kontenera. Na przykład, jeśli chcemy zachować wyniki działania jakiejś aplikacji, aby można je było wykorzystywać w inny sposób. Częstym przykładem są bazy danych.

Uruchommy kontener MongoDB, zamontujmy do niego zewnętrzny wolumen i przywróćmy z niego kopię zapasową bazy danych (mamy ją 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 maszynie gospodarza w katalogu /var/lib/docker/volumes. Ale dlaczego nie w poziomie odczytu-zapisu kontenera? Ponieważ w Dockerfile obrazu MongoDB katalog /data/db (w którym MongoDB domyślnie przechowuje swoje dane) został zdefiniowany jako wolumen (volume).

Docker Tips: Oczyść swoją maszynę z bałaganu

Notatki na marginesie: wiele obrazów, w wyniku działania których powinny być tworzone dane, używa wolumenów (volumes) do przechowywania tych danych.

Kiedy znudzeni MongoDB zatrzymamy (a może nawet usuniemy) kontener, wolumen nie zostanie usunięty. Będzie nadal zajmował naszą cenną przestrzeń dyskową, dopóki nie usuniemy go jawnie taką komendą:

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

Albo możemy użyć już znanej nam subkomendy prune:

$ docker volume prune
OSTRZEŻENIE! To spowoduje usunięcie wszystkich lokalnych wolumenów, które nie są używane przez co najmniej jeden kontener.
Czy na pewno chcesz kontynuować? [y/N] y
Usunięte wolumeny:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da

Łącznie odzyskana przestrzeń: 25,82GB
luc@saturn:~$

Wykorzystanie dysku dla pamięci podręcznej budowy obrazów

W Docker 18.09 proces tworzenia obrazów przeszedł kilka zmian dzięki narzędziu BuildKit. Dzięki temu narzędziu zwiększa się prędkość procesu, optymalizuje zarządzanie pamięcią i bezpieczeństwem. Tutaj nie będziemy omawiać szczegółowo wszystkich funkcji tego znakomitego narzędzia, skupimy się jedynie na tym, jak dotyka kwestii wykorzystywania przestrzeni dyskowej.

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

  • plik index.js uruchamia prosty serwer HTTP, który odpowiada tekstem na każde przyjęte zapytanie:
  • plik package.json określa zależności, z których używana jest tylko expressjs do uruchomienia serwera HTTP:

$ 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(), 'Otrzymano żądanie'));
});
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"
      }
    }

Dockerfile do budowy obrazu wygląda tak:

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

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

$ docker build -t app:1.0 .

Jeżeli sprawdzimy wykorzystanie przestrzeni dyskowej, zobaczymy, że miejsce zajmują tylko obraz bazowy (node:13-alpine) i końcowy obraz (app:1.0):

TYP           CAŁKOWITA   AKTYWNE     ROZMIAR     MOŻLIWE DO ODDANIA
Obrazy       2          0          109.3MB    109.3MB (100%)
Kontenery    0          0          0B         0B
Lokalne Wolumeny  0          0          0B         0B
Cache Budowy  0          0          0B         0B

Zbudujemy drugą wersję naszej aplikacji, już z wykorzystaniem BuildKit. W tym celu musimy jedynie ustawić zmienną DOCKER_BUILDKIT na wartość 1:

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

Jeśli teraz sprawdzimy użycie dysku, zobaczymy, że teraz angażuje się pamięć podręczna budowy (build-cache):

$ 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 go oczyścić, użyjemy następującego polecenia:

$ docker builder prune
WARNING! To usunie wszystkie wiszące pliki cache z budowy.
Czy na pewno chcesz kontynuować? [y/N] y
Usunięte obiekty cache:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi

Całkowita odzyskana przestrzeń: 8.949kB

Wyczyść wszystko!

Tak więc omówiliśmy oczyszczanie przestrzeni dyskowej zajmowanej przez kontenery, obrazy i wolumeny. W tym pomaga nam subkomenda prune. Można ją jednak zastosować również na poziomie systemu docker, a oczyści wszystko, co tylko możliwe:

$ docker system prune
WARNING! To usunie:
  - wszystkie zatrzymane kontenery
  - wszystkie sieci, które nie są używane przez przynajmniej jeden kontener
  - wszystkie wiszące obrazy
  - wszystkie wiszące pliki cache z budowy

Czy na pewno chcesz kontynuować? [y/N]

Jeśli z jakiegoś powodu oszczędzasz przestrzeń dyskową na maszynie z Dockerem, warto wprowadzić zwyczaj okresowego uruchamiania tego polecenia.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster