# Controladores de almacenamiento


> [!NOTE]
> Docker Engine 29.0 y versiones posteriores utilizan el
> [almacén de imágenes de containerd](/engine/containerd/) de forma predeterminada para instalaciones nuevas.
> El almacén de imágenes de containerd utiliza snapshotters en lugar de los controladores de almacenamiento
> clásicos descritos en esta página. Si estás ejecutando una instalación nueva de
> Docker Engine 29.0 o posterior, o si has migrado al almacén de imágenes de containerd,
> esta página proporciona información de contexto sobre cómo funcionan las capas de imágenes, pero los
> detalles de la implementación difieren. Para obtener información sobre el almacén de imágenes de
> containerd, consulta [almacén de imágenes de containerd](/engine/containerd/).

Para utilizar los controladores de almacenamiento de manera eficaz, es importante saber cómo Docker construye y almacena las imágenes, y cómo los contenedores utilizan estas imágenes. Puedes usar esta información para tomar decisiones informadas sobre la mejor manera de persistir los datos de tus aplicaciones y evitar problemas de rendimiento en el camino.

## Controladores de almacenamiento frente a volúmenes de Docker

Docker utiliza controladores de almacenamiento para almacenar las capas de imágenes y para almacenar datos en la capa de escritura de un contenedor. La capa de escritura del contenedor no persiste después de que este se elimina, pero es adecuada para almacenar datos efímeros que se generan en tiempo de ejecución. Los controladores de almacenamiento están optimizados para la eficiencia de espacio, pero (según el controlador de almacenamiento) las velocidades de escritura son menores que el rendimiento del sistema de archivos nativo, especialmente en el caso de los controladores de almacenamiento que utilizan un sistema de archivos copy-on-write (copia en escritura). Las aplicaciones con uso intensivo de escritura, como el almacenamiento de bases de datos, se ven afectadas por una sobrecarga de rendimiento, especialmente si existen datos preexistentes en la capa de solo lectura.

Utiliza volúmenes de Docker para datos con uso intensivo de escritura, datos que deben persistir más allá de la vida útil del contenedor y datos que deben compartirse entre contenedores. Consulta la [sección de volúmenes](/engine/volumes/) para aprender a usar volúmenes para persistir datos y mejorar el rendimiento.

## Imágenes y capas

Una imagen de Docker se construye a partir de una serie de capas. Cada capa representa una instrucción en el Dockerfile de la imagen. Todas las capas, excepto la última, son de solo lectura. Considera el siguiente Dockerfile:

```dockerfile
# syntax=docker/dockerfile:1

FROM ubuntu:22.04
LABEL org.opencontainers.image.authors="org@example.com"
COPY . /app
RUN make /app
RUN rm -r $HOME/.cache
CMD python /app/app.py
```

Este Dockerfile contiene cuatro comandos. Los comandos que modifican el sistema de archivos crean una nueva capa. La instrucción `FROM` comienza creando una capa a partir de la imagen `ubuntu:22.04`. El comando `LABEL` solo modifica los metadatos de la imagen y no produce una nueva capa. El comando `COPY` añade algunos archivos desde el directorio actual de tu cliente Docker. El primer comando `RUN` compila tu aplicación usando el comando `make` y escribe el resultado en una nueva capa. El segundo comando `RUN` elimina un directorio de caché y escribe el resultado en una nueva capa. Finalmente, la instrucción `CMD` especifica qué comando ejecutar dentro del contenedor, lo cual solo modifica los metadatos de la imagen y no produce una capa de imagen.

Cada capa es solo un conjunto de diferencias con respecto a la capa anterior. Ten en cuenta que tanto _añadir_ como _eliminar_ archivos dará como resultado una nueva capa. En el ejemplo anterior, se elimina el directorio `$HOME/.cache`, pero seguirá estando disponible en la capa anterior y se sumará al tamaño total de la imagen. Consulta las secciones [Buenas prácticas para escribir Dockerfiles](/build/building/best-practices/) y [usar compilaciones multi-etapa](/build/building/multi-stage/) para aprender cómo optimizar tus Dockerfiles para obtener imágenes eficientes.

