Introducción a ZFS

Hace un tiempo, publiqué un artículo sobre Nexenta, que aprovecha el ZFS de OpenSolaris para poder hacer un dist-upgrade y retornar a una versión anterior si algo sale mal. Hoy estaba viendo la entrada que escribió Marvin sobre Time Slider, el TimeMachine de OpenSolaris. A partir de esto me dieron ganas de averiguar un poco más sobre ZFS, me puse a buscar un poco en Internet y escribí esta pequeña introducción a ZFS que dejo a continuación.

Sun Solaris 10

Sun Solaris 10

Introducción

ZFS es un filesystem desarrollado por Sun Microsystems (por un equipo coordinado por Jeff Bonwick), implementado en Solaris y OpenSolaris desde 2005. Un filesystem (o sistema de archivos) es la parte del sistema operativo que se ocupa de administrar el almacenamiento de información en distintos tipos de unidades (generalmente discos rígidos). En primer lugar, todos los filesystems ofrecen métodos para crear, borrar, mover, copiar y renombrar archivos y directorios. A eso se agregan otras características muy importantes como la gestión de permisos, enlaces (hard links y symbolic links), mecanismos para evitar la fragmentación, mecanismos para asegurar la integridad de los datos (como el journaling, utilizado por ext3, ext4, NTFS, ReiserFS, entre otros), y métodos de gestión de RAIDs entre otras cosas.

Como indica la gente de Sun, ZFS es una nueva aproximación a la organización de la información, orientada a evitar conceptos obsoletos y reducir la complejidad de código, produciendo un sistema de archivos sencillo de administrar, con énfasis en la integridad de los datos y la escalabilidad.

Storage pools

Una de las características novedosas de ZFS es que, a diferencia de los filesystems tradicionales, que se ubican en un único dispositivo y, por lo tanto, requieren de un administrador de volúmenes cuando se quiere trabajar con más de un dispositivo, éste sistema se apoya en virtual storage pools (espacios de almacenamiento virtual) llamados “zpools”. Éstos están formados por virtual devices (dispositivos virtuales), “vdevs”, que a su vez se componen de block devices (dispositivos de bloques): archivos, particiones o discos enteros. De esta forma, se pueden configurar RAIDs de distinto tipo. Además de los tradicionales RAID-0 (dividido o data stripping) y RAID-1 (espejo o data mirroring), ZFS ofrece una variante del RAID-5 (dividido con paridad distribuida) llamada RAID-Z que evita un problema conocido llamado “agujero de escritura” (corrupción de las divisiones por perdida de energía entre la actualización de la información y las partes).

Capacidad y escalabilidad

En cuanto a la capacidad de almacenamiento y su escalabilidad, ZFS fue diseñado para tener límites que nunca fueran alcanzados (el tipo de proposición que sabemos que a la ciencia le encanta hacer para poder romper algunos años más tarde). Algunos límites teóricos de ZFS (tomados de Wikipedia) son:

  • 248 — Número de snapshots en cualquier sistema de ficheros (2 × 1014)
  • 248 — Número de ficheros en un sistema de ficheros (2 × 1014)
  • 16 exabytes — Tamaño máximo de un sistema de ficheros
  • 16 exabytes — Tamaño máximo de un fichero
  • 16 exabytes — Tamaño máximo de cualquier atributo
  • 3 × 1023 petabytes — Tamaño máximo de un zpool
  • 256 — Número de atributos de un fichero (realmente limitado a 248 que es el número de ficheros que puede contener un sistema de ficheros ZFS)
  • 256 — Número de ficheros en un directorio (realmente limitado a 248 que es el número de ficheros que puede contener un sistema de ficheros ZFS)
  • 264 — Número de dispositivos en cualquier zpool
  • 264 — Número de zpools en un sistema
  • 264 — Número de sistemas de ficheros en un zpool

Copy-on-write

OpenSolaris

OpenSolaris

Otra característica muy interesante que agrega ZFS es un modelo transaccional llamado copy-on-write (algo así como “copia al escribir”). Este modelo, que se aplica a todas las transacciones, implica que todos los punteros de bloque tienen un checksum de 256 bits que se verifica cada vez que se accede a él. Además, cada los bloques que contienen información activa no son nunca sobreescritos, sino que la información modificada se ubica en otros bloques nuevos, y estos últimos son agrupados en grupos transaccionales para reducir el overhead que esto podría producir. Esto permite una preservación casi perfecta de la información, que hace que no sea necesario ejecutar fsck sobre filesystems ZFS. Además, en configuraciones de discos donde la información es replicada (mirroring), la información es self-healing (es decir que los bloques corruptos se reparan automáticamente en función de la información previamente almacenada).

