Consejos para Docker: limpie su máquina de basura

Consejos para Docker: limpie su máquina de basura

¡Hola, Habr! Presento a su atención la traducción del artículo. "Consejos para Docker: limpie su máquina local" автора Luc Juggery.

Hoy hablaremos sobre cómo Docker usa el espacio en disco de la máquina host y también descubriremos cómo liberar este espacio de los restos de imágenes y contenedores no utilizados.


Consejos para Docker: limpie su máquina de basura

Consumo total

Docker es algo genial, probablemente pocas personas lo duden hoy. Hace apenas unos años, este producto nos brindó una forma completamente nueva de construir, entregar y ejecutar cualquier entorno, lo que nos permitió ahorrar significativamente recursos de CPU y RAM. Además de esto (y para algunos esto será lo más importante) Docker nos ha permitido simplificar y unificar increíblemente la gestión del ciclo de vida de nuestros entornos de producción.

Sin embargo, todos estos placeres de la vida moderna tienen un precio. Cuando ejecutamos contenedores, descargamos o creamos nuestras propias imágenes e implementamos ecosistemas complejos, tenemos que pagar. Y pagamos, entre otras cosas, con espacio en disco.

Si nunca ha pensado en cuánto espacio ocupa realmente Docker en su máquina, es posible que se sorprenda desagradablemente con el resultado de este comando:

$ docker system df

Consejos para Docker: limpie su máquina de basura

Esto muestra el uso del disco de Docker en diferentes contextos:

  • imágenes: el tamaño total de las imágenes que se descargaron de los repositorios de imágenes y se crearon en su sistema;
  • contenedores: la cantidad total de espacio en disco utilizado por los contenedores en ejecución (es decir, el volumen total de capas de lectura y escritura de todos los contenedores);
  • volúmenes locales: el volumen de almacenamiento local montado en contenedores;
  • caché de compilación: archivos temporales generados por el proceso de creación de imágenes (utilizando la herramienta BuildKit, disponible a partir de la versión 18.09 de Docker).

Apuesto a que después de esta simple transferencia estarás ansioso por limpiar tu disco de basura y devolverle la vida a tus preciosos gigabytes (nota: especialmente si pagas el alquiler por estos gigabytes todos los meses).

Uso de disco por contenedores

Cada vez que crea un contenedor en la máquina host, se crean varios archivos y directorios en el directorio /var/lib/docker, entre los que cabe destacar los siguientes:

  • Directorio /var/lib/docker/containers/container_ID: 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 los registros que nadie lee ni procesa, a menudo provocan que los discos se llenen.
  • El directorio /var/lib/docker/overlay2 contiene las capas de lectura y escritura del contenedor (overlay2 es el controlador preferido en la mayoría de las distribuciones de Linux). Si el contenedor almacena datos en su sistema de archivos, entonces será en este directorio donde se ubicarán.

Imaginemos un sistema en el que está instalado un Docker impecable, que nunca ha estado involucrado en el lanzamiento de contenedores o la creación de imágenes. Su informe de uso de espacio en disco se verá así:

$ 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

Lanzamos algún contenedor, por ejemplo, NGINX:

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

¿Qué le sucede al disco?

  • las imágenes ocupan 126 MB, este es el mismo NGINX que lanzamos en el contenedor;
  • Los contenedores ocupan unos ridículos 2 bytes.

$ 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 juzgar por la conclusión, todavía no tenemos espacio que podamos liberar. Dado que 2 bytes es completamente frívolo, imaginemos que nuestro NGINX escribió inesperadamente alrededor de 100 megabytes de datos y creó un archivo test.img de exactamente este tamaño dentro de sí mismo.

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

Examinemos nuevamente el uso del espacio en disco en el host. Veremos que allí el contenedor (contenedores) ocupa 100 Megas.

$ 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

Creo que tu cerebro curioso 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.img

Sin entrar en detalles, podemos señalar que el archivo test.img está convenientemente ubicado en el nivel de lectura-escritura, controlado por el controlador overlay2. Si detenemos nuestro contenedor, el host nos dirá que este espacio, en principio, se puede liberar:

# 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 hacer esto? Eliminando el contenedor, lo que supondrá limpiar el espacio correspondiente a nivel de lectura-escritura.

Con el siguiente comando, puede eliminar todos los contenedores instalados de una sola vez y borrar de su disco todos los archivos de lectura y escritura creados por ellos:

$ 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

Entonces, liberamos 104,9 Megabytes eliminando el contenedor. Pero como ya no utilizamos la imagen previamente descargada, también se convierte en candidata a eliminar y liberar nuestros recursos:

$ 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: Mientras la imagen esté siendo utilizada por al menos un contenedor, no podrás utilizar este truco.