Las capas se apilan una encima de otra. Cuando creas un contenedor, añades una nueva capa de escritura encima de las capas subyacentes. Esta capa a menudo se denomina "capa del contenedor". Todos los cambios realizados en el contenedor en ejecución, como escribir nuevos archivos, modificar archivos existentes y eliminar archivos, se escriben en esta delgada capa de escritura del contenedor. El siguiente diagrama muestra un contenedor basado en una imagen `ubuntu:15.04`.

![Capas de un contenedor basado en la imagen de Ubuntu](/engine/storage/drivers/images/container-layers.webp?w=450&h=300)

Un controlador de almacenamiento gestiona los detalles sobre la forma en que estas capas interactúan entre sí. Hay diferentes controladores de almacenamiento disponibles, cada uno con ventajas y desventajas para distintas situaciones.

## Contenedores y capas

La principal diferencia entre un contenedor y una imagen es la capa superior de escritura. Todas las escrituras en el contenedor que añaden nuevos datos o modifican los existentes se almacenan en esta capa de escritura. Cuando el contenedor se elimina, la capa de escritura también se elimina. La imagen subyacente permanece inalterada.

Dado que cada contenedor tiene su propia capa de escritura y todos los cambios se almacenan en ella, varios contenedores pueden compartir el acceso a la misma imagen subyacente y, al mismo tiempo, tener su propio estado de datos. El siguiente diagrama muestra varios contenedores que comparten la misma imagen de Ubuntu 15.04.

![Contenedores que comparten la misma imagen](/engine/storage/drivers/images/sharing-layers.webp?w=600&h=300)

Docker utiliza controladores de almacenamiento para gestionar el contenido de las capas de la imagen y la capa de escritura del contenedor. Cada controlador de almacenamiento maneja la implementación de manera diferente, pero todos los controladores utilizan capas de imagen apilables y la estrategia copy-on-write (CoW).

> [!NOTE]
>
> Utiliza volúmenes de Docker si necesitas que varios contenedores compartan el acceso a
> los mismos datos exactos. Consulta la [sección de volúmenes](/engine/volumes/) para obtener
> información sobre los volúmenes.

## Tamaño del contenedor en el disco

Para ver el tamaño aproximado de un contenedor en ejecución, puedes usar el comando `docker ps -s`. Hay dos columnas diferentes relacionadas con el tamaño.

- `size`: la cantidad de datos (en disco) que se utiliza para la capa de escritura de cada contenedor.
- `virtual size`: la cantidad de datos utilizados para los datos de la imagen de solo lectura que utiliza el contenedor más el tamaño (`size`) de la capa de escritura del contenedor. Varios contenedores pueden compartir algunos o todos los datos de la imagen de solo lectura. Dos contenedores iniciados a partir de la misma imagen comparten el 100% de los datos de solo lectura, mientras que dos contenedores con diferentes imágenes que tienen capas en común comparten esas capas comunes. Por lo tanto, no debes sumar los tamaños virtuales directamente, ya que esto sobreestima el uso total de disco en una cantidad que podría ser significativa.

El espacio total en disco utilizado por todos los contenedores en ejecución es una combinación de los valores de `size` y `virtual size` de cada contenedor. Si varios contenedores se iniciaron desde la misma imagen exacta, el tamaño total en disco para estos contenedores sería la SUMA (del `size` de los contenedores) más el tamaño de una sola imagen (`virtual size` - `size`).

Esto tampoco incluye las siguientes formas adicionales en las que un contenedor puede ocupar espacio en el disco:

- El espacio en disco utilizado para los archivos de registro (logs) almacenados por el [controlador de registro (logging driver)](/engine/logging/). Esto puede ser considerable si tu contenedor genera una gran cantidad de datos de registro y no se ha configurado la rotación de registros.
- Volúmenes y montajes de tipo bind utilizados por el contenedor.
- Espacio en disco utilizado para los archivos de configuración del contenedor, que normalmente son pequeños.
- Memoria escrita en disco (si el intercambio o swap está habilitado).
- Puntos de control (checkpoints), si estás utilizando la característica experimental de guardar/restaurar puntos de control.

## La estrategia copy-on-write (CoW)