Snapshots

Al utilizar el modelo de copy-on-write, se preservan los bloques con información antigua, con lo cual es posible crear snapshots (imágenes o “instantáneas”) de la información, las cuales se crean rápidamente y en una forma que es eficiente desde el punto de vista del espacio. Éstas permiten restaurar el sistema con los datos existentes en determinada fecha (lo que hace Time Slider). También se pueden crear snapshots escribibles llamadas clones, lo cual resulta en un par de sistemas de archivos independientes que comparten algunos bloques.

Administración de cache

ZFS utiliza ARC (Adaptive Replacement Cache), un algoritmo que resulta más eficiente que el tradicional sistema de memoria virtual page cache que implementaba Solaris.

Gestión de I/O

ZFS implementa un motor de I/O (Input/Output o Entrada/Salida) segmentado (en inglés es un pipelined I/O engine, basado en el concepto de las CPU pipelines). Esto le brinda una serie de características (scoreboarding, priorities, deadline scheduling, out-of-order issue y I/O aggregation), que hacen que ZFS soporte tranquilamente cargas de entrada y salida que otros filesystems no pueden manejar.

Limitaciones

Existen una serie de limitaciones que están mejor explicadas en la entrada en inglés de la wikipedia. Reproduzco algunas aquí.

  • ZFS no soporta la definición de quotas por grupo o por usuario. En cambio, como los filesystems ZFS son muy livianos, uno crea filesystems para cada usuario con sus propias limitaciones de espacio. Sin embargo, no hay una solución para cuando un mismo filesystem debe ser compartido por varios usuarios con quotas distintas.
  • Por el momento, no es posible reducir la cantidad de vdevs en un zpool, ni reducir la capacidad de un zpool.
  • No se puede agregar un disco a un vdev de un RAID-Z o de un RAID-Z2, pero sí se puede agregar otro vdev al mismo zpool.
  • No se pueden mezclar vdevs de distintos tipos en un mismo pool.
  • ZFS no es un sistema nativo de cluster, distribuido, ni un sistema paralelo de archivos y no puede proveer acceso concurrente para múltiples hosts porque ZFS es un filesystem local.

Palabras finales

Sin dudas, ZFS es un filesystem muy interesante, con características muy innovadoras. Personalmente tengo cierta reticencia a aceptar las contribuciones de software de las grandes empresas. Lejos de mí está casarme con Sun, que pone sus manos en muchos proyectos muy distintos y no en todos con buenos resultados. Sin embargo, hay algunas cosas que debo reconocerle. Así como me pareció muy interesante la implementación de SMF, este aporte de ZFS, al partir de una reconsideración integral de la estructura de un sistema de archivos (from the scratch), creo que es un avance muy interesante. No como el fin de un camino, sino justamente como un camino alternativo que pueda contribuir al desarrollo general de los sistemas operativos (unix).

¿Ustedes qué opinan?

Fuentes:

PHP 5.3 Alpha 3

PHP

Junto con el lanzamiento de Python 3, aparentemente los muchachos de PHP no quisieron ser menos y lanzaron ayer el release Alpha 3 de PHP 5.3.

Se trata de un nuevo release de pruebas de la nueva versión de PHP, la cual incluirá casi todos los nuevos features que venimos esperando en PHP 6, salvo el soporte para Unicode. Entre ellos se incluye el soporte para namespaces (respecto de cuya implementación hubo mucha controversia, por los operadores que se eligieron… la documentación parece bastante confusa hasta el momento), cambios en la forma de redondeo,  eliminación de la ext/msql, excepción E_DEPRECATED para la ext/ereg (por fin!), reemplazo de ext/mhash por ext/hash, cambio del compilador por default de gcc a cc y bugfixes generales.

Por supuesto, se trata de una versión Alpha, desaconsejada para entornos de producción. Pero es interesante ir probándola para actualizar nuestras aplicaciones.

Python 3.0 lanzado

Se lanzó la versión final de Python 3.0 que tiene la característica de no ser backward-compatible con las versiones anteriores de Python. Un paso muy interesante en el proceso de desarrollo de cualquier tipo de software, del tipo que todos siempre queremos hacer y nunca nos animamos.

