Clustering de Alta Disponibilidad
bajo GNU/Linux
Vicente José Aguilar Roselló
<v.aguilar@terra.es>, <vjaguilar@renr.es>
Septiembre 2001
Tutor: D. Manuel Marco Such
Departamento de Lenguajes y Sistemas Informáticos
Resumen:
Este trabajo explora las distintas posibilidades que nos ofrece hoy en día el mundo del Software Libre para implantar servidores de alta disponibilidad en el terreno empresarial y orientados principalmente al servicio en Internet (servidores HTTP, SMTP, POP, etc), basados en la replicación de servidores (clustering) con arquitecturas PC Intel x86 y bajo el Sistema Operativo GNU/Linux.
El presente documento se publica bajo los términos de la licencia FDL (Free Documentation License) de GNU y puede ser redistribuido o modificado según los mismos. Todos los programas, scripts o ficheros de configuración aquí expuestos se distribuyen bajo la licencia GPL (General Public License) de GNU, y se garantiza el derecho de redistribución y modificación bajo los términos de dicha licencia. El texto de ambas licencias se puede encontrar en la página web de GNU en http://www.gnu.org y en los enlaces de la bibliografía al final de este documento.
Este trabajo ha sido desarrollado íntegramente utilizando software libre: la plataforma de desarrollo fue Debian GNU/Linux (http://www.debian.org) con el entorno de escritorio GNOME (http://www.gnome.org, http://www.ximian.com); para la edición del texto se utilizó AbiWord (http://www.abisource.com) y Open Office (http://www.openoffice.org); para los diagramas, figuras y esquemas, DIA (http://www.lysator.liu.se/~alla/dia/); para los gráficos de barras, el gnuplot (http://www.gnuplot.org); y para el retoque de gráficos, el GIMP (http://www.gimp.org); y para convertir el fichero PostScript generado por OpenOffice a PDF con el conversor ps2pdf del paquete GhostScript (http://www.ghostscript.com).
2001 ã Vicente José Aguilar Roselló <v.aguilar@terra.es>, <vjaguilar@renr.es>.
Índice de contenidos
1. Introducción 1
1.1. GNU/Linux y el Software Libre 2
1.2. Introducción al clustering de servidores 3
1.3. Consideraciones previas 5
2. Gestión del almacenamiento 7
2.1. Gestión avanzada de los discos 7
2.1.1. RAID 7
2.1.2. LVM 10
2.2. Sistemas de Ficheros 13
2.2.1. ext2 13
2.2.1.1. Estructura física 15
2.2.1.2. Los i-nodos 16
2.2.1.3. Uso 17
2.2.1.4. ext3 18
2.2.2. ReiserFS 18
2.2.2.1. Sistemas transaccionales 18
2.2.2.2. Características de ReiserFS 19
2.2.2.3. Árboles B* 20
2.2.2.4. Uso 22
2.2.3. xfs y jfs 24
3. Distribución de los datos 25
3.1. Replicación de archivos 25
3.1.1. rsync 25
3.1.1.2. El algoritmo rsync 26
3.1.1.3. Resultados 27
3.1.1.4. Instalación y uso 28
3.2. Sistemas de ficheros distribuidos 30
3.2.1. NFS 30
3.2.1.1. Los protocolos detrás de NFS 30
3.2.1.2. El servidor 31
3.2.1.3. El cliente 33
3.2.1.4. Precauciones 34
3.2.2. Samba 35
3.2.2.1. Programas 36
3.2.2.2. Configuración 36
3.2.2.3. Accediendo a Windows desde Linux 38
3.2.3. CODA 39
3.2.3.1. Terminología CODA 40
3.2.3.2. Los servidores 41
3.2.3.3. Los clientes 45
3.2.3.4. Características avanzadas 45
3.2.4. GFS 46
3.2.4.1. Sistemas de discos compartidos 47
3.2.4.2. Características de GFS 47
3.2.4.2. Instalación de GFS sobre Canal de Fibra 48
3.2.4.3. Limitaciones de GFS 53
4. Monitorización 55
4.1. daemontools y ucspi-tcp 55
4.1.1. Configuración y uso 55
4.2. mon 57
4.3. heartbeat y fake 58
4.4. Failover de red con iANS de Intel 58
4.4.1. Configuración de iANS en modo AFT 60
4.4.2. Ejemplo de configuración manual 62
5. Clustering de Alta Disponibilidad 63
5.1. Linux Virtual Server 63
5.1.1. Visión general de LVS 63
5.1.2. Cómo distribuir la carga 65
5.1.3. Modos de balanceado de carga en LVS 66
5.1.3.1. Balanceado por NAT (VS-NAT) 66
5.1.3.2. Balanceado por encapsulado IP (VS-Tun) 69
5.1.3.3. Balanceado por enrutamiento directo (VS-DR) 71
5.1.3.4. Resumen de los métodos de balanceado 73
5.1.4. Planificación del balanceo de carga 73
5.1.4.1. Round Robin 73
5.1.4.2. Round Robin Ponderado 74
5.1.4.3. Servidor con menos conexiones activas 74
5.1.4.4. Servidor con menos conexiones activas (ponderado) 75
5.1.4.5. Menos conectado basado en servicio 75
5.1.4.6. Tablas hash por origen y destino 75
5.1.4.7. Conexiones persistentes 75
5.1.5. Alta disponibilidad en LVS 76
5.1.5.1. mon+heartbeat+fake+coda 76
5.1.5.2. ldirectord+heartbeat 78
5.1.6. El software 79
5.1.6.1. lvs-gui 80
5.1.6.2. LVSM 82
5.1.6.3. Módulo webmin para LVS 82
5.1.6.4. Ultra Monkey 82
5.1.6.5. Piranha 85
5.2. Super Sparrow 86
5.2.1. BGP 86
5.2.2. Funcionamiento de Super Sparrow 87
5.2.3. El software 89
5.2.4. Super Sparrow y Apache 91
6. Programas para la instalación y administración 93
6.1. Linux Utility for cluster Installation (LUI) 93
6.2. FAI 95
6.2.1. Funcionamiento 95
6.3. VA SystemInstaller 96
6.3.1. Requerimientos 97
6.3.2. Funcionamiento 98
6.4. webmin 100
7. Probando el software 103
7.1. Instalación de GNU/Linux en un equipo 103
7.2. RAID, LVM, ext2 y reiserfs 103
7.3. Instalación remota con VA System Imager 112
7.3.1. Instalación del software en el servidor 112
7.3.2. Instalación linux en el golden client 113
7.3.3. Instalación del software cliente en el golden client 114
7.3.4. Ejecutar getimage en el servidor 115
7.3.5. Creación del disco de arranque para instalar los clientes 122
7.4. CODA 124
7.4.1. El servidor CODA 125
7.4.2. El cliente CODA 132
7.4.3. Pruebas de rendimiento 135
7.5. mon 141
7.6. iANS 143
8. Conclusiones 147
9. Bibliografía 151
9.1. Documentación, HOWTOs y FAQs 151
9.2. RFCs 152
9.3. Licencias 153
10. Enlaces 155
Índice de imágenes
Imagen 1. RAID: Situación 9
Imagen 2. LVM: Situación 11
Imagen 3. LVM: Asignación de espacio 12
Imagen 4. ext2: Estructura del disco 15
Imagen 5. ext2: Estructura de una partición 15
Imagen 6. ext2: i-nodos 16
Imagen 7. ReiserFS: Árboles B 21
Imagen 8. CODA: Árbol de directorios 40
Imagen 9. CODA: Organización de una celda 43
Imagen 10. GFS: Esquema general 48
Imagen 11. LVS: Esquema general 63
Imagen 12. LVS: VS-NAT 66
Imagen 13. LVS: VS-NAT, esquema físico 67
Imagen 14. LVS: Encapsulado IP 69
Imagen 15. LVS: VS-Tun 69
Imagen 16. LVS: VS-DR 71
Imagen 17. LVS: Alta disponibilidad 77
Imagen 18. LVS: lvs-gui 81
Imagen 19. LVS: Ultra Monkey, método 1 83
Imagen 20. LVS: Ultra Monkey, método 2 83
Imagen 21. LVS: Ultra Monkey, método 3 84
Imagen 22. LVS: Ultra Monkey, método 4 84
Imagen 23. Super Sparrow: Ejemplo BGP 87
Imagen 24. Super Sparrow: Ejemplo de funcionamiento 88
Imagen 25. Super Sparrow: Funcionamiento de mod_supersparrow 90
Imagen 26. Super Sparrow: Integración con Apache 91
Imagen 27. LUI: Interfaz gráfico 94
Imagen 28. VA SystemImager: Instalación, paso 1 99
Imagen 29. VA SystemImager: Instalación, paso 2 99
Imagen 30. VA SystemImager: Instalación, paso 3 99
Imagen 31. webmin: Menú principal 100
Imagen 32. webmin: Administración Cyrus IMAP 101
Imagen 33. Comparativa: ext2 vs. reiserfs (1/2) 104
Imagen 34. Comparativa: ext2 vs. reiserfs (2/2) 105
Imagen 35. Comparativa: RAID + LVM + ext2 (1/2) 106
Imagen 36. Comparativa: RAID + LVM + ext2 (2/2) 107
Imagen 37. Comparativa: RAID + LVM + reiserfs (1/2) 108
Imagen 38. Comparativa: RAID + LVM + reiserfs (2/2) 109
Imagen 39. Comparativa: RAID + ext2 vs. RAID + reiserfs 110
Imagen 40. Comparativa: RAID1 + reiserfs 111
Imagen 41. Comparativa: NFS vs. CODA 140
Imagen 42. Conclusión: Cluster sencillo 147
Índice de tablas
Tabla 1. rsync: Rendimiento 27
Tabla 2. NFS: Demonios 31
Tabla 3. NFS: Servicios, puertos y protocolos 35
Tabla 4. CODA: Procesos servidores 42
Tabla 5. CODA: Particiones en el servidor 44
Tabla 6. LVS: Métodos de direccionamiento 73
Tabla 7. VA SystemImager: Distribuciones soportadas 97
Tabla 8. VA SystemImager: Otras distribuciones 97
Con el actual ritmo de crecimiento del comercio y el movimiento de datos de todo tipo en Internet (más de un 100% anual) y la incuestionable importancia de la informática en las empresas actuales de cualquier tamaño, es cada día más importante que los sistemas informáticos de éstas puedan funcionar de forma ininterrumpida y sin errores las 24h del día, 7 días a la semana y 365 días al año, ya sea para dar soporte interno (contabilidad, control de personal, desarrollo...) como para ofrecer servicios a través de Internet (comercio electrónico, correo, portales, etc). A esta necesidad de un servicio ininterrumpido y fiable se le conoce como alta disponibilidad.
Dos estudios independientes realizados en 1995 por Oracle Corp. y Datamation revelaron que una empresa media pierde entre 80,000 y 350,000 dólares (entre 15 y 70 millones de pesetas) por hora de interrupción no planeada de sus servicios informáticos. Otro ejemplo de la necesidad de la alta disponibilidad es que tras el atentado en el World Trade Center en 1993, 145 de las 350 empresas que allí se hospedaban (algo más del 40%) tuvieron que cerrar sus puertas tras este incidente por no disponer de una infraestructura informática redundante.
La principal técnica para obtener estos sistemas tolerantes a fallos es la redundancia, estrategia utilizada en la industria aeronáutica prácticamente desde sus principios, que consiste en replicar las zonas críticas del sistema, teniendo una unidad activa y varias copias inactivas que, tras el fallo de la principal, sean capaces de retomar su labor en el punto que aquella falló, en el menor tiempo posible y de forma transparente para el usuario.
Existen gran cantidad de servidores altamente redundantes en el mercado fabricados por SUN, IBM y demás empresas del ramo. Son grandes máquinas multiprocesador , con varias controladoras de disco, configuraciones RAID, fuentes de alimentación redundantes, y un largo etcétera de circuitos y controladoras duplicadas para que, en caso de fallo, haya alguna de respaldo. El precio de este tipo de equipos rara vez baja de varias decenas de millones de pesetas. Además, cuando una máquina de este tipo queda obsoleta, no nos queda otro remedio que comprar otra mayor y deshacernos de la antigua.
El presente estudio se centrará en la técnica de obtener una alta disponibilidad por medio de la redundancia, instalando varios servidores completos en lugar de uno sólo, que sean capaces de trabajar en paralelo y de asumir las caídas de algunos de sus compañeros, y podremos añadir y quitar servidores al grupo (cluster) según las necesidades. A esta técnica se la denomina clustering. Por otra parte, también se abordarán todas las técnicas necesarias para asegurar la estabilidad de cada uno de los servidores del cluster, técnicas que en muchos casos también se basarán en la redundancia de dispositivos. En todos caso los equipos serán PCs normales de los que podemos encontrar en cualquier tienda de informática personal, con procesadores Intel Pentium o AMD, que en ningún caso valdrá cada uno más de doscientas mil pesetas.
Este trabajo está estructurado según el orden que seguiremos a la hora de ir configurando cada uno de los equipos que formarán parte de nuestro cluster: tras una introducción inicial a las diversas técnicas de clustering, su problemática y sus soluciones, comenzaremos viendo los métodos para asegurar que la información almacenada en los discos de nuestros servidores sea segura, cómo conseguir que éstos compartan información, cómo conseguir que un equipo tome el control de los servicios de otro, cómo organizar y administrar el cluster y cómo dividir el cluster geográficamente en un cluster de clusters.
GNU/Linux es un sistema operativo compatible UNIX, multiusuario y multitarea. Su núcleo, el kernel Linux, fue diseñado a principios de los 90 por Linus Torvalds para los PCs 80x86 y compatibles de la época y, gracias a su código abierto y al desarrollo distribuido en Internet, ha sido adaptado a gran cantidad de arquitecturas, desde estaciones de trabajo RISC hasta PDAs como el IPac de Compaq o incluso a la consola de videojuegos PlayStation de Sony. GNU (acrónimo recursivo de GNU is Not Unix) por su parte, es un proyecto iniciado por Richard Stallman (otro de los gurús del software libre) a mediados de los 80 cuyo objetivo es el de conseguir un sistema operativo tipo UNIX completamente gratuito y con el código disponible bajo una licencia abierta. En principio, el kernel para GNU iba (y va) a ser Hurd, todavía en desarrollo, pero cuando Torvalds liberó las primeras versiones de Linux se vio claramente que se necesitaban el uno al otro, ya que el núcleo era la pieza que faltaba para poder echar a andar el sistema operativo de GNU, mientras que el kernel Linux de por si, sin utilidades ni librerías ni entorno operativo, no podía valerse por sí mismo. Así nació el binomio GNU (herramientas y entorno) / Linux (núcleo).
Se podría decir que el sistema GNU/Linux es el buque insignia del movimiento conocido como Software Libre. Este movimiento (casi una filosofía de vida) promueve el desarrollo cooperativo del software, por medio de la liberación bajo licencias abiertas del código fuente de los programas, de forma que cualquier persona en cualquier parte del mundo pueda aportar su granito de arena. Existen gran cantidad de licencias dentro del mundo del soft libre, siendo las más importantes y extendidas de ellas la General Public License (GPL) de GNU, que prácticamente da permisos para hacer cualquier cosa con el programa (incluso cobrar por su distribución, siempre que se cumplan el resto de cláusulas) excepto derivar de él trabajos y que éstos no se liberen también bajo la GPL, ni que formen parte de software propietario (un programa propietario no puede enlazarse con una librería GPL); la Lesser General Public License (LGPL) también de GNU, similar a la GPL pero que si permite que un programa con licencia propietaria enlace con librerías LGPL; y la licencia BSD, que elimina prácticamente todas las restricciones de la GPL y LGPL, permitiendo que el código de un programa con licencia BSD sea incluido en un programa comercial sin problemas. Al final de este trabajo se incluyen enlaces a los textos de varias de estas licencias.
Cabe aclarar aquí que todas estas licencias bajo ningún concepto dan derecho a nadie de ADUEÑARSE del código: el concepto de copyright (derechos de autor) sigue presente en todas ellas y se proteje con especial cuidado. Lo que persiguen las licencias abiertas es dar al usuario final una serie de derechos y libertades sobre el software mucho mayores de las que dan las licencias propietarias, pero manteniendo siempre el autor del programa los derechos sobre su obra. Esta es la principal diferencia entre el software libre y el software de Dominio Público (el autor cede TODOS los derechos, incluido el copyright), el Freeware (se puede utilizar gratuitamente pero generalmente no se dispone del código fuente, y cuando se dispone su uso y modificación está restringido) y el Shareware (se puede utilizar libremente con ciertas restricciones o durante un cierto periodo de tiempo tras el que hay que registrarse, y el código fuente no está disponible).
Además del sistema operativo GNU/Linux, otros notables éxitos del software libre son el servidor de HTTP Apache (líder en el terreno de los servidores Web, por delante del IIS de Microsoft), el lenguaje de script en el servidor embebido en HTTP PHP (claro competidor frente al ASP de Microsoft y el JSP de Sun), el navegador multiplataforma Mozilla (derivado del código fuente del Netscape Navigator 4.7x que liberó Netscape), la suite ofimática multiplataforma y compatible con MS Office Open Office, y los entornos de escritorio GNOME y KDE (a pesar de los problemas de licencias que tuvo en el pasado por una librería de la que depende).
GNU/Linux y el software libre en general han pasado en los últimos años de ser considerados como poco más que juguetes para locos de la informática, a formar parte clave de la estrategia comercial y la infraestructura de grandes empresas. Como ejemplo cabe destacar la investigación y desarrollo de aplicaciones que están realizando empresas como IBM o SUN y su adopción del sistema operativo Linux, y el apoyo que está recibiendo también desde el entorno de las instituciones gubernamentales, donde cabe señalar el proyecto GPG (GNU Privacy Guard, una alternativa open source al programa de criptografía de clave privada PGP), que ha sido patrocinado por el gobierno alemán. Aquí en España habría que destacar el proyecto de modernización de los sistemas informáticos del Ministerio de Administraciones Públicas, llevado a cabo por la empresa madrileña Andago y basado íntegramente en software libre bajo GNU/Linux.
Todo el software que se va a analizar y discutir en este trabajo se distribuye bajo licencias abiertas, principalmente la General Public License (GPL) de GNU, la licencia BSD y la de Apache. Son, por tanto, programas gratuitos y con el código fuente disponible.
En el verano de 1994 Thomas Sterling y Don Becker, trabajando para el CESDIS (Center of Excellence in Space Data and Informarion Sciencies) bajo el patrocinio del Proyecto de las Ciencias de la Tierra y el Espacio (ESS) de la NASA, construyeron un Cluster de Computadoras que consistía en 16 procesadores DX4 conectados por una red Ethernet a 10Mbps. Ellos llamaron a su máquina Beowulf. La máquina fue un éxito inmediato y su idea de proporcionar sistemas basados en COTS (equipos de sobremesa) para satisfacer requisitos de cómputo específicos se propagó rápidamente a través de la NASA y en las comunidades académicas y de investigación. El esfuerzo del desarrollo para esta primera máquina creció rápidamente en lo que ahora llamamos el Proyecto Beowulf.
Este Beowulf construido en la NASA en 1994 fué el primer cluster de la historia, y su finalidad era el cálculo masivo de datos. Desde entonces, la tecnología de clusters se ha desarrollado enormemente, apareciendo gran cantidad de estudios, teorías, programas y arquitecturas implantando clusters para diversos fines.
En general, podríamos decir que hay dos tipos de clusters, atendiendo a su finalidad:
Clusters para el procesamiento masivo de datos:
El ejemplo más claro de este tipo el el Proyecto Beowulf, del que ya hemos hablado. Este tipo de clusters, por lo general, aprovechan la posibilidad de paralelización de cierto tipo de operaciones matemáticas (en especial, el cálculo matricial) para repartir los datos entre todos los equipos del cluster y poder así operar varios grados de magnitud más rápido. Para este fin se utilizan librerías como las PVM (Parallel Virtual Machine), que facilitan la distribución de datos entre las máquinas, incluso entre máquinas con distintos sistemas operativos, arquitecturas y lenguajes de programación.
Otro ejemplo de cluster de este tipo sería el caso de MOSIX, unos parches para el núcleo de Linux con los que se consigue poder utilizar de forma transparente toda una red de equipos como si fuera una única supercomputadora, permitiendo el migrado transparente de cara al usuario de procesos de una máquina a otra y la compartición de recursos.
Toda la teoría sobre este tipo de clusters se centra en cómo compartir los recursos de procesador, memoria y/o red entre los equipos que forman el cluster para obtener un mejor rendimiento general.
Clusters de alta disponibilidad:
En este caso lo que se busca no es exactamente conseguir una gran potencia de cálculo si no conseguir un conjunto de máquinas que todas realicen la misma función y que, ante el fallo de una de ellas, las demás puedan asumir sus tareas de una forma transparente y rápida. Por supuesto, la escalabilidad también es importante, ya que siempre podremos añadir más máquinas al cluster para así conseguir más potencia, pero el objetivo prioritario no es este si no la resistencia a cualquier fallo imprevisto.
Aquí lo que se busca con la replicación de máquinas es evitar los puntos únicos de fallo, del inglés SPF (Single Point of Failure), que serían aquellas máquinas imprescindibles para el correcto funcionamiento del servicio que queremos dar: si únicamente tenemos una instancia de cada máquina de este tipo, se convierte en un SPF y ante cualquier fallo en este equipo, todo el cluster queda inutilizado. La teoría sobre este tipo de clusters gira en torno a estos SPF y cómo evitarlos, mediante redundancia hardware y el software apropiado para controlar el correcto funcionamiento de todos los equipos y, en caso negativo, hacer que una máquina de respaldo suplante a la que acaba de fallar.
Como ya hemos adelantado, la técnica que vamos a explorar aquí para obtener alta disponibilidad en nuestros servicios será la replicación de servidores a tantos niveles como nos sea posible. Por lo tanto, el tipo de clusters que nos interesa es el segundo de los expuestos.
Los clusters de alta disponibilidad necesitan de un amplio abanico de componentes que consideren diversos factores, entre otros:
Control de miembros del cluster.
Servicios de comunicaciones.
Control y gestión del clustering, flujo de datos.
Gestión y monitorización de recursos.
Compartición o replicación del almacenamiento:
Compartición:
Discos SCSI externos.
Sistemas NAS.
Sistemas de ficheros compartidos (NFS, SMB, Coda).
Replicación:
Propio de la aplicación (DNS, NIS, etc.)
ftp, rsync, etc.
Todos estos detalles habrá que tenerlos en cuenta a la hora de diseñar el cluster y elegir el software que lo gestionará, ya que este software debe ser capaz por sí mismo de atender todos estos puntos de atención y reaccionar a tiempo ante un fallo en cualquiera de ellos.
A lo largo de este trabajo vamos a estudiar las características y funcionamiento de diversos paquetes de software. Se explicará cómo configurarlos y utilizarlos una vez instalados pero, salvo excepciones, no se explicará paso a paso como instalar cada programa ya que existe abundante documentación en Internet (FAQs, HOW-TOs, etc) sobre como compilar e instalar software desde el código fuente, cómo parchear y reconfigurar el núcleo de linux, o cómo obtener software en el formato propio de nuestra distribución (.deb para Debian, .rpm para RedHat y derivadas) e instalarlo. En la bibliografía se pueden encontrar enlaces a varios de estos documentos, así como en webs dedicadas a recopilar información sobre Linux tales como el Linux Documentation Project (en inglés) o el Proyecto LuCAS (en castellano).
En la última parte de este trabajo, donde se exponen los resultados de las pruebas realizadas con algunos de los programas que aquí examinaremos, sí que se detallará todo el proceso de instalación, configuración y pruebas tal y como se llevaron a cabo en su día.
Una de las primeras cosas en las que tendremos que pensar a la hora de implantar un sistema de alta disponibilidad será en cómo asegurar la integridad y fiabilidad de los datos almacenados en los discos de nuestros servidores, que deberán estar disponibles de forma continuada durante largos (indefinidos) periodos de tiempo. Un fallo en un dispositivo de almacenamiento podría llevarnos a dar datos erróneos si el fallo se produce en una zona de datos ,con efectos imprevisibles para nuestra empresa; o a un mal funcionamiento del programa si el fallo se localiza en una zona que almacene ejecutables, con efectos aún más imprevisibles, desde la entrega de datos erróneos, hasta el mal funcionamiento del servidor pasando desde el servicio de datos erróneos hasta la corrupción irreversible de los mismos.
En este capítulo vamos a analizar las distintas técnicas disponibles para asegurar la consistencia de los datos albergados en los dispositivos de almacenamiento de nuestros servidores.
Todo el software que veremos en esta sección está formado de dos componentes: un controlador en el kernel, que tendremos que compilar (y que, salvo que se indique, viene de serie en el kernel 2.4.x y no tendremos que parchearlo); y una serie de utilidades en el espacio de usuario para modificar de alguna forma el funcionamiento del sistema (formatear particiones, etc).
La primera pregunta es cómo asignar el espacio del que disponemos. La serie del kernel de Linux 2.4.x nos ofrece dos opciones: agrupar los dispositivos en configuraciones RAID y la gestión avanzada de particiones virtuales conocida como LVM.
RAID (Redundant Array of Inexpensive Disks), como su propio nombre indica, consiste en crear un array (cadena) de varios discos simples (inexpensive, baratos), y tratarlos como un todo a la hora de acceder a ellos. El standard RAID consta de varios niveles, y en cada uno de ellos el acceso a los discos y su contenido se organiza de una forma u otra para conseguir bien mayor capacidad que la de un único disco físico, bien mayor rapidez en el acceso a los datos, bien tolerancia a fallos, o bien alguna combinación de las anteriores.
Los distintos niveles de RAID son:
Modo lineal:
Dos o más discos se utilizan en sucesión, uno detrás del otro (cuando se llena el disco A se utiliza el B), hasta completar el tamaño de los dos. No se consigue un aumento de velocidad ni seguridad por redundancia (si se daña un disco, se pierde la información que tuviera almacenada), tan sólo un dispositivo virtual de mayor tamaño. Es el modo RAID más simple.
RAID-0, también conocido como stripe (intercalado):
Similar al modo lineal, pero la información se va guardando en paralelo en ambos discos por bloques de un tamaño fijo. Tampoco añade seguridad, pero en este caso se consigue un aumento de velocidad al acceder a los dos dispositivos en paralelo. Los discos deben ser de aproximadamente el mismo tamaño y misma velocidad para obtener rendimientos óptimos.
RAID-1 (mirroring, espejado):
Es el primer modo que añade redundancia. Se puede utilizar con dos o más discos, y todos contienen los mismos datos (de ahí lo de espejado). Se pueden estropear o quitar hasta N-1 discos y no se pierde la información. Aparece el concepto de discos inactivos, que son discos que se añaden al RAID pero están en espera de que algún otro dispositivo falle, en cuyo caso el sistema inutiliza el disco dañado y utiliza uno de los discos libres para sustituirlo. Los discos deben ser del mismo tamaño. La escritura es lenta (como mucho, tan rápida como con un sólo disco) porque hay que replicar la información en todos los discos; la velocidad de lectura depende de la implementación del RAID, pero puede ser bastante rápida ya que se puede acceder en paralelo a los datos de varios discos.
RAID-2 y 3 son propuestas y prototipos que nunca llegaron a utilizarse.
RAID-4:
Se necesitan tres o más discos, en uno se guarda información de paridad y en los otros se almacenan los datos en paralelo, al estilo de RAID-0. El tamaño del conjunto es de (N-1)*T, siendo N el número total de discos activos y T el tamaño de los discos (o el del de menor tamaño, si no son iguales). Si falla un disco, la información se puede reconstruir gracias a los datos de paridad; si fallan dos, se pierde todo.
Este modo RAID tiene un problema que hace que no se utilice mucho, y es que, a pesar de escribir los datos en paralelo, como la información de paridad va siempre al mismo disco, éste se convierte en un cuello de botella, ralentizando todo el sistema.
RAID-5:
Se puede montar sobre tres o más discos, con o sin discos inactivos adicionales. Similar a RAID-4, pero la información de paridad se distribuye entre todos los discos, eliminando así el problema del cuello de botella con el disco de paridad. Si falla un disco, la información no se pierde gracias a la paridad, y el contenido del disco dañado se reconstruye en un disco inactivo. Si fallan dos discos de forma simultánea, o si nos quedamos sin discos inactivos, la información se pierde. Tanto la velocidad de lectura como la de escritura aumentan, al realizarse en paralelo.
Existen en el mercado dispositivos de almacenamiento de diversos fabricantes con configuraciones RAID, que externamente para el sistema se comportan como un dispositivo normal (generalmente son discos SCSI), pero internamente llevan varios discos y una controladora dedicada que accede a ellos según alguno de los niveles RAID. Además de estas soluciones prefabricadas, algunos sistemas operativos son capaces de tomar varios dispositivos normales (discos IDE o SCSI) y realizar un RAID por software que, si bien resulta algo más lento que uno por hardware, ya que es el procesador del equipo y no la controladora dedicada quien tiene que tratar con la organización de los datos en los discos, también resultan mucho más baratos y flexibles que los dispositivos prefabricados. El RAID software se sitúa como una capa software más entre el sistema de ficheros y los dispositivos físicos:

Imagen
1. RAID: Situación
Vamos a estudiar cómo se configura un sistema RAID por software en Linux:
Para poder hacer RAID por software en Linux, tendremos que habilitar esta opción en el kernel (en make menuconfig, Multi-device support (RAID and LVM) ---> RAID support, y los modos que queramos) e instalar las raidtools, que nos permitirán configurar el RAID. Para definir el nivel RAID que vamos a implantar y los discos que lo forman, editamos el fichero /etc/raidtab, que tiene esta forma:
raiddev /dev/md0 (el dispositivo a crear)
raid-level linear (nivel: linear,0,1,4,5)
nr-raid-disks 2 (nº de discos activos)
chunk-size 32 (tamaño del bloque l/e)
persistent-superblock 1 (almacena estructura)
device /dev/sdb6 (dispositivo)
raid-disk 0 (nº disco en el RAID)
device /dev/sdc5 (dispositivo)
raid-disk 1 (nº disco en el raid)
Conviene que los dispositivos indicados en las líneas device sean particiones, aún que si fuéramos a utilizar todo el disco podríamos acceder directamente a /dev/hd?. Si son particiones, podemos asignarles un tipo específico para RAID (tipo fd en el fdisk), y de esta forma el kernel sabe nada más arrancar que se trata de una partición en uso por un sistema RAID.
La opción persistent-superblock almacena el contenido del fichero /etc/raidtab al inicio de todos los discos que participan en el RAID. De esta forma, se puede arrancar el sistema desde un dispositivo en RAID (antes de aparecer esta opción, había que montar el dispositivo raíz desde un disco sin RAID para poder leer el fichero /etc/raidtab y montar el RAID). Por su parte, chunk-size indica el tamaño en Kb de los bloques para las lecturas y escrituras en el RAID, que según el nivel RAID serán los bloques que se escribirán en paralelo en los discos.
Si tuviéramos discos libres y el nivel RAID a configurar los soportara, se indicarían como:
nr-spare-disks 1
device /dev/sdf1
spare-disk 0
Para activar el RAID, llamamos a:
mkraid /dev/md0
Una vez hecho esto, ya tenemos disponible /dev/md0, que podemos utilizar como un dispositivo de bloques más: lo podremos formatear y posteriormente montar, copiar con dd, utilizar en un LVM como veremos en el siguiente apartado, etc.
El kernel nos provee de información sobre el estado del array a través del fichero /proc/mdstat. Aquí veremos si hay algún dispositivo dañado, o en caso de que se esté produciendo una reconstrucción o un espejado de algún disco, veremos el progreso.
Por último, señalar la recomendación, no sólo para Linux si no para cualquier sistema que soporte RAID por software, de que si se va a montar el RAID sobre dispositivos IDE, conviene no utilizar más de un disco por canal (es decir, no utilizar discos esclavos). Esto es porque la velocidad soportada por los canales IDE es limitada, y si añadimos dos discos por canal es muy probable que se sature, bajando el rendimiento del sistema. Con dispositivos SCSI, por lo general, no tendremos este problema.
LVM (Logical Volume Manager) es un subsistema para la gestión avanzada de unidades de almacenamiento en caliente, que se ha convertido en un estándar de-facto en varias implementaciones de UNIX. Inicialmente desarrollado por IBM, posteriormente adoptado por la OSF (Open Standards Foundation, ahora OpenGroup) para su sistema operativo OSF/1, que fue la base de las implementaciones de HP-UX y Digital UNIX. Otra implementación de LVM es la desarrollada por Veritas, que está disponible para gran cantidad de sistemas UNIX pero funciona de forma distinta al resto. La versión de Linux, desarrollada por la empresa Sistina Software y liberada bajo la licencia GPL, es muy similar a la de HP-UX. Logical Volume Manager añade una capa software adicional entre los dispositivos físicos y el interfaz de entrada/salida de bloques del kernel, de forma similar a como lo hace el RAID por software, pero con un objetivo distinto.

Imagen
2. LVM: Situación
LVM nos ofrece una forma más potente y flexible de asignar en particiones el espacio físico de los discos duros. En lugar de dividir cada disco de forma individual en una o más particiones, como se haría con fdisk, con las habituales desventajas de no poder tener particiones que ocupen más de un disco (salvo con RAID) y no poder variar el tamaño de las particiones una vez creadas, con lvm agrupamos volúmenes físicos (PV, de Physical Volumes), que pueden ser cualquier dispositivo de bloques (particiones, discos completos o dispositivos /dev/md? del RAID por software) en grupos de volúmenes (VG, Volume Groups). Un VG consiste de uno o más PV, y se dividen en particiones virtuales al estilo de las tradicionales, denominadas volúmenes lógicos (LV, Logical Volumes). Lo novedoso de esta tecnología es que, una vez configurados todos los volúmenes físicos y lógicos, podemos añadir o quitar en cualquier momento y en caliente (si el hardware y software lo permite) más volúmenes físicos a un grupo virtual, o más espacio a un volumen lógico. De esta forma, se elimina de un plumazo el típico problema de tener que parar y reinstalar un sistema porque una partición se ha quedado pequeña y no se puede ampliar.
Dentro de un VG, de los que puede haber varios en nuestro sistema, tanto los PV como los LV se dividen en extensiones, físicas (PE) y lógicas (LE) respectivamente. Estas extensiones son bloques de tamaño fijo, que son la unidad de la asignación de espacio de un PV a un LV, habiendo una relación uno a uno entre cada PE asignada y las LE de los LV:

Imagen
3. LVM: Asignación de espacio
Por supuesto, se pueden dejar PE sin asignar. En el gráfico, la 5ª PE de PV1 no está asignada, ni tampoco las 1ª y 6ª de PV2. En cualquier momento podríamos asignar alguna de estas PE libres a un LV existente para aumentar su tamaño, o crear uno nuevo. De forma similar, podemos eliminar LEs de un LV reduciendo su tamaño y aumentando el número de LEs libres en el sistema.
A pesar de que en el gráfico del ejemplo las asignaciones de PE a LE se han hecho de forma desordenada, el ordenador llevará en principio el orden que nosotros le digamos:
Linear: va asignando PEs de un PV hasta que se agota, y pasa al siguiente.
Striped: asigna un PE de cada PV de forma sucesiva.
Igual que ocurría con RAID, el jugando con estos modos de asignación podemos ganar en velocidad de lectura/escritura por el acceso a varios discos en paralelo.
Otra característica muy interesante de LVM es la posibilidad de crear snapshots (fotos) del sistema en un momento dado, algo muy útil a la hora de hacer una copia de seguridad. LVM necesita un LV para almacenar los datos del snapshot, que se recomienda que tenga sobre el 10% del tamaño del LV original que se quiere replicar. Cuando se le dice a LVM que monte el snapshot, crea un nuevo sistema de ficheros virtual, que será siempre una copia de sólo lectura del sistema original en el momento en que se creó el snapshot, y va utilizando el espacio que se le ha asignado para almacenar los cambios que se realicen sobre el sistema real. De esta forma, podemos seguir trabajando con el sistema normalmente, y disponemos de una imagen estable del sistema en un momento dado, de la que podemos hacer tranquilamente la copia de seguridad.
Para poder utilizar LVM en nuestro equipo, en primer lugar tendremos que compilar el kernel para que lo soporte. La opción se encuentra en make menuconfig -> Multi-device support (RAID and LVM) -> Logical volume manager (LVM) support.
Una vez que hemos arrancado un kernel con soporte para LVM, el primer paso será preparar un dispositivo (volumen físico) para añadirlo a un grupo virtual con la orden pvcreate (phisical volume create). Por ejemplo, si queremos preparar una partición física /dev/hda3, que deberá ser del tipo 8e (Linux LVM), tendríamos que hacer (siempre como root):
pvcreate /dev/hda3
Tras esto, la partición ya está preparada para añadirla a, o crear con ella, un grupo virtual. Como aún no tenemos ninguno, crearemos uno con vgcreate:
vgcreate /dev/lvm1 /dev/hda3
Podríamos haber añadido más dispositivos en este paso, si los hubiéramos tenido.
Con esto, ya tenemos un grupo virtual llamado /dev/lvm1 con toda la capacidad de hda3. Ahora tendremos que dividir este grupo en particiones virtuales (volúmenes lógicos) con lvcreate:
lvcreate -L 500M -n raiz /dev/lvm1
Esta orden crearía una partición virtual /dev/lvm1/raiz de 500Mb. Con la opción -L se indica el tamaño, y acepta los indicadores K (para Kb), M (para Mb) y G (para Gb). También podemos elegir el tamaño con la opción -l, indicando en este caso en número de extensiones en lugar de la capacidad. Con esta orden crearemos tantas particiones virtuales como consideremos necesarias para organizar nuestro sistema. En caso de haber añadido más de una partición física al grupo virtual /dev/lvm1, las particiones virtuales se repartirán entre todos los discos, de forma similar a lo que hace RAID, pero con menos control por nuestra parte del que podemos llegar a tener con RAID.
Una vez que tenemos creadas las particiones virtuales, tendremos que darles formato tal y como lo haríamos con una partición física (p.ej. mkfs como veremos en el siguiente apartado). Tras ello las montaremos, y se podrán utilizar normalmente.
Una vez que tenemos asignado el espacio en particiones (ya sean físicas de fdisk o lógicas de LVM, con o sin RAID) tenemos que darle una estructura lógica para que acoja los directorios y ficheros. A esta estructura lógica se le conoce como sistema de ficheros.
Linux soporta de serie gran cantidad de sistemas de ficheros, algunos considerados nativos de este sistema (diseñados para él específicamente, o para otros UNIX y adaptados y adoptados ampliamente bajo Linux), y otros propios de otros sistemas operativos (la vfat de Windows 9X/ME, el NTFS de Windows NT/2000 o el HPFS de MAC).
ext2 es el sistema de ficheros por excelencia de Linux, y el que instalan por defecto las distribuciones actuales (aún que algunas ya están ofreciendo la opción de utilizar ReiserFS). Ofrece funcionalidades estándar, soporta los archivos Unix (archivos regulares, directorios, archivos especiales, enlaces simbólicos) y ofrece funcionalidades avanzadas:
Pueden asociarse atributos a los archivos para modificar el comportamiento del núcleo; los atributos reconocidos son los siguientes:
Supresión segura: cuando el archivo se suprime, su contenido se destruye previamente con datos aleatorios.
Undelete: cuando el archivo se suprime, se guarda automáticamente a fin de poder restaurarlo ulteriormente (aún no se ha implementado).
Compresión automática: la lectura y la escritura de datos en el archivo da lugar a una compresión al vuelo (aún no se ha implementado en el núcleo estándar).
Escrituras síncronas: toda modificación sobre el archivo se escribe de manera síncrona en disco.
Inmutable: el archivo no puede modificarse ni suprimirse.
Adición exclusiva: el archivo sólo puede modificarse si se ha abierto en modo adición, y no puede suprimirse.
Compatibilidad con las semánticas de Unix System V Release 4 o BSD: una opción de montaje permite elegir el grupo asociado a los nuevos archivos; la semántica BSD especifica que el grupo se hereda desde el directorio padre, mientras que SVR4 utiliza el número de grupo primario del proceso que llama.
Enlaces simbólicos rápidos: ciertos enlaces simbólicos no utilizan bloque de datos; el nombre del archivo destino está contenido directamente en el i-nodo en disco, lo que permite economizar el espacio en disco y acelerar la resolución de estos enlaces evitando una lectura de bloque.
El estado de cada sistema de archivos se memoriza: cuando el sistema de archivos se monta, se marca como inválido hasta que se desmonte. El verificador de la estructura, e2fsck, utiliza este estado para acelerar las verificaciones cuando no son necesarias.
Un contador de montaje y una demora máxima entre dos verificaciones pueden utilizarse para forjar la ejecución de e2fsck.
El comportamiento del código de gestión puede adaptarse en caso de error: puede mostrar un mensaje de error, remontar el sistema de archivos en lectura exclusiva a fin de evitar una corrupción de los datos, o provocar un error del sistema.
Además, ext2 incluye numerosas optimizaciones. En las lecturas de datos, se efectúan lecturas anticipadas. Ello significa que el código de gestión pide la lectura no sólo del bloque que necesita, sino también de otros bloques consecutivos. Esto permite cargar en memoria bloques que se usarían en las entradas/salidas siguientes. Este mecanismo se utiliza también en las lecturas de entradas de directorio, ya sean explícitas (por la primitiva readdir) o implícitas (en la resolución de nombres de archivos en la operación sobre el i-nodo lookup).
Las asignaciones de bloques e i-nodos también se han optimizado. Se usan grupos de bloques para agrupar los i-nodos emparentados así como sus bloques de datos. Un mecanismo de preasignación permite también asignar bloques consecutivos a los archivos: cuando debe asignarse un bloque, se reservan hasta 8 bloques consecutivos. De este modo, las asignaciones de bloques siguientes ya se han satisfecho y el contenido de archivos tiende a escribirse en bloques contiguos, lo que acelera su lectura, especialmente gracias a las técnicas de lectura anticipada.
Un sistema de archivos de tipo ext2 debe estar presente sobre un dispositivo físico (disquete, disco duro, ...), y el contenido de este dispositivo se descompone lógicamente en varias partes, como muestra la siguiente figura:

Imagen
4. ext2: Estructura del disco
El sector de arranque contiene el código máquina necesario para cargar el núcleo en el arranque del sistema (p.ej., el lilo), y cada uno de los grupos de bloques se descompone a su vez en varios elementos:

Imagen
5. ext2: Estructura de una partición
Una copia del superbloque: esta estructura contiene las informaciones de control del sistema de archivos y se duplica en cada grupo de bloques para permitir paliar fácilmente una corrupción del sistema de archivos.
Una tabla de descriptores: estos últimos contienen las direcciones de bloques que contienen las informaciones cruciales, como los bloques de bitmap y la tabla de i-nodos; también se duplican en cada grupo de bloques.
Un bloque de bitmap para los bloques: este bloque contiene una tabla de bits: a cada bloque del grupo se le asocia un bit indicando si el bloque está asignado (el bit está entonces a 1) o disponible (el bit está a 0).
Una tabla de i-nodos : estos bloques contienen una parte de la tabla de i-nodos del sistema de archivos.
Bloques de datos: el resto de los bloques del grupo se utiliza para almacenar los datos contenidos en los archivos y los directorios.
Un sistema de archivos se organiza en archivos y directorios. Un directorio es un archivo de tipo particular, que contiene entradas. Cada una de las entradas de directorio contiene varios campos:
El número del i-nodo correspondiente al archivo.
El tamaño de la entrada en bytes .
El número de caracteres que componen el nombre del archivo.
El nombre del archivo.
En el sistema de ficheros ext2, el i-nodo es el bloque de construcción básico; cada fichero y directorio del sistema de ficheros es descrito por un y sólo un i-nodo. Los i-nodos ext2 para cada Grupo de Bloque se almacenan juntos en la tabla de i-nodos con un mapa de bits (bitmap) que permite al sistema seguir la pista de i-nodos reservados y libres.
La tabla de i-nodos se descompone en varias partes: cada parte está contenida en un grupo de bloques. Esto permite utilizar estrategias de asignación particulares: cuando un bloque debe asignarse, el núcleo intenta asignarlo en el mismo grupo que su i-nodo, a fin de minimizar el desplazamiento de las cabezas de lectura/escritura en la lectura del archivo.
De todos los campos que componen un i-nodo (su estructura se encuentra en el código del kernel de Linux, en el fichero linux/Ext2_fs.h), el campo i_block contiene las direcciones de bloques de datos asociados al i-nodo. Esta tabla se estructura según el método clásico de Unix:
Los primeros doce elementos (valor de la constante EXT2_NDIR_BLOCKS) de la tabla contienen las direcciones de bloques de datos;
La posición EXT2_IND_BLOCK contiene la dirección de un bloque que contiene a su vez la dirección de los bloques de datos siguientes;
La posición EXT2_DIND_BLOCK contiene la dirección de un bloque que contiene la dirección de bloques que contienen la dirección de los bloques de datos siguientes;
La posición EXT2_TIND_BLOCK contiene la dirección de un bloque que contiene la dirección de bloques que apuntan a su vez a bloques indirectos.
Este mecanismo de direccionamiento se ilustra a continuación (limitándose a dos niveles de indirección por razones de claridad):

Imagen
6. ext2: i-nodos
Para poder utilizar ext2 no tendremos que recompilar el kernel, ya que todas las distribuciones lo soportan por defecto (y si tuviéramos que compilarlo por añadir otras de las opciones que ya hemos visto, ext2 también viene marcado por defecto). Para formatear una partición ext2 debe tener tipo 83 (Linux), y se formatea con:
mke2fs /dev/hda3
Tras formatear la partición, la montamos con mount:
mount /dev/hda3 /mnt/prueba -t ext2
donde /mnt/prueba es el punto de montaje, y con la opción -t se ha indicado el tipo (a pesar de que los kernels modernos son capaces de reconocer el tipo).
Por último, comentar que se está desarrollando ya la siguiente encarnación del sistema de ficheros ext2, que será conocido con el original nombre de ext3. Éste será un sistema de ficheros transaccional (ver el siguiente punto) pero que será compatible hacia atrás con el ext2 tradicional, es decir, un kernel sin soporte ext3 será capaz de montarlo pero tratándolo como un ext2 normal, perdiendo las capacidades nuevas, mientras que un kernel moderno si que las aprovechará. No hemos llegado a probar este sistema ya que está todavía en una fase bastante experimental y las críticas que hemos leído sobre él no son demasiado buenas, más aún cuando ya existen sistemas transaccionales para Linux bastante estables.
ext3, como su nombre hace suponer, es la evolución de ext2. Sus principales características son que es un sistema de ficheros transaccional (ver siguiente punto), que se puede convertir al vuelo una partición ext2 en ext3 sin detener el sistema, y la compatibilidad hacia atrás con ext2. Es decir, si nuestro sistema operativo soporta ext3, la partición se montará con todas las nuevas características de ext3; si no, se podrá montar como ext2 y utilizarse sin problemas, perdiendo eso sí todas las nuevas prestaciones.
En principio, ext3 iba a ser el sistema de ficheros estándar para las nuevas versiones de Linux, pero ReiserFS ha llegado antes al grado de madurez necesario para utilizarlo en servidores importantes, mientras que ext3 todavía es un proyecto en desarrollo y plagado de errores y fallos. Por otro lado, las pruebas de rendimiento sobre las versiones de desarrollo de ext3 no dan resultados alentadores, resultando un sistema de ficheros lento y no muy eficiente (incluso algo peor que ext2 en algunos casos), que de hecho no es más que un parche sobre un parche ... sobre ext2, con los únicos hechos positivos de ser compatible con sistemas antiguos y ser transaccional. Es por esto que puede que ext3 tenga éxito a la hora de actualizar algún sistema que ya esté en ejecución, pero para sistemas nuevos es mucho mejor olvidarse de ext2 y ext3 y directamente implantarlos sobre ReiserFS, JFS o XFS, que seguidamente pasamos a comentar.
ReiserFS, al igual que JFS y XFS que estudiaremos a continuación, es un sistema de ficheros transaccional que nos asegura que mantiene su contenido en un estado consistente ante una caída del sistema (cuelgue, fallo del suministro eléctrico, etc) en cuestión de segundos y sin necesidad de realizar un fsck. ReiserFS también tiene otras características que lo hacen muy aconsejable en el terreno de los servidores. Antes de pasar a comentar ReiserFS en más profundidad, vamos a estudiar en qué consiste el journalling(mecanismo de seguridad de los sistemas transaccionales).
Cualquier sistema de ficheros permite almacenar, recuperar y manipular datos, almacenados en ficheros y organizados en directorios. Para conseguir esto, el sistema debe almacenar, además de los datos en sí, unas estructuras internas que mantengan la organización de los datos sobre el disco para tenerlos accesibles en todo momento. Estas estructuras de datos internas (como los i-nodos explicados en el punto anterior) son conocidas como meta-datos. El diseño de estos meta-datos es lo que da su personalidad y características (rendimiento, etc) a cada sistema de ficheros.
Los meta-datos no los maneja el usuario ni el administrador del sistema: se encarga el controlador del sistema de ficheros en el núcleo de Linux, que se programa para tratar con especial cuidado todo este enjambre de datos y punteros a datos. Pero para que el sistema funcione correctamente se necesita una cosa: que los meta-datos estén en un estado consistente. Si no, el controlador del sistema de ficheros no entenderá estas estructuras o las malinterpretará, resultando en una perdida o corrupción de los ficheros almacenados en el sistema. Por supuesto, el núcleo del sistema se encarga de que los meta-datos estén siempre en buen estado, pero ante una parada inesperada del sistema (un cuelgue o similar) éste no tendrá tiempo de escribir al disco todos los datos y meta-datos que estuviera almacenando en ese momento o que tuviera en alguna caché interna. De este modo, el sistema quedaría en un estado inestable ya que los meta-datos no han podido ser actualizados consecuentemente.
¿Qué hacer cuando esto ocurra? La solución clásica es utilizar el fsck, un programa que comprueba el estado de los meta-datos de nuestros sistemas de ficheros y los repara si encuentra algún error. Cuando el sistema arranca, comprueba si algún sistema de ficheros no se pudo desmontar correctamente en el último reinicio, y fuerza un análisis con fsck. Por regla general el sistema se reconstruye sin problemas y sin necesitar interacción alguna con el usuario, y puede ser utilizado de forma normal después de ser reparado. El problema es que este análisis y reparación puede llevar MUCHO tiempo, del orden de horas cuando tratamos con dispositivos de varias decenas de gigabytes, horas durante las que nuestro servidor de alta disponibilidad está, en efecto, no disponible.
La solución aportada por los sistemas de ficheros
transaccionales consiste en añadir a los datos de siempre y
sus meta-datos, otra nueva estructura que se encarga de ir apuntando
como en un cuaderno de bitácora las operaciones que se van a
realizar con los meta-datos antes de llevarlas a cabo. Sería,
por decirlo de algún modo, una forma de meta-meta-datos. Así,
si durante el arranque se comprueba que el sistema de ficheros está
en un estado inconsistente, se puede consultar esta bitácora
para ver qué se estaba haciendo cuando el sistema se colgó,
y el análisis y reparación de las estructuras de datos
del disco se centra únicamente en esas zonas del disco. Desde
luego, los datos que estuvieran a medio escribir en el momento del
cuelgue se pierden irremisiblemente, pero si que se consigue volver
al estado consistente inmediatamente anterior en cuestión de
segundos.
ReiserFS 3.6.x, la versión incluida de serie en el kernel 2.4, ha sido diseñado e implementado por Hans Reiser y su equipo de desarrolladores de la empresa Namesys. La filosofía de diseño detrás de ReiserFS es la de que un buen sistema de ficheros debe proporcionarnos un entorno común, o namespace (espacio de nombres), sea cual sea la tarea que vayamos a realizar, y debe ser capaz de cumplir dicha tarea de forma rápida y eficiente. Dicho de otra forma, el sistema de ficheros debe tener las características necesarias para que el usuario no se vea forzado a añadir más capas de software sobre él, por ejemplo, implantando una base de datos cuando estos datos podrían estar directamente sobre el propio sistema de ficheros (por supuesto, esto no siempre tiene sentido ni siempre es posible).
El primer aspecto a optimizar para conseguir este fin es el rendimiento en los ficheros pequeños, un campo que generalmente es dado de lado por la mayoría de los sistemas de ficheros. Sistemas como ext2 o ufs son lentos y desperdician espacio con los ficheros pequeños y con directorios muy llenos, llegando a desaconsejar al usuario utilizarlos e incluso a decir que no es una práctica aconsejable. Por esto, en muchos casos en los que una base de datos no tendría por qué ser necesaria, se pasa a utilizar una en lugar de tener los ficheros directamente en el disco. Esto es lo que tratan de evitar Hans Reiser y su equipo con ReiserFS. Y lo han conseguido. ReiserFS es entre ocho y quince veces más rápido que ext2 al tratar con ficheros de menos de 1k, sin por ello penalizar otros tipos de ficheros (Reiser no es más lento que ext2 con ficheros grandes, en general es siempre algo más rápido).
Otras características interesantes de ReiserFS son:
Soporta todos los atributos para ficheros UNIX vistos en ext2 (es compatible de cara al sistema y el usuario).
La organización interna en árboles B* proporciona un rendimiento general superior al obtenido con otros sistemas de ficheros, para ficheros y directorios de cualquier tamaño, en especial con directorios con muchos ficheros que otros sistemas no tratan bien.
Aprovecha mejor el dispositivo, ya que por un lado no dedica una cantidad fija de sectores para las tablas de i-nodos (se ahorra un 6% de espacio), y por otro se pueden agrupar en un mismo sector ficheros pequeños que no ocupen un sector por sí mismos y colas de ficheros (el último trozo que tampoco ocupa todo un sector), en lugar de utilizar un sector por cada fichero pequeño.
En el futuro se va a abrir el sistema de ficheros a una arquitectura de plug-ins, mediante los cuales el usuario podrá extender fácilmente el sistema con nuevos tipos de ficheros, directorios o atributos.
Se han implantado las bases para adaptar al mundo de los sistemas de ficheros algunas técnicas hasta ahora únicas de las bases de datos.
ReiserFS se organiza internamente en Árboles B*, en lugar de en i-nodos como ext2 o tablas como la FAT de MS-DOS. Los árboles B* son un tipo de árboles balanceados (de ahí la 'B' del nombre), esto es, se van reorganizando para mantener siempre una altura mínima y así garantizar que las búsquedas sobre ellos tendrán siempre unos tiempos medios buenos, sin casos peores muy alejados de esta media. Además, los tipos de datos y la algoritmia asociada a estos árboles están especialmente diseñados para trabajar con los datos sobre disco en lugar de tener todo el árbol cargado en memoria, lo que posibilita tratar con mayor cantidad de datos que otros tipos de árboles.
Los árboles balanceados son muy utilizados en algoritmos de búsqueda por lo rápido que es obtener resultados, siendo los costes de búsqueda, inserción y borrado logarítmicos. Hasta ahora se creía que no se podía implementar un sistema de ficheros sobre árboles y que diera un buen rendimiento, pero ReiserFS ha venido a probar lo contrario.
Sin entrar en mayor detalle sobre el funcionamiento interno de los árboles B*, digamos que son un caso específico de los árboles B, cuyas principales características para un árbol de orden n son:
Cada nodo tiene a lo sumo 2n claves.
Cada nodo, excepto la raiz, tiene como mínimo n claves.
Todo nodo interno (no hoja) tiene m+1 descendientes, siendo m su grado de ocupación:
Para la raiz, 2n ≥ m ≥ 1.
Para el resto de nodos, 2n ≥ m ≥ n.
Todos los nodos hoja se encuentran en el mismo nivel.
El árbol implementará toda la algoritmia necesaria para, ante las operaciones típicas de inserción, borrado y modificación de claves, reorganizarse internamente para cumplir con todos los puntos anteriormente citados.
Un ejemplo del desarrollo de un árbol de este tipo según se le van añadiendo claves sería:

Imagen
7. ReiserFS: Árboles B
Knuth define un árbol-B* como un árbol-B en el cual cada nodo está al menos lleno en 2/3 partes. La inserción del árbol-B* emplea un esquema de redistribución local para retrasar la división hasta que dos nodos hermanos están llenos: entonces los dos nodos se dividen en tres cada uno lleno en 2/3 partes. Este esquema garantiza que la utilización del almacenamiento es al menos del 66%, mientras que solamente requieren una moderada modificación de los algoritmos de mantenimiento. Esto debe ser señalado ya que el incremento en la utilización del almacenamiento tiene el efecto lateral de acelerar la búsqueda ya que la altura del árbol resultante es más pequeña.
El uso de este tipo de árboles como base de ReiserFS, además del aumento en velocidad media respecto a otros sistemas de ficheros, ha traído otras ventajas como la eliminación de los problemas con directorios muy poblados: Con ReiserFS es perfectamente posible tener, por ejemplo, un directorio que contenga 100.000 directorios más sin ninguna pérdida de rendimiento, algo completamente impensable en ext2. Para conseguir tratar directorios con tantos elementos en su interior sin problemas, se utilizan tablas hash para almacenar los nombres de los ficheros y directorios y poder acceder en un tiempo prácticamente lineal. Otra ventaja es que el espacio para las estructuras internas del sistema de ficheros se reserva y libera dinámicamente, en lugar de tener una tabla fija de i-nodos como en ext2, que consume un espacio fijo que puede que no se use (desperdiciando espacio) o puede que se quede corto (desperdiciando espacio también, ya que no se podría añadir nada más al sistema aun que quede espacio libre en la zona de datos).
ReiserFS tiene toda una serie de características para optimizar el uso de ficheros pequeños: por una parte, no se reserva el espacio en bloques de un tamaño fijo (de 1 ó 4k, como hace ext2), sino que es capaz de reservar el espacio exacto que necesita; además, es capaz de almacenar en las hojas del propio árbol B* del sistema las colas de los ficheros. En la jerga de ReiserFS, se denominan tails (colas) a los ficheros más pequeños que un bloque de disco o a la parte final de un fichero que ocupa menos que un bloque (de ahí el nombre de colas).
De esta forma se consiguen dos cosas: por un lado se aumenta en gran medida el rendimiento, ya que con un único acceso al disco se leen los meta-datos del fichero y el propio contenido del fichero, ya que ambos se encuentran en el árbol B* uno al lado del otro; por otro lado, se pueden empaquetar varias colas en una hoja del árbol (si son suficientemente pequeñas) con el consiguiente ahorro de espacio, que llega a ser de hasta un 6% respecto a ext2.
Sin embargo no todo son ventajas: el empaquetado de colas puede hacer mella seriamente en el rendimiento del sistema, ya que si un fichero modifica su tamaño habrá que reorganizar todas las colas que se encuentren empaquetadas con la del fichero que ha sido modificado. Por este motivo, se puede desactivar el empaquetado de colas, quedando a elección del administrador del sistema el utilizar esta característica: por ejemplo, si se sabe que los ficheros de una partición no van a ser modificados frecuentemente (son datos estáticos), sería conveniente activar el empaquetado para ganar espacio, mientras que si sabemos de antemano que en otra partición los datos sufren continuas modificaciones porque se está trabajando con ellos, sería preferible desactivarlo.
En primer lugar, tendremos que compilar el núcleo de Linux con soporte para ReiserFS, si no lo hemos hecho ya. La opción para añadir soporte ReiserFS se encuentra bajo el menú File Systems.
Para trabajar con las particiones ReiserFS, disponemos de una serie de herramientas similares a las que hay para ext2 o cualquier otro sistema de ficheros. Para formatear una partición, tenemos mkreiserfs:
vjaguilar:~# mkreiserfs
<-------------mkreiserfs, 2001------------->
reiserfsprogs 3.x.0j
Usage: mkreiserfs [ -f ] [ -h tea | rupasov | r5 ] [ -v 1 | 2] [ -q ] device [block-count]
vjaguilar:~# mkreiserfs /dev/hda1
mkreiserfs puede recibir varios comandos. Uno de los más útiles es -h, con el que se indica la función de hashing con la que se codificarán internamente los nombres de ficheros y directorios. Hay tres funciones soportadas, r5, tea y rupasov, siendo r5 la opción por defecto (y la más segura). Jugando con esta opción podemos conseguir particiones más rápidas, aún que algunas de estas funciones (excepto r5) no se ha demostrado todavía que sean seguras al 100% y no haya colisiones de nombres.
Para montar una partición, se utiliza el comando mount de forma normal:
vjaguilar:~# mount /dev/hda1 /mnt/reiser -t reiserfs
Cabe señalar que el kernel 2.4.x es capaz de identificar el tipo de partición automáticamente, por lo que el parámetro -t pasa a ser opcional (si bien es recomendable utilizarlo).
Además, ReiserFS soporta el redimensionado de una partición en caliente, es decir, sin necesidad de desmontarla antes de redimensionarla y luego volverla a montar (por supuesto, siempre que quede espacio libre en el dispositivo físico). Esto es muy útil, por ejemplo, si por debajo del sistema de ficheros hemos instalado LVM y acabamos de ampliar una partición virtual. Para redimensionar una partición se utiliza resize_reiserfs:
vjaguilar:~# resize_reiserfs
<-------------resize_reiserfs, 2001------------->
reiserfsprogs 3.x.0j
Usage: resize_reiserfs [-s[+|-]#[G|M|K]] [-fqv] device
El redimensionado se controla con la opción -s, seguida del tamaño que vaya a tener la partición en gigabytes ('G'), megabytes ('M') o kilobytes ('K'). Si se utilizan los modificadores '+' o '-', lo que se hace es añadir o quitar el espacio indicado a la partición. Si se omite por completo la opción -s, se redimensionará la partición para ocupar todo el espacio libre en el dispositivo.
xfs y jfs son dos ejemplos de tecnologías de nuevo desarrollo o bien adaptadas de otro sistema UNIX a Linux y posteriormente donadas a la comunidad del software libre. También son ejemplos del interés que el software libre está generando en el mundo de las grandes compañías informáticas, ya que estos sistemas han sido desarrollados por SGI e IBM, respectivamente.
Ambos son sistemas completamente compatibles UNIX (atributos, etc) y transaccionales, con características en general muy similares a ResierFS, si bien superiores en ciertos aspectos (soporte de ACLs, etc). Pero presentan dos problemas:
A pesar de que ambos sistemas ya van por la versión 1.x, parece ser que aún no han llegado al grado de estabilidad (ni de implantación en el mundo Linux) de ReiserFS.
Ninguno de los dos ha sido integrado todavía en el kernel oficial de Linux. Para probarlos, es necesario parchear el núcleo.
Las noticias de que estos dos sistemas iban a ser donados a la comunidad Linux fueron recibidas en su día con gran entusiasmo, pero desde entonces se han calmado los ánimos. La mayor prueba de esto puede ser el hecho de que aún no se haya aceptado ninguno de los dos para la distribución oficial del kernel, ni para la actual versión 2.4 ni parece ser que tampoco para la serie de desarrollo 2.5 que verá la luz dentro de poco.
Otro ejemplo es que tanto RedHat como Mandrake, que habían anunciado que sus próximas versiones soportarían JFS como sistema de ficheros nativo desde la instalación del sistema (y que actualmente ya soportan ReiserFS), han hecho pública recientemente su decisión de no introducirlo finalmente por los malos resultados que ha dado en ciertos tests de estabilidad. Si bien los errores detectados han sido durante pruebas extremas, y aún así son errores que sólo se pueden dar en casos muy concretos bajo condiciones muy determinadas, el hecho de que estos problemas existan ha sido más que suficiente para que ambas distribuciones decidan hacer marcha atrás con en su decisión.
Aún así, son dos tecnologías que no hay que perder de vista y que habrá que tener en cuenta en un futuro, cuando hayan alcanzado el grado de estabilidad comercial que sus desarrolladores, IBM y SGI ayudados por la comunidad del software libre, seguro conseguirán alcanzar.
Ahora que ya conocemos las diversas técnicas para salvaguardar los datos de nuestros discos duros y posibilitar el cambio de discos en caliente, y los distintos sistemas con los que organizar los sistemas de archivos, se nos presenta otro problema: como ya se avanzó en los primeros capítulos, vamos a conseguir la alta disponibilidad a través de la replicación de servidores, capaces de trabajar en paralelo como uno sólo e incluso sustituirse unos a otros en sus funciones. Esto implica que los datos que tengan que servir o procesar deben estar disponibles para todos y cada uno de nuestros servidores, pero, ¿cómo conseguirlo? Nuestra intención es crear varios servidores, réplicas exactas unos de otros, que sirvan todos el mismo contenido, tendremos que encontrar alguna forma de realizar estas réplicas automáticamente, de forma que para el usuario (en este caso, los desarrolladores o encargados de contenidos) el cluster se comporte como un único ordenador, en el que ellos copian (o trabajan) en un único lugar los ficheros, y el software de control del cluster internamente se encargue de hacer llegar una copia a cada uno de los servidores que lo componen.
A este respecto tenemos dos estrategias: la replicación física de archivos, en la que cada servidor tendrá una copia de todos los datos en su disco duro; y la distribución de los datos mediante sistemas de archivos distribuidos, en los que tendremos un servidor de ficheros y el resto de equipos del cluster accederán a sus contenidos por la red. Cada estrategia tendrá sus ventajas y desventajas, que ahora estudiaremos.
La alternativa más primitiva para la distribución del contenido a servir a todos los equipos de nuestro cluster es la replicación (automática o manual) de los ficheros en todos los ordenadores. Por ejemplo, una forma de replicar los archivos sería tener en un servidor FTP central el contenido a replicar en los clientes, y que estos a una hora determinada lancen un script (programado en el cron del sistema) que se encargue de conectarse al servidor y descargar todo el contenido.
Aquí veremos un novedoso protocolo que optimizará en gran medida la cantidad de datos a transmitir por la red y, en consecuencia, el tiempo necesario para realizar la sincronización.
rsync es un programa para copiar archivos entre dos sistemas UNIX que, utilizando un ingenioso algoritmo propio, para los archivos que ya existan en ambas máquinas es capaz de enviar de un equipo a otro tan sólo aquellas partes de los archivos que hayan sido modificadas, sincronizando de esta forma los contenidos de los dos equipos. Esta característica hace a rsync especialmente apropiado (frente a otros métodos como rcp o ftp) para mantener al día copias idénticas de directorios entre equipos geográficamente distantes (p.ej., mirrors, réplicas de servidores, etc).
Con rsync podemos:
Copiar o sincronizar ficheros, directorios o sistemas de archivos enteros, manteniendo si fuera necesario enlaces, permisos, fechas, etc.
Redireccionar todo el tráfico a través de ssh para cifrarlo.
Permitir un acceso anónimo para que terceras personas puedan hacer mirror de nuestras páginas.
El hecho de copiar por la red únicamente las diferencias entre los ficheros sería trivial si en una misma máquina tuviéramos ambos ficheros: utilizando diff podemos calcular las diferencias y enviarlas. El problema es que no tenemos las dos versiones del fichero en la misma máquina, tenemos una versión en cada máquina, y lo que queremos evitar es enviar todo el fichero de un sitio al otro.
El protocolo que utiliza rsync para transferir sólo las modificaciones de los archivos está descrito en su página web, y es bastante ingenioso. A grandes rasgos:
El equipo receptor ('A') divide el fichero en bloques de un tamaño fijo que no se solapan entre sí.
Se dispone de dos algoritmos de cálculo de CRC (en la web del rsync se detallan minuciosamente ambos algoritmos):
Uno muy rápido (X) pero no exacto, aún que asegura que nunca dará un falso negativo (un bloque con CRC correcto siempre será evaluado como correcto; uno incorrecto puede que sea evaluado como correcto). Además, este algoritmo tiene la característica de que el resultado del bloque x+1 se puede calcular rápidamente a partir del resultado del bloque x.
Otro más lento (Y'), pero que sí que es capaz de discriminar siempre si el CRC es correcto o no.
Se calculan los valores de ambos algoritmos sobre los bloques del fichero, y se envían al otro equipo.
El equipo emisor ('B') busca en su fichero bloques del tamaño fijado para los que coincida el algoritmo X: Si el CRC difiere en el fichero local y el remoto, no hace falta retransmitir este bloque; si coincide, se analiza con el algoritmo Y.
Si con el algoritmo Y también coincide, entonces el bloque ha cambiado y habrá que transmitirlo. Se envía al otro equipo la información precisa para reconstruir el fichero, bien como una referencia a otro bloque del fichero o como datos puros.
De esta forma, se consigue disminuir en gran medida la cantidad de datos a transmitir entre las dos máquinas, algo muy a tener en cuenta si la conexión entre los equipos es lenta o si el número de ficheros a sincronizar es grande.