Copy-on-write es una estrategia para compartir y copiar archivos con la máxima eficiencia. Si un archivo o directorio existe en una capa inferior dentro de la imagen, y otra capa (incluida la capa de escritura) necesita acceso de lectura a él, utiliza el archivo existente directamente. La primera vez que otra capa necesita modificar el archivo (al construir la imagen o ejecutar el contenedor), el archivo se copia en esa capa y se modifica. Esto minimiza la E/S (I/O) y el tamaño de cada de las capas posteriores. Estas ventajas se explican con más detalle a continuación.

### Compartir capas ayuda a tener imágenes más pequeñas

Cuando usas `docker pull` para descargar una imagen desde un repositorio, o cuando creas un contenedor a partir de una imagen que aún no existe localmente, cada capa se descarga por separado y se almacena en el área de almacenamiento local de Docker, que normalmente es `/var/lib/docker/` en hosts Linux. Puedes ver cómo se descargan estas capas en este ejemplo:

```console
$ docker pull ubuntu:22.04
22.04: Pulling from library/ubuntu
f476d66f5408: Pull complete
8882c27f669e: Pull complete
d9af21273955: Pull complete
f5029279ec12: Pull complete
Digest: sha256:6120be6a2b7ce665d0cbddc3ce6eae60fe94637c6a66985312d1f02f63cc0bcd
Status: Downloaded newer image for ubuntu:22.04
docker.io/library/ubuntu:22.04
```

Cada de estas capas se almacena en su propio directorio dentro del área de almacenamiento local del host de Docker. Para examinar las capas en el sistema de archivos, lista el contenido de `/var/lib/docker/<storage-driver>`. Este ejemplo utiliza el controlador de almacenamiento `overlay2`:

```console
$ ls /var/lib/docker/overlay2
16802227a96c24dcbeab5b37821e2b67a9f921749cd9a2e386d5a6d5bc6fc6d3
377d73dbb466e0bc7c9ee23166771b35ebdbe02ef17753d79fd3571d4ce659d7
3f02d96212b03e3383160d31d7c6aeca750d2d8a1879965b89fe8146594c453d
ec1ec45792908e90484f7e629330666e7eee599f08729c93890a7205a6ba35f5
l
```

Los nombres de los directorios no se corresponden con los IDs de las capas.

Ahora imagina que tienes dos Dockerfiles diferentes. Utilizas el primero para crear una imagen llamada `acme/my-base-image:1.0`.

```dockerfile
# syntax=docker/dockerfile:1
FROM alpine
RUN apk add --no-cache bash
```

El segundo se basa en `acme/my-base-image:1.0`, pero tiene algunas capas adicionales:

```dockerfile
# syntax=docker/dockerfile:1
FROM acme/my-base-image:1.0
COPY . /app
RUN chmod +x /app/hello.sh
CMD /app/hello.sh
```

La segunda imagen contiene todas las capas de la primera imagen, además de las nuevas capas creadas por las instrucciones `COPY` y `RUN`, y una capa de contenedor de lectura-escritura. Docker ya tiene todas las capas de la primera imagen, por lo que no necesita descargarlas de nuevo. Las dos imágenes comparten las capas que tienen en común.

Si compilas imágenes a partir de los dos Dockerfiles, puedes usar los comandos `docker image ls` y `docker image history` para verificar que los IDs criptográficos de las capas compartidas sean los mismos.

1. Crea un nuevo directorio `cow-test/` y accede a él.

2. Dentro de `cow-test/`, crea un nuevo archivo llamado `hello.sh` con el siguiente contenido.

   ```bash
   #!/usr/bin/env bash
   echo "Hello world"
   ```

3. Copia el contenido del primer Dockerfile anterior en un nuevo archivo llamado `Dockerfile.base`.

4. Copia el contenido del segundo Dockerfile anterior en un nuevo archivo llamado `Dockerfile`.

