# Controlador de almacenamiento ZFS


ZFS es un sistema de archivos de próxima generación que admite muchas tecnologías de almacenamiento avanzadas, como gestión de volúmenes, snapshots, sumas de comprobación (checksumming), compresión y deduplicación, replicación y más.

Fue creado por Sun Microsystems (ahora Oracle Corporation) y es de código abierto bajo la licencia CDDL. Debido a incompatibilidades de licencias entre la CDDL y la GPL, ZFS no se puede distribuir como parte de la línea principal del kernel Linux. Sin embargo, el proyecto ZFS On Linux (ZoL) proporciona un módulo de kernel externo (out-of-tree) y herramientas de espacio de usuario que se pueden instalar por separado.

El port de ZFS en Linux (ZoL) está activo y madurando. Sin embargo, en este momento no se recomienda utilizar el controlador de almacenamiento de Docker `zfs` para uso en producción a menos que tengas una experiencia sustancial con ZFS en Linux.

> [!NOTE]
>
> También existe una implementación FUSE de ZFS en la plataforma Linux. Esto no se recomienda. El controlador nativo de ZFS (ZoL) está más probado, ofrece mejor rendimiento y se utiliza de forma más amplia. El resto de este documento se refiere al port nativo ZoL.

## Requisitos previos

- ZFS requiere uno o más dispositivos de bloque dedicados, preferiblemente unidades de estado sólido (SSD).
- El directorio `/var/lib/docker/` debe estar montado en un sistema de archivos formateado con ZFS.
- Cambiar el controlador de almacenamiento hace que los contenedores que ya hayas creado dejen de estar accesibles en el sistema local. Usa `docker save` para guardar los contenedores y envía las imágenes existentes a Docker Hub o a un registro privado para que no tengas que volver a crearlas más tarde.

> [!NOTE]
>
> No es necesario utilizar `MountFlags=slave` porque `dockerd` y `containerd` están en espacios de nombres de montaje (mount namespaces) diferentes.

## Configurar Docker con el controlador de almacenamiento `zfs`

1.  Detén Docker.

2.  Copia el contenido de `/var/lib/docker/` a `/var/lib/docker.bk` y elimina el contenido de `/var/lib/docker/`.

    ```console
    $ sudo cp -au /var/lib/docker /var/lib/docker.bk

    $ sudo rm -rf /var/lib/docker/*
    ```

3.  Crea un nuevo `zpool` en tu dispositivo o dispositivos de bloque dedicados y móntalo en `/var/lib/docker/`. Asegúrate de haber especificado los dispositivos correctos porque esta es una operación destructiva. Este ejemplo añade dos dispositivos al pool.

    ```console
    $ sudo zpool create -f zpool-docker -m /var/lib/docker /dev/xvdf /dev/xvdg
    ```

    El comando crea el `zpool` y lo nombra `zpool-docker`. El nombre es únicamente para fines de visualización y puedes utilizar un nombre diferente. Comprueba que el pool se haya creado y montado correctamente usando `zfs list`.

    ```console
    $ sudo zfs list

    NAME           USED  AVAIL  REFER  MOUNTPOINT
    zpool-docker    55K  96.4G    19K  /var/lib/docker
    ```

4.  Configura Docker para usar `zfs`. Edita `/etc/docker/daemon.json` y establece `storage-driver` en `zfs`. Si el archivo estaba vacío antes, ahora debería verse así:

    ```json
    {
      "storage-driver": "zfs"
    }
    ```

    Guarda y cierra el archivo.

5.  Inicia Docker. Usa `docker info` para verificar que el controlador de almacenamiento sea `zfs`.

    ```console
    $ sudo docker info
      Containers: 0
       Running: 0
       Paused: 0
       Stopped: 0
      Images: 0
      Server Version: 17.03.1-ce
      Storage Driver: zfs
       Zpool: zpool-docker
       Zpool Health: ONLINE
       Parent Dataset: zpool-docker
       Space Used By Parent: 249856
       Space Available: 103498395648
       Parent Quota: no
       Compression: off
    <...>
    ```

