Suggerimenti per Docker: pulisci la tua macchina dalla spazzatura

Suggerimenti per Docker: pulisci la tua macchina dalla spazzatura

Ehi Habr! Presento alla vostra attenzione la traduzione dell'articolo "Suggerimenti per Docker: ripulire il computer locale" l'autore Luc Juggery.

Oggi parleremo di come Docker utilizza lo spazio su disco della macchina host e scopriremo anche come liberare questo spazio dai ritagli di immagini e contenitori inutilizzati.


Suggerimenti per Docker: pulisci la tua macchina dalla spazzatura

Consumo totale

Docker è una cosa interessante, probabilmente poche persone ne dubitano oggi. Solo pochi anni fa, questo prodotto ci ha offerto un modo completamente nuovo di costruire, fornire ed eseguire qualsiasi ambiente, permettendoci di risparmiare in modo significativo sulle risorse di CPU e RAM. Oltre a questo (e per alcuni questa sarà la cosa più importante) Docker ci ha permesso di semplificare e unificare incredibilmente la gestione del ciclo di vita dei nostri ambienti produttivi.

Tuttavia, tutte queste delizie della vita moderna hanno un prezzo. Quando eseguiamo container, scarichiamo o creiamo le nostre immagini e distribuiamo ecosistemi complessi, dobbiamo pagare. E paghiamo, tra le altre cose, con lo spazio su disco.

Se non hai mai pensato a quanto spazio occupa effettivamente Docker sul tuo computer, potresti rimanere spiacevolmente sorpreso dall'output di questo comando:

$ docker system df

Suggerimenti per Docker: pulisci la tua macchina dalla spazzatura

Questo mostra l'utilizzo del disco di Docker in diversi contesti:

  • immagini: la dimensione totale delle immagini scaricate dai repository di immagini e create sul tuo sistema;
  • contenitori: la quantità totale di spazio su disco utilizzato dai contenitori in esecuzione (ovvero il volume totale dei livelli di lettura-scrittura di tutti i contenitori);
  • volumi locali: il volume dello spazio di archiviazione locale montato sui contenitori;
  • build cache: file temporanei generati dal processo di creazione dell'immagine (utilizzando lo strumento BuildKit, disponibile a partire dalla versione Docker 18.09).

Scommetto che dopo questo semplice trasferimento non vedrai l'ora di ripulire il tuo disco dalla spazzatura e riportare in vita preziosi gigabyte (nota: soprattutto se paghi l'affitto per questi gigabyte ogni mese).

Utilizzo del disco da parte dei contenitori

Ogni volta che si crea un contenitore sulla macchina host, nella directory /var/lib/docker vengono creati diversi file e directory, tra cui vale la pena notare:

  • Directory /var/lib/docker/containers/container_ID: quando si utilizza il driver di registrazione standard, è qui che i registri eventi vengono salvati in formato JSON. Registri troppo dettagliati, nonché registri che nessuno legge o elabora in altro modo, spesso causano il riempimento dei dischi.
  • La directory /var/lib/docker/overlay2 contiene i livelli di lettura-scrittura del contenitore (overlay2 è il driver preferito nella maggior parte delle distribuzioni Linux). Se il contenitore memorizza i dati nel suo file system, sarà in questa directory che verrà inserito.

Immaginiamo un sistema su cui sia installato un Docker incontaminato, che non è mai stato coinvolto nel lancio di container o nella costruzione di immagini. Il rapporto sull'utilizzo dello spazio su disco sarà simile al seguente:

$ 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

Lanciamo qualche contenitore, ad esempio NGINX:

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

Cosa succede al disco:

  • le immagini occupano 126 MB, è lo stesso NGINX che abbiamo lanciato nel container;
  • i contenitori occupano ridicoli 2 byte.

$ 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

A giudicare dalla conclusione, non abbiamo ancora spazio da liberare. Poiché 2 byte sono del tutto frivoli, immaginiamo che il nostro NGINX abbia scritto inaspettatamente circa 100 megabyte di dati e abbia creato al suo interno un file test.img esattamente di queste dimensioni.

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

Esaminiamo nuovamente l'utilizzo dello spazio su disco sull'host. Vedremo che il contenitore (i contenitori) occupa lì 100 Megabyte.

$ 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

Penso che il tuo cervello curioso si stia già chiedendo dove si trova il nostro file test.img. Cerchiamolo:

$ 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

Senza entrare nei dettagli, possiamo notare che il file test.img è convenientemente posizionato al livello di lettura-scrittura, controllato dal driver overlay2. Se fermiamo il nostro contenitore, l'host ci dirà che questo spazio può, in linea di principio, essere liberato:

# 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

Come possiamo farlo? Eliminando il contenitore, ciò comporterà la cancellazione dello spazio corrispondente a livello di lettura-scrittura.

Con il comando seguente puoi rimuovere tutti i contenitori installati in un colpo solo e cancellare dal disco tutti i file di lettura e scrittura da essi creati:

$ 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

Quindi, abbiamo liberato 104,9 megabyte eliminando il contenitore. Ma poiché non utilizziamo più l'immagine scaricata in precedenza, questa diventa anche candidata all'eliminazione e alla liberazione delle nostre risorse:

$ 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

Nota: finché l'immagine è utilizzata da almeno un contenitore, non potrai utilizzare questo trucco.

Il sottocomando prune che abbiamo usato sopra ha effetto solo sui contenitori fermati. Se vogliamo eliminare non solo i contenitori interrotti ma anche quelli in esecuzione, dovremmo utilizzare uno di questi comandi:

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

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

Note a margine: se si utilizza il parametro -rm all'avvio di un contenitore, quando si arresta, tutto lo spazio su disco che occupa verrà liberato.

Utilizzo delle immagini del disco

Alcuni anni fa, una dimensione dell'immagine di diverse centinaia di megabyte era del tutto normale: un'immagine di Ubuntu pesava 600 megabyte e un'immagine di Microsoft .Net pesava diversi gigabyte. In quei giorni difficili, il download di una sola immagine poteva incidere notevolmente sullo spazio libero su disco, anche se si condividevano livelli tra immagini. Oggi - lode al grande - le immagini pesano molto meno, ma anche così, se non si prendono alcuni accorgimenti, si possono riempire velocemente le risorse disponibili.

Esistono diversi tipi di immagini che non sono direttamente visibili all'utente finale:

  • immagini intermedie, sulla base delle quali vengono raccolte altre immagini - non possono essere cancellate se si utilizzano contenitori basati su queste "altre" immagini;
  • le immagini penzolanti sono immagini intermedie a cui non fa riferimento nessuno dei contenitori in esecuzione: possono essere eliminate.
  • Con il seguente comando puoi verificare la presenza di immagini penzolanti sul tuo sistema:

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

Puoi rimuoverli nel seguente modo:

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

Possiamo anche usare il sottocomando 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

Se all'improvviso vogliamo eliminare tutte le immagini (e non solo quelle penzolanti) con un comando, allora possiamo farlo:

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

Utilizzo del disco per volumi

I volumi vengono utilizzati per archiviare dati all'esterno del file system del contenitore. Ad esempio, se vogliamo salvare i risultati di un'applicazione per utilizzarli in altro modo. Un esempio comune sono i database.

Lanciamo un contenitore MongoDB, montiamo un volume esterno al contenitore e ripristiniamo un backup del database da esso (lo abbiamo disponibile nel file 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

I dati si troveranno sul computer host nella directory /var/lib/docker/volumes. Ma perché non a livello di lettura/scrittura del contenitore? Perché nel Dockerfile dell'immagine MongoDB, la directory /data/db (dove MongoDB memorizza i suoi dati per impostazione predefinita) è definita come volume.

Suggerimenti per Docker: pulisci la tua macchina dalla spazzatura

Nota a margine: molte immagini che devono produrre dati utilizzano volumi per archiviare tali dati.

Quando giochiamo abbastanza con MongoDB e interrompiamo (o forse addirittura eliminiamo) il contenitore, il volume non verrà eliminato. Continuerà a occupare il nostro prezioso spazio su disco finché non lo elimineremo esplicitamente con un comando come questo:

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

Bene, oppure possiamo usare il sottocomando prune che ci è già familiare:

$ 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:~$

Utilizzo del disco per la cache di creazione delle immagini

In Docker 18.09 il processo di creazione delle immagini ha subito alcune modifiche grazie allo strumento BuildKit. Questa cosa aumenta la velocità del processo e ottimizza l'archiviazione dei dati e la gestione della sicurezza. Qui non prenderemo in considerazione tutti i dettagli di questo meraviglioso strumento; ci concentreremo solo su come affronta i problemi di utilizzo dello spazio su disco.

Diciamo di avere un'applicazione Node.Js completamente semplice:

  • il file index.js avvia un semplice server HTTP che risponde con una riga ad ogni richiesta ricevuta:
  • il file package.json definisce le dipendenze, di cui solo expressjs viene utilizzato per eseguire il server 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(), '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"
      }
    }

Il Dockerfile per creare l'immagine è simile al seguente:

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

Costruiamo l'immagine nel solito modo, senza utilizzare BuildKit:

$ docker build -t app:1.0 .

Se controlliamo l'utilizzo dello spazio su disco, possiamo vedere che solo l'immagine di base (node:13-alpine) e l'immagine di destinazione (app:1.0) occupano spazio:

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

Costruiamo la seconda versione della nostra applicazione, utilizzando BuildKit. Per fare ciò, dobbiamo solo impostare la variabile DOCKER_BUILDKIT su 1:

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

Se ora controlliamo l'utilizzo del disco, vedremo che la cache di build (buid-cache) è ora coinvolta lì:

$ 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

Per cancellarlo, utilizzare il seguente comando:

$ 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

Cancella tutto!

Quindi, abbiamo esaminato la pulizia dello spazio su disco occupato da contenitori, immagini e volumi. Il sottocomando prune ci aiuta in questo. Ma può essere utilizzato anche a livello di sistema docker e ripulirà tutto ciò che può:

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

Se per qualche motivo stai risparmiando spazio su disco su una macchina che esegue Docker, eseguire periodicamente questo comando dovrebbe diventare un'abitudine.

Fonte: habr.com

Aggiungi un commento