5. Dentro del directorio `cow-test/`, compila la primera imagen. No olvides incluir el `.` final en el comando. Esto establece la ruta (`PATH`), que le indica a Docker dónde buscar los archivos que deben agregarse a la imagen.

   ```console
   $ docker build -t acme/my-base-image:1.0 -f Dockerfile.base .
   [+] Building 6.0s (11/11) FINISHED
   => [internal] load build definition from Dockerfile.base                                      0.4s
   => => transferring dockerfile: 116B                                                           0.0s
   => [internal] load .dockerignore                                                              0.3s
   => => transferring context: 2B                                                                0.0s
   => resolve image config for docker.io/docker/dockerfile:1                                     1.5s
   => [auth] docker/dockerfile:pull token for registry-1.docker.io                               0.0s
   => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:9e2c9eca7367393aecc68795c671... 0.0s
   => [internal] load .dockerignore                                                              0.0s
   => [internal] load build definition from Dockerfile.base                                      0.0s
   => [internal] load metadata for docker.io/library/alpine:latest                               0.0s
   => CACHED [1/2] FROM docker.io/library/alpine                                                 0.0s
   => [2/2] RUN apk add --no-cache bash                                                          3.1s
   => exporting to image                                                                         0.2s
   => => exporting layers                                                                        0.2s
   => => writing image sha256:da3cf8df55ee9777ddcd5afc40fffc3ead816bda99430bad2257de4459625eaa   0.0s
   => => naming to docker.io/acme/my-base-image:1.0                                              0.0s
   ```

6. Compila la segunda imagen.

   ```console
   $ docker build -t acme/my-final-image:1.0 -f Dockerfile .

   [+] Building 3.6s (12/12) FINISHED
   => [internal] load build definition from Dockerfile                                            0.1s
   => => transferring dockerfile: 156B                                                            0.0s
   => [internal] load .dockerignore                                                               0.1s
   => => transferring context: 2B                                                                 0.0s
   => resolve image config for docker.io/docker/dockerfile:1                                      0.5s
   => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:9e2c9eca7367393aecc68795c671...  0.0s
   => [internal] load .dockerignore                                                               0.0s
   => [internal] load build definition from Dockerfile                                            0.0s
   => [internal] load metadata for docker.io/acme/my-base-image:1.0                               0.0s
   => [internal] load build context                                                               0.2s
   => => transferring context: 340B                                                               0.0s
   => [1/3] FROM docker.io/acme/my-base-image:1.0                                                 0.2s
   => [2/3] COPY . /app                                                                           0.1s
   => [3/3] RUN chmod +x /app/hello.sh                                                            0.4s
   => exporting to image                                                                          0.1s
   => => exporting layers                                                                         0.1s
   => => writing image sha256:8bd85c42fa7ff6b33902ada7dcefaaae112bf5673873a089d73583b0074313dd    0.0s
   => => naming to docker.io/acme/my-final-image:1.0                                              0.0s
   ```

7. Comprueba los tamaños de las imágenes.

   ```console
   $ docker image ls

   REPOSITORY             TAG     IMAGE ID         CREATED               SIZE
   acme/my-final-image    1.0     8bd85c42fa7f     About a minute ago    7.75MB
   acme/my-base-image     1.0     da3cf8df55ee     2 minutes ago         7.75MB
   ```

8. Comprueba el historial de cada imagen.

   ```console
   $ docker image history acme/my-base-image:1.0

   IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
   da3cf8df55ee   5 minutes ago   RUN /bin/sh -c apk add --no-cache bash # bui…   2.15MB    buildkit.dockerfile.v0
   <missing>      7 weeks ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
   <missing>      7 weeks ago     /bin/sh -c #(nop) ADD file:f278386b0cef68136…   5.6MB
   ```

   Algunos pasos no tienen tamaño (`0B`) y son cambios que solo afectan a los metadatos, los cuales no producen una capa de imagen ni ocupan espacio, aparte de los metadatos en sí. La salida anterior muestra que esta imagen consta de 2 capas de imagen.

   ```console
   $ docker image history  acme/my-final-image:1.0

   IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
   8bd85c42fa7f   3 minutes ago   CMD ["/bin/sh" "-c" "/app/hello.sh"]            0B        buildkit.dockerfile.v0
   <missing>      3 minutes ago   RUN /bin/sh -c chmod +x /app/hello.sh # buil…   39B       buildkit.dockerfile.v0
   <missing>      3 minutes ago   COPY . /app # buildkit                          222B      buildkit.dockerfile.v0
   <missing>      4 minutes ago   RUN /bin/sh -c apk add --no-cache bash # bui…   2.15MB    buildkit.dockerfile.v0
   <missing>      7 weeks ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
   <missing>      7 weeks ago     /bin/sh -c #(nop) ADD file:f278386b0cef68136…   5.6MB
   ```

   Ten en cuenta que todos los pasos de la primera imagen también se incluyen en la imagen final. La imagen final incluye las dos capas de la primera imagen y las dos capas que se agregaron en la segunda imagen.

   Las líneas `<missing>` en la salida de `docker history` indican que esos pasos se construyeron en otro sistema y forman parte de la imagen `alpine` que se descargó de Docker Hub, o que se compilaron con BuildKit como constructor. Antes de BuildKit, el constructor "clásico" producía una nueva imagen "intermedia" para cada paso con fines de almacenamiento en caché, y la columna `IMAGE` mostraba el ID de esa imagen.
   
   BuildKit utiliza su propio mecanismo de caché y ya no requiere imágenes intermedias para este fin. Consulta [BuildKit](/build/buildkit/) para aprender más sobre otras mejoras introducidas en BuildKit.