El subcomando podar que utilizamos anteriormente solo tiene efecto en los contenedores detenidos. Si queremos eliminar no solo los contenedores detenidos sino también los que están en ejecución, debemos usar 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 utiliza el parámetro -rm al iniciar un contenedor, cuando se detenga, se liberará todo el espacio en disco que ocupaba.

Usando imágenes de disco

Hace unos años, un tamaño de imagen de varios cientos de megabytes era completamente normal: una imagen de Ubuntu pesaba 600 megabytes y una imagen de Microsoft .Net pesaba varios gigabytes. En aquellos días difíciles, descargar solo una imagen podía afectar considerablemente el espacio libre en el disco, incluso si compartía niveles entre imágenes. Hoy en día -alabado sea el grande- las imágenes pesan mucho menos, pero aun así, puedes agotar rápidamente los recursos disponibles si no tomas algunas precauciones.

Existen varios tipos de imágenes que no son directamente visibles para el usuario final:

  • imágenes intermedias, a partir de las cuales se recopilan otras imágenes; no se pueden eliminar si utiliza contenedores basados ​​en estas "otras" imágenes;
  • Las imágenes colgantes son imágenes intermedias a las que ninguno de los contenedores en ejecución hace referencia; se pueden eliminar.
  • Con el siguiente comando puede comprobar si hay imágenes colgantes en su sistema:

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

Puedes eliminarlos de la siguiente manera:

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

También podemos usar el subcomando podar:

$ 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

Si de repente queremos eliminar todas las imágenes por completo (y no solo las que cuelgan) con un comando, entonces podemos hacer esto:

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

Uso del disco por volúmenes

Los volúmenes se utilizan para almacenar datos fuera del sistema de archivos del contenedor. Por ejemplo, si queremos guardar los resultados de una aplicación para poder utilizarlos de alguna otra forma. Un ejemplo común son las bases de datos.

Iniciemos un contenedor MongoDB, montemos un volumen externo al contenedor y restauremos una copia de seguridad de la base de datos (lo 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 
  --jsonArray

Los datos se ubicarán en la máquina host en el directorio /var/lib/docker/volumes. ¿Pero por qué no en el nivel de lectura y escritura del contenedor? Porque en el Dockerfile de la imagen de MongoDB, el directorio /data/db (donde MongoDB almacena sus datos de forma predeterminada) se define como un volumen.

Consejos para Docker: limpie su máquina de basura

Nota al margen: muchas imágenes que deben producir datos utilizan volúmenes para almacenar esos datos.

Cuando jugamos lo suficiente con MongoDB y detenemos (o tal vez incluso eliminamos) el contenedor, el volumen no se eliminará. Continuará ocupando nuestro valioso espacio en disco hasta que lo eliminemos explícitamente con un comando como este:

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

Bueno, o podemos usar el subcomando prune que ya nos resulta familiar:

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

Uso del disco para caché de creación de imágenes

En Docker 18.09, el proceso de creación de imágenes ha sufrido algunos cambios gracias a la herramienta BuildKit. Esto aumenta la velocidad del proceso y optimiza el almacenamiento de datos y la gestión de la seguridad. Aquí no consideraremos todos los detalles de esta maravillosa herramienta; sólo nos centraremos en cómo aborda los problemas del uso del espacio en disco.

Digamos que tenemos una aplicación Node.Js completamente simple:

  • El archivo index.js inicia un servidor HTTP simple que responde con una línea a cada solicitud recibida:
  • el archivo package.json define las dependencias, de las cuales solo se usa expressjs para ejecutar 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 crear la imagen tiene este aspecto:

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

Construyamos la imagen de la forma habitual, sin usar BuildKit:

$ docker build -t app:1.0 .

Si verificamos el uso del espacio en disco, podemos ver que solo la imagen base (nodo:13-alpine) y la imagen de destino (aplicación:1.0) están ocupando espacio:

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

Construyamos la segunda versión de nuestra aplicación, usando BuildKit. Para hacer esto, solo necesitamos establecer la variable DOCKER_BUILDKIT en 1:

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

Si ahora verificamos el uso del disco, veremos que el caché de compilación (buid-cache) ahora está involucrado allí:

$ 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

Para borrarlo, use el siguiente 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

¡Limpiar todo!

Entonces, analizamos la limpieza del espacio en disco ocupado por contenedores, imágenes y volúmenes. El subcomando prune nos ayuda con esto. Pero también se puede usar a nivel del sistema acoplable y limpiará todo lo que pueda:

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

Si por alguna razón está ahorrando espacio en disco en una máquina que ejecuta Docker, ejecutar periódicamente este comando debería convertirse en un hábito.

Fuente: habr.com

Añadir un comentario