
Ciao, Habr! Ti presento la traduzione di un articolo autore .
Oggi parleremo di come Docker utilizza lo spazio su disco della macchina host e scopriremo come liberare questo spazio dai residui di immagini e contenitori non utilizzati.

Consumo complessivo
Docker è una cosa fantastica, e oggi credo che pochi ne dubitino. Solo pochi anni fa, questo prodotto ci ha fornito un modo completamente nuovo per costruire, distribuire e avviare qualsiasi ambiente, consentendo un notevole risparmio di risorse della CPU e della memoria. Inoltre (e per alcuni sarà persino la cosa più importante), Docker ci ha permesso di semplificare e unificare incredibilmente la gestione del ciclo di vita degli ambienti di lavoro utilizzati.
Tuttavia, per tutte queste meraviglie della vita moderna bisogna pagare un prezzo. Quando avviamo contenitori, scarichiamo o creiamo le nostre immagini, distribuiamo ecosistemi complessi, dobbiamo sostenere un costo. E paghiamo, tra le altre cose, con lo spazio su disco.
Se non ti sei mai chiesto quanto spazio sia realmente occupato sulla tua macchina da Docker, potresti rimanere sgradevolmente sorpreso dal risultato di questo comando:
$ docker system df 
Qui viene mostrato l'utilizzo del disco da parte di Docker in vari contesti:
- immagini (images) – dimensione totale delle immagini scaricate dai registri delle immagini e costruite nel tuo sistema;
- container (containers) – spazio su disco totale utilizzato dai container attivi (si intende l'aggregato degli strati di lettura e scrittura di tutti i container);
- volumi locali (local volumes) – spazio degli archivi locali montati sui container;
- cache di build (build cache) – file temporanei generati dal processo di creazione delle immagini (utilizzando lo strumento BuildKit, disponibile a partire dalla versione 18.09 di Docker).
Scommetto che già dopo questo semplice elenco hai voglia di liberare il disco da spazzatura e recuperare preziosi gigabyte (nota: soprattutto se per questi gigabyte paghi un affitto mensile).
Utilizzo del disco da parte dei container
Ogni volta che viene creato un container sulla macchina host, vengono creati diversi file e directory nella cartella /var/lib/docker, tra cui i seguenti:
- La directory /var/lib/docker/containers/ID_contenitore – utilizzando il driver di logging standard, i registri degli eventi vengono salvati qui in formato JSON. Registri troppo dettagliati e quelli che non vengono letti o elaborati in altro modo diventano spesso causa di saturazione dei dischi.
- La directory /var/lib/docker/overlay2 – contiene i layer in lettura-scrittura dei contenitori (overlay2 è il driver preferito nella maggior parte delle distribuzioni Linux). Se un contenitore salva dati nel proprio file system, saranno collocati proprio in questa directory.
Immaginiamo un sistema con Docker vergine, mai utilizzato per l'avvio di contenitori o la costruzione di immagini. Il suo report sull'utilizzo dello spazio su disco apparirebbe così:
$ docker system df
TIPO TOTALE ATTIVO DIMENSIONE RIUTILIZZABILE
Immagini 0 0 0B 0B
Contenitori 0 0 0B 0B
Volumes Locali 0 0 0B 0B
Cache di Build 0 0 0B 0BAvviamo un contenitore, per esempio, NGINX:
$ docker container run --name www -d -p 8000:80 nginx:1.16Cosa succede con il disco:
- le immagini (images) occupano 126 MB, è proprio quell'NGINX che abbiamo avviato nel contenitore;
- I contenitori (containers) occupano solo 2 byte.
$ docker system df
TIPO TOTALE ATTIVO DIMENSIONE RECUPERABILE
Immagini 1 1 126M 0B (0%)
Contenitori 1 1 2B 0B (0%)
Volumi Locali 0 0 0B 0B
Cache di Build 0 0 0B 0BA giudicare dall'output, non abbiamo ancora spazio che possiamo liberare. Poiché 2 byte sono veramente irrisori, immaginiamo che il nostro NGINX abbia inaspettatamente scritto 100 Megabyte di dati e creato al suo interno un file test.img di tale dimensione.
$ docker exec -ti www
dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]Esploriamo nuovamente l'utilizzo dello spazio su disco nell'host. Vedremo che il contenitore (containers) occupa 100 Megabyte.
$ docker system df
TIPO TOTALE ATTIVO DIMENSIONE RECUPERABILE
Immagini 1 1 126M 0B (0%)
Contenitori 1 1 104.9MB 0B (0%)
Volumi Locali 0 0 0B 0B
Cache di Build 0 0 0B 0BPenso che la tua mente curiosa si stia già chiedendo dove si trovi il nostro file test.img. Cerchiamo di trovarlo:
$ 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.imgSenza entrare nei dettagli, possiamo notare che il file test.img si è posizionato comodamente a livello di lettura-scrittura, gestito dal driver overlay2. Se fermiamo il nostro container, l'host ci avviserà che questo spazio può 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 0BCome possiamo fare questo? Eliminando il container, il che comporterà la pulizia dello spazio corrispondente a livello di lettura-scrittura.
Con il seguente comando puoi eliminare tutti i container installati in un colpo solo e ripulire il tuo disco da tutti i file che hanno creato a livello di lettura-scrittura:
$ docker container prune
WARNING! Questo rimuoverà tutti i container fermati.
Sei sicuro di voler continuare? [y/N] y
Container rimossi:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2
Spazio totale recuperato: 104,9MBQuindi, abbiamo liberato 104,9 Megabyte eliminando il container. Ma dal momento che non utilizziamo più l'immagine scaricata in precedenza, anche questa diventa un candidato per la rimozione e liberazione delle nostre risorse:
$ docker system df
TIPO TOTALE ATTIVO DIMENSIONE RECUPERABILE
Immagini 1 0 126M 126M (100%)
Container 0 0 0B 0B
Volume Locali 0 0 0B 0B
Cache di Build 0 0 0B 0BAttenzione: finché l'immagine è utilizzata almeno da un contenitore, non sarà possibile utilizzare questo trucco.
Il sottocomando prune che abbiamo utilizzato sopra ha effetto solo sui contenitori fermi. Se vogliamo eliminare non solo i contenitori fermi, ma anche quelli in esecuzione, dobbiamo usare uno di questi comandi:
# Historical command
$ docker rm -f $(docker ps –aq)
# More recent command
$ docker container rm -f $(docker container ls -aq)Nota a margine: se si utilizza l'opzione —rm durante l'avvio del contenitore, al suo arresto verrà liberato tutto lo spazio su disco che occupava.
Utilizzo del disco da parte delle immagini
Alcuni anni fa, una dimensione dell'immagine di diverse centinaia di megabyte era del tutto normale: l'immagine di Ubuntu pesava 600 megabyte, mentre quella di Microsoft .Net pesava diversi gigabyte. In quei tempi lontani, il download di un'unica immagine poteva causare un notevole danno allo spazio libero sul disco, anche se si condividevano i livelli tra le immagini. Oggi, grazie ai grandi miglioramenti, le immagini pesano molto meno, ma anche in questo caso è possibile esaurire rapidamente le risorse disponibili se non si adottano alcune misure di precauzione.
Esistono diversi tipi di immagini che non sono direttamente visibili all'utente finale:
- immagini intermedie, su cui si basano altre immagini – non possono essere eliminate se stai utilizzando contenitori basati su queste «altre» immagini;
- immagini dangling – sono immagini intermedie che non sono referenziate da nessun contenitore in esecuzione – possono essere eliminate.
- Puoi controllare la presenza di immagini dangling nel tuo sistema utilizzando il seguente comando:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
none none 21e658fe5351 12 minutes ago 71.3MBPuoi eliminarle in questo modo:
$ docker image rm $(docker image ls -f dangling=true -q)Possiamo anche utilizzare il subtipo prune:
$ docker image prune
WARNING! Questo rimuoverà tutte le immagini dangling.
Sei sicuro di voler continuare? [y/N] y
Immagini eliminate:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f
Spazio totale recuperato: 12.9kBSe volessimo eliminare tutte le immagini (e non solo quelle dangling) con un solo comando, possiamo farlo così:
$ docker image rm $(docker image ls -q)Utilizzo del disco con volumi
I volumi vengono utilizzati per memorizzare i dati al di fuori del file system del contenitore. Ad esempio, se vogliamo salvare i risultati del lavoro di un'applicazione per utilizzarli in seguito. Un esempio comune sono i database.
Avviamo un contenitore MongoDB, montiamo un volume esterno rispetto al contenitore e ripristiniamo un backup del database (che abbiamo a disposizione 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
--jsonArrayI dati si troveranno sulla macchina 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 propri dati per default) è definita come volume.