9. Comprueba las capas de cada imagen

   Usa el comando `docker image inspect` para ver los IDs criptográficos de las capas en cada imagen:

   ```console
   $ docker image inspect --format "{{json .RootFS.Layers}}" acme/my-base-image:1.0
   [
     "sha256:72e830a4dff5f0d5225cdc0a320e85ab1ce06ea5673acfe8d83a7645cbd0e9cf",
     "sha256:07b4a9068b6af337e8b8f1f1dae3dd14185b2c0003a9a1f0a6fd2587495b204a"
   ]
   ```
   
   ```console
   $ docker image inspect --format "{{json .RootFS.Layers}}" acme/my-final-image:1.0
   [
     "sha256:72e830a4dff5f0d5225cdc0a320e85ab1ce06ea5673acfe8d83a7645cbd0e9cf",
     "sha256:07b4a9068b6af337e8b8f1f1dae3dd14185b2c0003a9a1f0a6fd2587495b204a",
     "sha256:cc644054967e516db4689b5282ee98e4bc4b11ea2255c9630309f559ab96562e",
     "sha256:e84fb818852626e89a09f5143dbc31fe7f0e0a6a24cd8d2eb68062b904337af4"
   ]
   ```

   Observa que las dos primeras capas son idénticas en ambas imágenes. La segunda imagen añade dos capas adicionales. Las capas de imagen compartidas se almacenan una sola vez en `/var/lib/docker/` y también se comparten al enviar (push) y descargar (pull) una imagen a un registro de imágenes. Por lo tanto, las capas de imágenes compartidas pueden reducir el ancho de banda de red y el espacio de almacenamiento.

   > [!TIP]
   >
   > Da formato a la salida de los comandos de Docker con la opción `--format`.
   > 
   > Los ejemplos anteriores utilizan el comando `docker image inspect` con la opción `--format`
   > para ver los IDs de las capas, formateados como un array JSON. La opción `--format`
   > en los comandos de Docker puede ser una característica potente que te permite
   > extraer y dar formato a información específica de la salida, sin necesidad de
   > herramientas adicionales como `awk` o `sed`. Para aprender más sobre cómo formatear
   > la salida de los comandos de Docker utilizando la bandera `--format`, consulta la
   > [sección de formateo de comandos y salida de registros](/engine/cli/formatting/).
   > También formateamos la salida JSON utilizando la [utilidad `jq`](https://stedolan.github.io/jq/)
   > para mejorar la legibilidad.

### La copia de archivos hace que los contenedores sean eficientes

Cuando inicias un contenedor, se añade una delgada capa de escritura del contenedor sobre las otras capas. Cualquier cambio que el contenedor realice en el sistema de archivos se almacena aquí. Los archivos que el contenedor no modifica no se copian a esta capa de escritura. Esto significa que la capa de escritura es lo más pequeña posible.

Cuando se modifica un archivo existente en un contenedor, el controlador de almacenamiento realiza una operación copy-on-write. Los pasos específicos involucrados dependen del controlador de almacenamiento concreto. Para el controlador `overlay2`, la operación copy-on-write sigue esta secuencia general:

* Busca el archivo a actualizar a través de las capas de la imagen. El proceso comienza en la capa más nueva y avanza hacia la capa base, una capa a la vez. Cuando se encuentran resultados, se añaden a una caché para acelerar las operaciones futuras.
* Realiza una operación `copy_up` en la primera copia del archivo que se encuentre, para copiar el archivo a la capa de escritura del contenedor.
* Cualquier modificación se realiza en esta copia del archivo, y el contenedor ya no puede ver la copia de solo lectura del archivo que existe en la capa inferior.

Btrfs, ZFS y otros controladores gestionan el copy-on-write de forma diferente. Puedes leer más sobre los métodos de estos controladores más adelante en sus descripciones detalladas.

Los contenedores que escriben muchos datos consumen más espacio que los que no lo hacen. Esto se debe a que la mayoría de las operaciones de escritura consumen nuevo espacio en la delgada capa superior de escritura del contenedor. Ten en cuenta que cambiar los metadatos de los archivos (por ejemplo, cambiar los permisos o la propiedad de un archivo) también puede dar lugar a una operación `copy_up`, duplicando por tanto el archivo en la capa de escritura.

> [!TIP]
>
> Utiliza volúmenes para aplicaciones con uso intensivo de escritura.
>
> No almacenes los datos en el contenedor para aplicaciones con uso intensivo de escritura.
> Se sabe que estas aplicaciones, por ejemplo, las bases de datos con un alto volumen de escrituras,
> presentan inconvenientes, especialmente cuando existen datos preexistentes en la capa de solo lectura.
> 
> En su lugar, utiliza volúmenes de Docker, que son independientes del contenedor en ejecución
> y están diseñados para ser eficientes en E/S. Además, los volúmenes se pueden compartir
> entre contenedores y no aumentan el tamaño de la capa de escritura del contenedor. Consulta
> la sección de [usar volúmenes](/engine/volumes/) para aprender sobre los volúmenes.

Una operación `copy_up` puede incurrir en una sobrecarga de rendimiento perceptible. Esta sobrecarga varía según el controlador de almacenamiento que se esté utilizando. Los archivos grandes, la gran cantidad de capas y los árboles de directorios profundos pueden hacer que el impacto sea más notorio. Esto se mitiga por el hecho de que cada operación `copy_up` solo ocurre la primera vez que se modifica un archivo determinado.

Para verificar el funcionamiento de copy-on-write, el siguiente procedimiento inicia 5 contenedores basados en la imagen `acme/my-final-image:1.0` que construimos anteriormente y examina cuánto espacio ocupan.

1. Desde una terminal en tu host Docker, ejecuta los siguientes comandos `docker run`. Las cadenas al final son los IDs de cada contenedor.

   ```console
   $ docker run -dit --name my_container_1 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_2 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_3 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_4 acme/my-final-image:1.0 bash \
     && docker run -dit --name my_container_5 acme/my-final-image:1.0 bash

   40ebdd7634162eb42bdb1ba76a395095527e9c0aa40348e6c325bd0aa289423c
   a5ff32e2b551168b9498870faf16c9cd0af820edf8a5c157f7b80da59d01a107
   3ed3c1a10430e09f253704116965b01ca920202d52f3bf381fbb833b8ae356bc
   939b3bf9e7ece24bcffec57d974c939da2bdcc6a5077b5459c897c1e2fa37a39
   cddae31c314fbab3f7eabeb9b26733838187abc9a2ed53f97bd5b04cd7984a5a
   ```

2. Ejecuta el comando `docker ps` con la opción `--size` para verificar que los 5 contenedores se están ejecutando y para ver el tamaño de cada contenedor.

   ```console
   $ docker ps --size --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Size}}"

   CONTAINER ID   IMAGE                     NAMES            SIZE
   cddae31c314f   acme/my-final-image:1.0   my_container_5   0B (virtual 7.75MB)
   939b3bf9e7ec   acme/my-final-image:1.0   my_container_4   0B (virtual 7.75MB)
   3ed3c1a10430   acme/my-final-image:1.0   my_container_3   0B (virtual 7.75MB)
   a5ff32e2b551   acme/my-final-image:1.0   my_container_2   0B (virtual 7.75MB)
   40ebdd763416   acme/my-final-image:1.0   my_container_1   0B (virtual 7.75MB)
   ```
   
   La salida anterior muestra que todos los contenedores comparten las capas de solo lectura de la imagen (7.75MB), pero no se escribieron datos en el sistema de archivos del contenedor, por lo que no se utiliza almacenamiento adicional para los contenedores.

   **Avanzado: almacenamiento de metadatos y registros utilizado para los contenedores**



> [!NOTE]
>
> Este paso requiere una máquina Linux y no funciona en Docker Desktop, ya
> que requiere acceso al almacenamiento de archivos del Demonio de Docker.

Aunque la salida de `docker ps` te proporciona información sobre el espacio en disco consumido por la capa de escritura de un contenedor, no incluye información sobre los metadatos y los archivos de registro (logs) almacenados para cada contenedor.

Se pueden obtener más detalles explorando la ubicación de almacenamiento del Demonio de Docker (por defecto `/var/lib/docker`).

```console
$ sudo du -sh /var/lib/docker/containers/*

36K  /var/lib/docker/containers/3ed3c1a10430e09f253704116965b01ca920202d52f3bf381fbb833b8ae356bc
36K  /var/lib/docker/containers/40ebdd7634162eb42bdb1ba76a395095527e9c0aa40348e6c325bd0aa289423c
36K  /var/lib/docker/containers/939b3bf9e7ece24bcffec57d974c939da2bdcc6a5077b5459c897c1e2fa37a39
36K  /var/lib/docker/containers/a5ff32e2b551168b9498870faf16c9cd0af820edf8a5c157f7b80da59d01a107
36K  /var/lib/docker/containers/cddae31c314fbab3f7eabeb9b26733838187abc9a2ed53f97bd5b04cd7984a5a
```

Cada uno de estos contenedores solo ocupa 36 KB de espacio en el sistema de archivos.




3. Almacenamiento por contenedor

   Para demostrar esto, ejecuta el siguiente comando para escribir la palabra 'hello' en un archivo en la capa de escritura del contenedor en los contenedores `my_container_1`, `my_container_2` y `my_container_3`:

   ```console
   $ for i in {1..3}; do docker exec my_container_$i sh -c 'printf hello > /out.txt'; done
   ```
   
   Al ejecutar de nuevo el comando `docker ps` se muestra que esos contenedores ahora consumen 5 bytes cada uno. Estos datos son únicos para cada contenedor y no se comparten. Las capas de solo lectura de los contenedores no se ven afectadas y siguen siendo compartidas por todos los contenedores.

   ```console
   $ docker ps --size --format "table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Size}}"

   CONTAINER ID   IMAGE                     NAMES            SIZE
   cddae31c314f   acme/my-final-image:1.0   my_container_5   0B (virtual 7.75MB)
   939b3bf9e7ec   acme/my-final-image:1.0   my_container_4   0B (virtual 7.75MB)
   3ed3c1a10430   acme/my-final-image:1.0   my_container_3   5B (virtual 7.75MB)
   a5ff32e2b551   acme/my-final-image:1.0   my_container_2   5B (virtual 7.75MB)
   40ebdd763416   acme/my-final-image:1.0   my_container_1   5B (virtual 7.75MB)
   ```

Los ejemplos anteriores ilustran cómo los sistemas de archivos copy-on-write ayudan a que los contenedores sean eficientes. El copy-on-write no solo ahorra espacio, sino que también reduce el tiempo de inicio del contenedor. Cuando creas un contenedor (o varios contenedores a partir de la misma imagen), Docker solo necesita crear la delgada capa de escritura del contenedor.

Si Docker tuviera que hacer una copia completa de la pila de imágenes subyacente cada vez que crea un nuevo contenedor, los tiempos de creación de contenedores y el espacio en disco utilizado aumentarían significativamente. Esto sería similar al funcionamiento de las máquinas virtuales, con uno o más discos virtuales por máquina virtual. El [almacenamiento `vfs`](/engine/storage/drivers/vfs-driver/) no proporciona un sistema de archivos CoW ni otras optimizaciones. Al usar este controlador de almacenamiento, se crea una copia completa de los datos de la imagen para cada contenedor.

## Información relacionada

* [Volúmenes](/engine/volumes/)
* [Seleccionar un controlador de almacenamiento](/engine/storage/drivers/select-storage-driver/)