## Gestionar `zfs`

### Incrementar la capacidad en un dispositivo en ejecución

Para aumentar el tamaño del `zpool`, necesitas añadir un dispositivo de bloques dedicado al host Docker y luego añadirlo al `zpool` mediante el comando `zpool add`:

```console
$ sudo zpool add zpool-docker /dev/xvdh
```

### Limitar la cuota de almacenamiento de escritura de un contenedor

Si deseas implementar una cuota por imagen o dataset, puedes establecer la opción de almacenamiento `size` para limitar la cantidad de espacio que un solo contenedor puede utilizar para su capa de escritura.

Edita `/etc/docker/daemon.json` y añade lo siguiente:

```json
{
  "storage-driver": "zfs",
  "storage-opts": ["size=256M"]
}
```

Consulta todas las opciones de almacenamiento para cada controlador en la [documentación de referencia del demonio](/reference/cli/dockerd/#daemon-storage-driver)

Guarda y cierra el archivo, y reinicia Docker.

## Cómo funciona el controlador de almacenamiento `zfs`

ZFS utiliza los siguientes objetos:

- **sistemas de archivos (filesystems)**: de aprovisionamiento dinámico, con espacio asignado desde el `zpool` bajo demanda.
- **snapshots**: copias de solo lectura de los sistemas de archivos en un momento dado, eficientes en términos de espacio.
- **clones**: copias de lectura-escritura de los snapshots. Se utilizan para almacenar las diferencias con respecto a la capa anterior.

El proceso de creación de un clon:

![Snapshots y clones de ZFS](/engine/storage/drivers/zfs-driver/images/zfs_clones.webp?w=450)


1.  Se crea un snapshot de solo lectura a partir del sistema de archivos.
2.  Se crea un clon de escritura a partir del snapshot. Este contiene cualquier diferencia con respecto a la capa padre.

Los sistemas de archivos, snapshots y clones asignan espacio del `zpool` subyacente.

### Capas de imágenes y contenedores en disco

El sistema de archivos unificado de cada contenedor en ejecución está montado en un punto de montaje en `/var/lib/docker/zfs/graph/`. Continúa leyendo para obtener una explicación de cómo se compone el sistema de archivos unificado.

### Capas de imágenes y uso compartido

La capa base de una imagen es un sistema de archivos ZFS. Cada capa hija es un clon de ZFS basado en un snapshot de ZFS de la capa inferior. Un contenedor es un clon de ZFS basado en un snapshot de ZFS de la capa superior de la imagen a partir de la cual se creó.

El diagrama de abajo muestra cómo se organiza esto con un contenedor en ejecución basado en una imagen de dos capas.

![ZFS pool para contenedor de Docker](/engine/storage/drivers/zfs-driver/images/zfs_zpool.webp?w=600)

Cuando inicias un contenedor, se producen los siguientes pasos en orden:

1.  La capa base de la imagen existe en el host de Docker como un sistema de archivos ZFS.

2.  Las capas de imagen adicionales son clones del dataset que aloja la capa de imagen inmediatamente inferior.

    En el diagrama, la "Capa 1" se añade tomando un snapshot de ZFS de la capa base y luego creando un clon a partir de ese snapshot. El clon es de escritura y consume espacio bajo demanda del zpool. El snapshot es de solo lectura, manteniendo la capa base como un objeto inmutable.

3.  Cuando se inicia el contenedor, se añade una capa de escritura encima de la imagen.

    En el diagrama, la capa de lectura-escritura del contenedor se crea haciendo un snapshot de la capa superior de la imagen (Capa 1) y creando un clon a partir de ese snapshot.

4.  A medida que el contenedor modifica el contenido de su capa de escritura, se asigna espacio para los bloques que cambian. Por defecto, estos bloques son de 128k.


## Cómo funcionan las lecturas y escrituras de contenedores con `zfs`

### Lectura de archivos

La capa de escritura de cada contenedor es un clon de ZFS que comparte todos sus datos con el dataset a partir del cual se creó (los snapshots de sus capas padres). Las operaciones de lectura son rápidas, incluso si los datos que se están leyendo pertenecen a una capa profunda.
Este diagrama ilustra cómo funciona el uso compartido de bloques:

![Uso compartido de bloques de ZFS](/engine/storage/drivers/zfs-driver/images/zpool_blocks.webp?w=450)


### Escritura de archivos

**Escribir un archivo nuevo**: el espacio se asigna bajo demanda a partir del `zpool` subyacente y los bloques se escriben directamente en la capa de escritura del contenedor.

**Modificar un archivo existente**: el espacio se asigna únicamente para los bloques modificados y dichos bloques se escriben en la capa de escritura del contenedor mediante una estrategia copy-on-write (CoW). Esto minimiza el tamaño de la capa e incrementa el rendimiento de escritura.

**Eliminar un archivo o directorio**:
  - Cuando eliminas un archivo o directorio que existe en una capa inferior, el controlador de ZFS oculta la existencia de dicho archivo o directorio en la capa de escritura del contenedor, aunque el archivo o directorio siga existiendo en las capas inferiores de solo lectura.
  - Si creas y luego eliminas un archivo o directorio dentro de la capa de escritura del contenedor, los bloques son recuperados por el `zpool`.


## ZFS y Docker rendimiento

Existen varios factores que influyen en el rendimiento de Docker al utilizar el controlador de almacenamiento `zfs`.

- **Memoria**: La memoria tiene un gran impacto en el rendimiento de ZFS. ZFS fue diseñado originalmente para servidores de nivel empresarial con una gran cantidad de memoria.

- **Características de ZFS**: ZFS incluye una característica de deduplicación. El uso de esta característica puede ahorrar espacio en disco, pero consume una gran cantidad de memoria. Se recomienda desactivar esta función para el `zpool` que estés utilizando con Docker, a menos que utilices SAN, NAS u otras tecnologías RAID de hardware.

- **Caché de ZFS**: ZFS almacena en caché los bloques de disco en una estructura de memoria llamada Adaptive Replacement Cache (ARC). La característica *Single Copy ARC* de ZFS permite que una sola copia de un bloque en caché sea compartida por varios clones. Con esta característica, varios contenedores en ejecución pueden compartir una sola copia de un bloque almacenado en caché. Esta característica hace que ZFS sea una buena opción para PaaS y otros casos de uso de alta densidad.

- **Fragmentación**: La fragmentación es un subproducto natural de los sistemas de archivos copy-on-write como ZFS. ZFS mitiga esto utilizando un tamaño de bloque pequeño de 128k. El registro de intención de ZFS (ZIL) y la coalescencia de escrituras (escrituras retrasadas) también ayudan a reducir la fragmentación. Puedes monitorear la fragmentación usando `zpool status`. Sin embargo, no hay forma de desfragmentar ZFS sin reformatear y restaurar el sistema de archivos.

- **Utilizar el controlador nativo de ZFS para Linux**: No se recomienda la implementación FUSE de ZFS debido a su bajo rendimiento.

### Buenas prácticas de rendimiento

- **Utilizar almacenamiento rápido**: Las unidades de estado sólido (SSD) proporcionan lecturas y escrituras más rápidas que los discos mecánicos.

- **Utilizar volúmenes para cargas de trabajo con uso intensivo de escritura**: Los volúmenes ofrecen el mejor y más predecible rendimiento para cargas de trabajo con uso intensivo de escritura. Esto se debe a que omiten el controlador de almacenamiento y no incurren en ninguna de las posibles sobrecargas introducidas por el aprovisionamiento dinámico y copy-on-write. Los volúmenes tienen otros beneficios, como permitirte compartir datos entre contenedores y persistir incluso cuando ningún contenedor en ejecución los está utilizando.