Note a margine: molte immagini, la cui esecuzione deve generare dati, utilizzano volumi per salvare proprio questi dati.
Quando avremo finito di utilizzare MongoDB e fermeremo (o forse addirittura elimineremo) il contenitore, il volume non verrà eliminato. Continuerà a occupare il nostro prezioso spazio su disco finché non lo rimuoveremo esplicitamente con questo comando:
$ docker volume rm $(docker volume ls -q)Oppure possiamo usare il comando prune che già conosciamo:
$ docker volume prune
ATTENZIONE! Questo rimuoverà tutti i volumi locali non utilizzati da almeno un container.
Sei sicuro di voler continuare? [y/N] y
Volumi eliminati:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da
Spazio totale recuperato: 25.82GB
luc@saturn:~$Utilizzo del disco per la cache della creazione delle immagini
In Docker 18.09, il processo di creazione delle immagini ha subito alcune modifiche grazie allo strumento BuildKit. Con questo strumento aumenta la velocità del processo e si ottimizza la gestione dello spazio di archiviazione e della sicurezza. Qui non tratteremo tutti i dettagli di questo straordinario strumento, ma ci concentreremo solo su come influisce sull'utilizzo dello spazio su disco.
Supponiamo di avere un'applicazione Node.Js molto semplice:
- il file index.js avvia un semplice server HTTP che risponde con una stringa a ogni richiesta ricevuta:
- il file package.json definisce le dipendenze, da cui viene utilizzato solo expressjs per avviare 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(), 'Richiesta ricevuta'));
});
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 la costruzione dell'immagine appare così:
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 modo tradizionale, senza utilizzare BuildKit:
$ docker build -t app:1.0 .Se controlliamo l'uso dello spazio su disco, vediamo che spazio è occupato solo dall'immagine di base (node:13-alpine) e dall'immagine finale (app:1.0):
TIPO TOTALE ATTIVO DIMENSIONE RICICLABILE
Immagini 2 0 109.3MB 109.3MB (100%)
Contenitori 0 0 0B 0B
Volume Locale 0 0 0B 0B
Cache di Costruzione 0 0 0B 0BCostruiamo la seconda versione della nostra applicazione, già utilizzando BuildKit. Per fare ciò, dobbiamo semplicemente impostare la variabile DOCKER_BUILDKIT su 1:
$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .Se ora controlliamo l'uso del disco, vedremo che ora è presente la cache di costruzione (build-cache):
$ docker system df
TIPO TOTALE ATTIVO DIMENSIONE RECUPERABILE
Immagini 2 0 109.3MB 109.3MB (100%)
Contenitori 0 0 0B 0B
Volume Locali 0 0 0B 0B
Cache di Build 11 0 8.949kB 8.949kBPer pulirlo, utilizziamo il seguente comando:
$ docker builder prune
ATTENZIONE! Questo rimuoverà tutte le cache di build orfane.
Sei sicuro di voler continuare? [y/N] y
Oggetti della cache di build eliminati:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi
Spazio totale recuperato: 8.949kBPulisci tutto!
Quindi, abbiamo esaminato la pulizia dello spazio su disco occupato da contenitori, immagini e volumi. In questo ci aiuta il sotto-comando prune. Ma può essere utilizzato anche a livello di sistema docker e rimuoverà tutto ciò che può:
$ docker system prune
ATTENZIONE! Questo rimuoverà:
- tutti i contenitori fermati
- tutte le reti non utilizzate da almeno un contenitore
- tutte le immagini orfane
- tutte le cache di build orfane
Sei sicuro di voler continuare? [y/N]Se per qualche motivo stai risparmiando spazio su disco su una macchina con Docker, considera di abituarti a eseguire periodicamente questo comando.
Fonte: habr.com
