
¡Hola, Habr! Les presento la traducción del artículo autor .
Hoy hablaremos sobre cómo Docker utiliza el espacio en disco de la máquina anfitriona, así como sobre cómo liberar este espacio de los residuos de imágenes y contenedores no utilizados.

Uso total
Docker es increíble, probablemente hoy en día pocos lo dudan. Hace solo unos años, este producto nos proporcionó una forma completamente nueva de construir, entregar y ejecutar cualquier entorno, permitiendo un ahorro significativo en los recursos de CPU y RAM. Además de esto (y para algunos esto será incluso lo más importante), Docker nos permitió simplificar y unificar drásticamente la gestión del ciclo de vida de los entornos de trabajo utilizados.
Sin embargo, por todas estas maravillas de la vida moderna hay que pagar. Cuando ejecutamos contenedores, descargamos o creamos nuestras propias imágenes y desplegamos ecosistemas complejos, tenemos que pagar. Y pagamos, entre otras cosas, con espacio en disco.
Si nunca te has preguntado cuánto espacio ocupa realmente Docker en tu máquina, puedes llevarte una desagradable sorpresa al ejecutar este comando:
$ docker system df 
Aquí se muestra el uso del disco por parte de Docker en diferentes aspectos:
- imágenes (images) – tamaño total de las imágenes que se han descargado de repositorios de imágenes y se han construido en tu sistema;
- contenedores (containers) – volumen total del espacio en disco utilizado por los contenedores en ejecución (se refiere al volumen total de las capas de lectura-escritura de todos los contenedores);
- volúmenes locales (local volumes) – volumen de los almacenamientos locales montados en los contenedores;
- caché de construcción (build cache) – archivos temporales generados por el proceso de construcción de imágenes (al usar la herramienta BuildKit, disponible a partir de Docker versión 18.09).
Apostaría a que ya después de esta simple lista estás deseando limpiar el disco de basura y recuperar esos valiosos gigabytes (nota del traductor: especialmente si pagas mensualmente por esos gigabytes).
Uso de disco por contenedores
Cada vez que se crea un contenedor en la máquina anfitriona, se generan varios archivos y directorios en el directorio /var/lib/docker, entre los cuales cabe destacar los siguientes:
- El directorio \/var\/lib\/docker\/containers\/ID_contenedor – cuando se utiliza el controlador de registro estándar, aquí es donde se guardan los registros de eventos en formato JSON. Los registros demasiado detallados, así como aquellos que nadie lee ni procesa de otra manera, a menudo causan el desbordamiento de discos.
- El directorio \/var\/lib\/docker\/overlay2 – contiene las capas de lectura-escritura de los contenedores (overlay2 – es el controlador preferido en la mayoría de las distribuciones de Linux). Si un contenedor guarda datos en su sistema de archivos, es en este directorio donde se alojarán.
Imaginen un sistema con Docker completamente nuevo, que nunca ha participado en la ejecución de contenedores o en la creación de imágenes. Su informe sobre el uso del espacio en disco se verá así:
$ docker system df
TIPO TOTAL ACTIVO TAMAÑO RECUPERABLE
Imágenes 0 0 0B 0B
Contenedores 0 0 0B 0B
Volúmenes Locales 0 0 0B 0B
Caché de Construcción 0 0 0B 0BVamos a iniciar algún contenedor, por ejemplo, NGINX:
$ docker container run --name www -d -p 8000:80 nginx:1.16Qué sucede con el disco:
- las imágenes (images) ocupan 126 Mb, ese es el NGINX que hemos iniciado en el contenedor;
- los contenedores (containers) ocupan ridículos 2 bytes.
$ docker system df
TIPO TOTAL ACTIVO TAMAÑO RECUPERABLE
Imágenes 1 1 126M 0B (0%)
Contenedores 1 1 2B 0B (0%)
Volúmenes Locales 0 0 0B 0B
Caché de Construcción 0 0 0B 0BSegún la salida, aún no tenemos espacio que podamos liberar. Dado que 2 bytes son completamente insignificantes, imaginemos que nuestro NGINX de repente escribió 100 Megabytes de datos y creó dentro de sí mismo un archivo test.img de ese tamaño.
$ docker exec -ti www
dd if=\/dev\/zero of=test.img bs=1024 count=0 seek=$[1024*100]Volvemos a explorar el uso del espacio en disco en el host. Veremos que el contenedor (containers) ocupa 100 Megabytes allí.
$ docker system df
TIPO TOTAL ACTIVO TAMAÑO RECUPERABLE
Imágenes 1 1 126M 0B (0%)
Contenedores 1 1 104.9MB 0B (0%)
Volúmenes Locales 0 0 0B 0B
Caché de Construcción 0 0 0B 0BCreo que tu mente curiosa ya se está preguntando dónde se encuentra nuestro archivo test.img. Vamos a buscarlo:
$ 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.imgSin entrar en detalles, se puede señalar que el archivo test.img se ha ubicado convenientemente en el nivel de lectura-escritura, controlado por el controlador overlay2. Si detenemos nuestro contenedor, el host nos indicará que este espacio, en principio, puede liberarse:
# 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¿Cómo podemos hacerlo? Mediante la eliminación del contenedor, lo que resultará en la limpieza del espacio correspondiente en el nivel de lectura-escritura.
Con el siguiente comando, puedes eliminar todos los contenedores instalados de una sola vez y limpiar tu disco de todos los archivos generados por ellos en el nivel de lectura-escritura:
$ docker container prune
¡ATENCIÓN! Esto eliminará todos los contenedores detenidos.
¿Estás seguro de que deseas continuar? [y/N] y
Contenedores eliminados:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2
Espacio total recuperado: 104.9MBAsí que liberamos 104,9 megabytes al eliminar el contenedor. Pero dado que ya no usamos la imagen descargada anteriormente, también es candidata para su eliminación y para liberar nuestros recursos:
$ docker system df
TIPO TOTAL ACTIVO TAMAÑO RECUPERABLE
Imágenes 1 0 126M 126M (100%)
Contenedores 0 0 0B 0B
Volúmenes locales 0 0 0B 0B
Cache de construcción 0 0 0B 0BAtención: mientras la imagen sea utilizada por al menos un contenedor, no podrás usar este truco.
El subcomando prune que utilizamos anteriormente solo tiene efecto en los contenedores detenidos. Si queremos eliminar no solo los detenidos, sino también los contenedores en ejecución, debemos utilizar uno de estos comandos:
# Historical command
$ docker rm -f $(docker ps –aq)
# More recent command
$ docker container rm -f $(docker container ls -aq)Notas al margen: si al iniciar el contenedor usas el parámetro —rm, al detenerlo se liberará todo el espacio en disco que ocupaba.
Uso del disco por imágenes
Hace algunos años, un tamaño de imagen de varios cientos de megabytes era completamente normal: la imagen de Ubuntu pesaba 600 megabytes, y la imagen de Microsoft .Net varios gigabytes. En aquellos tiempos lejanos, descargar solo una imagen podía tener un gran impacto en tu espacio libre en disco, incluso si compartías capas entre imágenes. Hoy, gracias a los grandes avances, las imágenes pesan mucho menos, pero incluso así, se puede llenar rápidamente el espacio disponible si no se toman ciertas precauciones.
Hay varios tipos de imágenes que no son directamente visibles para el usuario final:
- Las imágenes intermedias, a partir de las cuales se construyen otras imágenes, no se pueden eliminar si estás utilizando contenedores basados en esas "otras" imágenes;
- Las imágenes colgantes son aquellas imágenes intermedias que no son referenciadas por ninguno de los contenedores en ejecución; pueden ser eliminadas.
- Con el siguiente comando puedes verificar si hay imágenes colgantes en tu sistema:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
none none 21e658fe5351 hace 12 minutos 71.3MBSe pueden eliminar de la siguiente manera:
$ docker image rm $(docker image ls -f dangling=true -q)También podemos usar el subcomando prune:
$ docker image prune
¡ADVERTENCIA! Esto eliminará todas las imágenes colgantes.
¿Estás seguro de que quieres continuar? [y/N] y
Imágenes eliminadas:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f
Espacio total recuperado: 12.9kBSi de repente queremos eliminar todas las imágenes (y no solo las colgantes) con un solo comando, podemos hacerlo de esta manera:
$ docker image rm $(docker image ls -q)Uso de disco con volúmenes
Los volúmenes se utilizan para almacenar datos fuera del sistema de archivos del contenedor. Por ejemplo, si queremos guardar los resultados del funcionamiento de alguna aplicación para utilizarlos de alguna otra forma. Un ejemplo común son las bases de datos.
Vamos a ejecutar un contenedor de MongoDB, montaremos un volumen externo al contenedor y restauraremos desde él una copia de seguridad de la base de datos (la tenemos disponible en el archivo 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
--jsonArrayLos datos estarán en la máquina host en el directorio /var/lib/docker/volumes. Pero, ¿por qué no en el nivel de lectura-escritura del contenedor? Porque en el Dockerfile de la imagen de MongoDB, el directorio /data/db (donde MongoDB almacena sus datos por defecto) está definido como un volumen.

Notas al margen: muchas imágenes, cuyo funcionamiento genera datos, utilizan volúmenes para conservar esos datos.
Cuando nos cansemos de MongoDB y detengamos (o incluso eliminemos) el contenedor, el volumen no será eliminado. Seguirá ocupando nuestro valioso espacio en disco hasta que lo eliminemos explícitamente con el siguiente comando:
$ docker volume rm $(docker volume ls -q)O podemos usar el ya conocido subcomando prune:
$ docker volume prune
ADVERTENCIA! Esto eliminará todos los volúmenes locales que no son utilizados por al menos un contenedor.
¿Estás seguro de que quieres continuar? [y/N] y
Volúmenes eliminados:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da
Espacio total reclamado: 25.82GB
luc@saturn:~$Uso del disco para la caché de construcción de imágenes
En Docker 18.09, el proceso de creación de imágenes ha experimentado algunos cambios gracias a la herramienta BuildKit. Con este recurso, se incrementa la velocidad del proceso, se optimiza la gestión del almacenamiento de datos y la seguridad. Aquí no abordaremos todos los detalles de esta maravillosa herramienta, nos centraremos solo en cómo afecta a la utilización del espacio en disco.
Supongamos que tenemos una aplicación Node.Js muy simple:
- el archivo index.js inicia un servidor HTTP simple, que responde a cada solicitud recibida con una línea:
- el archivo package.json define las dependencias, de las cuales solo se utiliza expressjs para iniciar el servidor 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"
}
}El Dockerfile para construir la imagen es el siguiente:
FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]Vamos a construir la imagen de la manera habitual, sin usar BuildKit:
$ docker build -t app:1.0 .Si verificamos el uso del espacio en disco, veremos que solo ocupan espacio la imagen base (node:13-alpine) y la imagen final (app:1.0):
TIPO TOTAL ACTIVO TAMAÑO RECUPERABLE
Imágenes 2 0 109.3MB 109.3MB (100%)
Contenedores 0 0 0B 0B
Volúmenes Locales 0 0 0B 0B
Caché de Construcción 0 0 0B 0BVamos a construir la segunda versión de nuestra aplicación, ahora utilizando BuildKit. Para ello, solo necesitamos establecer la variable DOCKER_BUILDKIT en 1:
$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .Si ahora comprobamos el uso del disco, veremos que ahora participa la caché de construcción (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.949kBPara limpiarlo, utilizaremos el siguiente comando:
$ docker builder prune
WARNING! Esto eliminará toda la caché de construcción que esté en desuso.
¿Está seguro de que desea continuar? [y/N] y
Objetos de caché de construcción eliminados:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi
Espacio total recuperado: 8.949kB¡Limpiar todo!
Así que hemos revisado la limpieza del espacio en disco ocupado por contenedores, imágenes y volúmenes. Para esto, nos ayuda el subcomando prune. Pero también se puede utilizar a nivel del sistema docker y limpiará todo lo que pueda:
$ docker system prune
WARNING! Esto eliminará:
- todos los contenedores detenidos
- todas las redes que no son utilizadas por al menos un contenedor
- todas las imágenes en desuso
- toda la caché de construcción en desuso
¿Está seguro de que desea continuar? [y/N]Si por alguna razón está ahorrando espacio en disco en una máquina con Docker, vale la pena acostumbrarse a ejecutar este comando periódicamente.
Fuente: habr.com