Python 3.0

Python 3.0

Hoy mi Google Reader estaba explotando con la noticia. Lo ví en VivaLinux, Slashdot y Barrapunto.

Aquí pueden ver el anuncio oficial del release de la versión 3.0 de Python.

Los cambios son muchos e incluyen: modificaciones en la sintaxis, corrección de errores, remoción de características obsoletas, reorganización de sus librerías internas, etc. Aquí la lista completa.

Es importante resaltar, como indican en Barrapunto, que esto no significa, por supuesto, que vaya a desaparecer Python 2.x, sino que esta última va a tener soporte por bastante tiempo más.

Resulta interesante que la noticia coincide con este otro artículo de Barrapunto, donde se habra del languidecimiento de Perl y de la necesidad de revitalizar su comunidad, para que no muera el lenguaje o no siga la misma suerte que COBOL.

Slackware 12.2 RC1 en camino

Veo en VivaLinux que la Release Candidate de la nueva versión de Slackware está pronta a ser lanzada.

Slackware Linux

“A pesar de que todavía hay alguna documentación que necesita ser actualizada y probablemente otros arreglos aquí y allá, todo está en su lugar en su mayoría para la próxima versión estable, Slackware 12.2. Las versiones de componentes principales como X y el Kernel pueden considerarse como congelados. Cualquier otra cosa importante que no hayamos hecho todavía probablemente tendrá que esperar para la próxima versión después de esta. Debido a que Slackware 12.2 es sólo un salto 0.1 de versiones, estamos tratando de enfocarnos en hacerla mejor sin causar inestabilidad ni perder compatibilidad con Slackware 12.1 hasta donde sea posible.”

Changelist: lwn.net

Script en BASH para comparar tráfico con los access_logs de Apache

Antes que nada debo decir que este script fue algo que hice en un rato tratando de solucionar un problema, y tiene varios errores y cosas mejorables. De más está decir que no cuenta con ninguna garantía, sino que lo comparto porque quizás pueda ayudar a alguien que enfrente un problema similar.

Mi problema es que necesitaba saber cuáles eran los dominios de un servidor que alojaba más de 300 que estaban teniendo mayor tráfico. Como no tenía ninguna herramienta en tiempo real que me pudiera reportar esta información para el Apache, lo que hice es un script que revisa los access_log de cada dominio y obtiene la cantidad de bytes traficados por cada uno.

En este caso, cada VirtualHost define un archivo de logs diferente para cada dominio. Los access_log usan el formato combined (que incluye User-Agent, etc.) y no especifican nada más. El script no hace ninguna validación al respecto, así que podría no funcionar con logs en otros formatos.

Debe tenerse en cuenta que si los archivos de logs rotan en base a su tamaño, es probable que se presente el siguiente caso: El dominio web1.com tiene gran cantidad de tráfico y el dominio web2.com todo lo contrario. El access_log correspondiente al primer dominio puede reportar el tráfico de 1 Gb en un día y el access_log del segundo puede reportar tráfico también de 1 Gb pero en 6 meses. Por lo tanto, es importante mantener referencia de las fechas extremas de los logs, para poder dar cuenta de estas circunstancias.

Hechas estas aclaraciones, pasamos a la explicación del script.

¿Cómo funciona?

Muy sencillo. El script recorre todos los archivos de logs (1 por dominio). Por cada uno obtiene la fecha inicial (la del primer request registrado) y la final (la del último). Luego suma la cantidad de bytes de todos los requests registrados, e imprime una fila con esa cifra en megabytes, kilobytes, bytes y el rango de fechas que abarca. El conjunto de todas esas líneas es enviada al sort de GNU para ordenar las líneas de menor a mayor cantidad de tráfico.

Quizás suene medio complejo, pero se entenderá mejor viendo el código.

El Script

BASE_DIR=/tmp/domain_logs

