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.
NoteTambié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 savepara 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.
NoteNo es necesario utilizar
MountFlags=slaveporquedockerdycontainerdestán en espacios de nombres de montaje (mount namespaces) diferentes.
Configurar Docker con el controlador de almacenamiento zfs
Detén Docker.
Copia el contenido de
/var/lib/docker/a/var/lib/docker.bky elimina el contenido de/var/lib/docker/.$ sudo cp -au /var/lib/docker /var/lib/docker.bk $ sudo rm -rf /var/lib/docker/*Crea un nuevo
zpoolen 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.$ sudo zpool create -f zpool-docker -m /var/lib/docker /dev/xvdf /dev/xvdgEl comando crea el
zpooly lo nombrazpool-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 usandozfs list.$ sudo zfs list NAME USED AVAIL REFER MOUNTPOINT zpool-docker 55K 96.4G 19K /var/lib/dockerConfigura Docker para usar
zfs. Edita/etc/docker/daemon.jsony establecestorage-driverenzfs. Si el archivo estaba vacío antes, ahora debería verse así:{ "storage-driver": "zfs" }Guarda y cierra el archivo.
Inicia Docker. Usa
docker infopara verificar que el controlador de almacenamiento seazfs.$ 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:
$ 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:
{
"storage-driver": "zfs",
"storage-opts": ["size=256M"]
}Consulta todas las opciones de almacenamiento para cada controlador en la documentación de referencia del demonio
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
zpoolbajo 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:

- Se crea un snapshot de solo lectura a partir del sistema de archivos.
- 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.

Cuando inicias un contenedor, se producen los siguientes pasos en orden:
La capa base de la imagen existe en el host de Docker como un sistema de archivos ZFS.
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.
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.
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:

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
zpoolque 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.