dateToStamp ()
{
    if [ ${#1} -eq 19 ]; then
        date --utc --date "$1" +%s;
    else
        echo 0
    fi
}

formatDate ()
{
    echo $1 | awk 'BEGIN { \
                    dict["Jan"]=1; \
                    dict["Feb"]=2; \
                    dict["Mar"]=3; \
                    dict["Apr"]=4; \
                    dict["May"]=5; \
                    dict["Jun"]=6; \
                    dict["Jul"]=7; \
                    dict["Aug"]=8; \
                    dict["Sep"]=9; \
                    dict["Oct"]=10; \
                    dict["Nov"]=11; \
                    dict["Dec"]=12 \
                    } { \
                        split(substr($1, 2), a, /[\\\/:]+/); \
                        print a[3] "-" dict[a[2]] "-" a[1] " " a[4] ":" a[5] ":" a[6] \
                    }'
}

for file in `ls $BASE_DIR/*`;
do
    from=$(formatDate `head -1 $file | awk '{print($4)}'`)
    to=$(formatDate `tail -1 $file | awk '{print($4)}'`)
    stamp_from=$(dateToStamp "$from")
    stamp_to=$(dateToStamp "$to")
    total_time=$(($stamp_to-$stamp_from))
    bytes=`cat $file | awk '{a+=$10}END{print a}'`
    kbytes=$((bytes/1024))
    mbytes=$((kbytes/1024))
    echo  "$mbytes MB | \
           $kbytes KB | \
           $bytes Bytes | \
           From: $from ($stamp_from) | \
           To: $to ($stamp_to) | \
           $total_time secs | \
           ${file:${#BASE_DIR}+1}"
done \
| sort -nb

Como se puede ver, al principio se define la variable BASE_DIR, que es el directorio base donde el script va a buscar los access logs. Cuando ejecuto el script, para evitar problemas de cambios en tiempo real, creo un snapshot con todos los archivos de logs y los guardo en una carpeta (en este caso /tmp/domain_logs).

# cp /var/log/httpd/domains/*.access.log /tmp/domain_logs/

Luego, trabajo directamente sobre el snapshot. Entonces, como se observa en el script, defino dos funciones que utilizo más abajo, dateToStamp y formatDate. Como sus respectivos nombres indican, la primera convierte una fecha en un unix timestamp y la segunda cambia el formato de la fecha que figura en el archivo de logs por el que a mi me sirve para pasarle a date. Esto seguramente es mucha complicación para algo que podría hacer usando mejor el comando date, pero fue lo que más rápido me resultó hacer.

Y luego está el for, que recorre los archivos y por cada uno obtiene las fechas límite ($from y $to), de allí obtiene los timestamps ($stamp_from, $stamp_to), el tiempo total en segundos y la cantidad de bytes (la cual sumo con awk).

Por último, verán que la salida del for tiene un pipe a sort, a quien le paso los parámetros “n” y “b”. Para que haga un ordenamiento numérico ignorando los espacios en blanco.

Ejemplo

# ./access_log_parser.sh
97 MB | 99901 KB | 102299281 Bytes | From: 2008-11-28 02:25:54 (1227839154) | To: 2008-11-28 15:34:12 (1227886452) | 47298 secs | domain1.com.ar.log
115 MB | 118260 KB | 121098817 Bytes | From: 2008-11-28 02:27:45 (1227839265) | To: 2008-11-28 15:34:26 (1227886466) | 47201 secs | domain2.com.ar.log
123 MB | 126652 KB | 129691792 Bytes | From: 2008-11-28 02:27:53 (1227839273) | To: 2008-11-28 15:33:01 (1227886381) | 47108 secs | domain3.com.ar.log
145 MB | 149279 KB | 152862681 Bytes | From: 2008-11-28 02:30:29 (1227839429) | To: 2008-11-28 15:34:31 (1227886471) | 47042 secs | domain4.com.ar.log
189 MB | 194517 KB | 199185477 Bytes | From: 2008-11-28 04:35:20 (1227846920) | To: 2008-11-28 15:32:58 (1227886378) | 39458 secs | domain5.com.log
197 MB | 202034 KB | 206883560 Bytes | From: 2008-11-28 02:35:55 (1227839755) | To: 2008-11-28 15:34:29 (1227886469) | 46714 secs | domain6.com.ar.log
499 MB | 511816 KB | 524099937 Bytes | From: 2008-11-28 02:25:44 (1227839144) | To: 2008-11-28 15:34:29 (1227886469) | 47325 secs | domain7.com.ar.log
#

Comentarios

Por última vez, aclaro que es algo que hice con cierto apuro y que es ampliamente mejorable. Estoy abierto a sus comentarios y sugerencias para mejorar el script. También, si conocen alguna forma de monitorear esto en tiempo real, también me sería de gran utilidad.

Descargar

Pueden descargar el script de aquí.