Archive of May 2010


Fri 21 May

El guardián entre el centeno

Si no fuera porque su autor es J. D. Salinger podría jurar que lo escribí yo :P

Pensé que encontraría trabajo en una gasolinera poniendo a los coches aceite y gasolina. Pero la verdad es que no me importaba qué clase de trabajo fuera con tal de que nadie me conociera y yo no conociera a nadie. Lo que haría sería hacerme pasar por sordomudo y así no tendría que hablar. Si querían decirme algo, tendrían que escribirlo en un papelito y enseñármelo. Al final se hartarían y ya no tendría que hablar el resto de mi vida. Pensarían que era un pobre hombre y me dejarían en paz. Yo les llenaría los depósitos de gasolina, ellos me pagarían, y con el dinero me construiría una cabaña en algún sitio y pasaría allí el resto de mi vida. La levantaría cerca del bosque, pero no entre los árboles, porque quería ver el sol todo el tiempo. Me haría la comida, y luego, si me daba la gana de casarme, conocería a una chica guapísima que sería también sordomuda y nos casaríamos. Vendría a vivir a la cabaña conmigo y si quería decirme algo tendría que escribirlo como todo el mundo. Si llegábamos a tener hijos, los esconderíamos en alguna parte. Compraríamos un montón de libros y les enseñaríamos a leer y escribir nosotros solos.

¡Grande! Toca leérmelo otra vez, el ritual... una vez al año.


Fri 14 May

MogileFS, sistema de ficheros distribuido a través de la red

MogileFS es un sistema de ficheros distribuido que funciona en activo-activo o dicho de otra forma, es un RAID a través de la red. Algunas de sus características:

  • No requiere de ningún parche en kernel ni módulos especiales ya que funciona completamente a nivel de aplicación.

  • No hay ningún SPOF(single point of failure) ya que todos los elementos que forman MogileFS pueden estar distribuidos en múltiples máquinas.

  • Se pueden especificar clases de ficheros. Las clases definirán cuantas réplicas pueden llegar a tener. Por ejemplo, un fichero de log poco importante puede ser suficiente con dos réplicas, pero los datos bancarios de la empresa necesita 10 réplicas. Dependiendo de la clase, MogileFS hará automáticamente las réplicas que sea necesario.

  • No importa el sistema de ficheros que tengamos debajo. Puede ser Ext3, Ext4, XFS…

Está compuesto por varios componentes, cada uno de los cuales puede estár en diferentes máquinas y replicado tantas veces como quieras:

  • Base de datos: MySQL ;) Se almacenan los metadatos y los namespaces y debería estar en HA o tendremos un punto de fallo.

  • Tracker (mogilefsd): es el proceso encargado de recibir las peticiones del cliente y gestionarlas.

  • Nodos de almacenamiento (mogstored): donde los datos se guardan físicamente. Estos nodos son simplemente servidores HTTP que recibe peticiones PUT, GET, DELETE

Vamos con la instalación ;)

1- Instalar dependencias:

 # apt-get install debhelper dpkg-dev fakeroot
 # apt-get install libio-aio-perl libdanga-socket-perl libnet-netmask-perl libstring-crc32-perl
 # apt-get install gearman-server libgearman-client-perl libgearman-client-async-perl

2- Instalar perlbal:

 # cd /usr/src
 # svn co http://code.sixapart.com/svn/perlbal
 # cd perlbal/trunk
 # dpkg-buildpackage -rfakeroot
 # dpkg -i ../libperlbal-perl_1.70-1_all.deb

3- Instalar mogileFS:

 # cd /usr/src
 # svn co http://code.sixapart.com/svn/mogilefs
 # cd mogilefs/trunk
 # bin/build-all-debian.sh
 # dpkg -i ./api/perl/libmogilefs-perl_1.00-1_all.deb
 # bin/build-all-debian.sh
 # dpkg -i packages/mogilefsd_1.00-2_all.deb packages/mogilefs-utils_0.01-1_all.deb packages/mogstored_1.00-2_all.deb

4- Crear base de datos:

 $ mysql -u root -p
  mysql> create database mogilefs;
  mysql> grant all on mogilefs.* to 'mogile'@'%' identified by 'mogilepw';
  mysql> flush privileges;
  mysql> quit

5- Crear esquema:

 $ /usr/bin/mogdbsetup --yes --dbname=mogilefs --dbuser=mogile --dbpassword=mogilepw

Al menos tiene que existir un tracker, se puede configurar en el mismo nodo de storage:

zimbra:/var/mogdata# cat /etc/mogilefs/mogilefsd.conf 
\#daemonize = 1
\# Database connection information
db_dsn = DBI:mysql:mogilefs:host=127.0.0.1
db_user = mogile
db_pass = mogilepw
\# IP:PORT to listen on for mogilefs client requests
listen = 127.0.0.1:7001
\# Optional, if you don't define the port above.
conf_port = 7001
\# Number of query workers to start by default.
query_jobs = 10
\# Number of delete workers to start by default.
delete_jobs = 1
\# Number of replicate workers to start by default.
replicate_jobs = 5
\# Number of reaper workers to start by default.
\# (you don't usually need to increase this)
reaper_jobs = 1
\# Number of fsck workers to start by default.
\# (these can cause a lot of load when fsck'ing)
\#fsck_jobs = 1
\# Minimum amount of space to reserve in megabytes
\# default: 100
\# Consider setting this to be larger than the largest file you
\# would normally be uploading.
\#min_free_space = 200
\# Number of seconds to wait for a storage node to respond.
\# default: 2
\# Keep this low, so busy storage nodes are quickly ignored.
\#node_timeout = 2
\# Allow replication to use the secondary node get port,
\# if you have apache or similar configured for GET's
\#repl_use_get_port = 1

Una vez hecho, se añaden los hosts de almacenamiento:

mogadm --trackers=127.0.0.1:7001 host add hostmolon1 --ip=10.10.0.122 --port 7500 --status=alive
mogadm --trackers=127.0.0.1:7001 host add hostmolon2 --ip=10.10.0.122 --port 7500 --status=alive

Se listan para ver si es correcto:

zimbra:/var/mogdata# mogadm --trackers=127.0.0.1:7001 host list
hostmolon1 [1]: alive
  IP: 127.0.0.1:7500

hostmolon2 [2]: alive
  IP: 10.10.0.122:7500

Se añaden los dispositivos de almacenamiento:

mogadm --trackers=127.0.0.1:7001 device add hostmolon1 1
mogadm --trackers=127.0.0.1:7001 device add hostmolon2 2

El último valor es el id, por lo tanto en hostmolon1 habrá que crear la carpeta:

/var/mogdata/dev1/

y en hostmolon2:

/var/mogdata/dev2/

El propietario de la carpeta tiene que ser “mogstored”. Generalmente lo que se suele hacer es montar discos duros sata directamente en dichas carpetas.

Comprobamos que tira:

zimbra:/var/mogdata# mogadm --trackers=127.0.0.1:7001 device list
hostmolon1 [1]: alive
                   used(G) free(G) total(G)
  dev1: alive 1.539 5.567 7.106 

hostmolon2 [2]: alive
                   used(G) free(G) total(G)
 dev2: alive 2.502 4.604 7.106

Para poder catalogar los ficheros es necesario definir dos cosas, dominios y clases. El dominio sería como una separación de un grupo de ficheros. El dominio a su vez puede tener definido un número n de clases.

mogadm --trackers=127.0.0.1:7001 domain add irontec
mogadm --trackers=127.0.0.1:7001 class add irontec miclase

Y ya podemos añadir ficheros. Estos se deben gestionar mediante API, ya sea con PHP, Python o cualquier otro que nos guste más. Esto es así porque los ficheros no se escriben en la carpeta como si se tratase de un sistema de ficheros normal, si no que hay que hacerlo a través de un tracker para que este se encarge de las réplicas. Nosotros, en lugar de hacer uso de estas APIs, tiraremos de la herramienta de consola mogtool.

mogtool inject /boot/vmlinuz-2.6.26-2-686 vmlinuz --trackers=127.0.0.1:7001 --domain=irontec

Buscarlos:

zimbra:/var/mogdata# mogtool locate vmlinuz --trackers=127.0.0.1:7001 --domain=irontec
http://127.0.0.1:7500/dev2/0/000/000/0000000005.fid [^]
http://10.10.0.122:7500/dev10/0/000/000/0000000005.fid [^]
0000002 paths found

Y extraerlos:

zimbra:/var/mogdata# mogtool extract vmlinuz /tmp/test --trackers=127.0.0.1:7001 --domain=irontec
Fetching piece 1...
        Trying http://10.10.0.122:7500/dev10/0/000/000/0000000005.fid... [^]
Done.
zimbra:/var/mogdata# md5sum /tmp/test /boot/vmlinuz-2.6.26-2-686 
f6690b230573b7f791e84c33adf0804c /tmp/test
f6690b230573b7f791e84c33adf0804c /boot/vmlinuz-2.6.26-2-686

Todo esto tiene una mejora importante sobre los RAID por software o hardware. El RAID por si mismo te salva si se rompe alguno de los discos, ¿pero que pasa si se rompe el servidor? ;) Nada mejor que un RAID distribuido a través de la red para evitar estos problemas.


Fri 7 May

Introducción al mundo noSQL

NoSQL es la nueva palabra de moda. Una solución pensada para el almacenamiento de gran cantidad de datos para su posterior uso. Esto ya nos suena, una base de datos, pero realmente, ¿cual es la diferencia?

NoSQL es un término usado en informática para agrupar una serie de almacenes de datos no relacionales que no proporcionan garantías ACID. Normalmente no tienen esquemas fijos de tablas ni sentencias "join".Wikipedia

La diferencia ya es clara, no es una base de datos como las que conocemos actualmente, es bastante más peculiar y tira por tierra bastantes de los conceptos y formas de trabajar a los que estamos acostumbrados. Y aunque el termino en si sea nuevo, la teoría tiene sus años, bastantes más que las bases de datos relacionales, pero ahora vuelve con fuerza para salvarnos la vida en algunos casos concretos, evitando problemas de escalabilidad o rendimiento a costa de algo de fiabilidad.

Hasta ahora el cuello de botella siempre ha sido el acceso a los datos y la forma de evitarlo la escalabilidad vertical, esto es, añadiendo más memoria, procesamiento y discos rápidos. Pero todo escalado vertical tiene un límite, y es el que pone nuestro bolsillo y la propia tecnología. Por otro lado, las bases de datos relacionales no suelen tender a escalar bien horizontalmente.

  • Normalización o desnormalización
  • Replicación asíncrona y por lo tanto problemática
  • ¿Cómo distribuyes la carga de escrituras?
  • ¿Transacciones distribuidas? Uff... no!

Es importante tener en cuenta que no es un reemplazo a las bases de datos SQL que llevamos usando desde hace años, si no que un complemento que nos ayudará en casos particulares, por lo tanto tened en mente que noSQL es en realidad no(tOnly)SQL :)

A modo introducción vamos a ver el funcionamiento de MongoDB, una de las que más me han impresionado por su facilidad de uso y rapidez.

MongoDB agrupa los datos documentos (y sub-documentos). Esto es, objetos sin ningún tipo de esquema fijo, como por ejemplo un array de PHP o una tupla de Python. Los datos se guardan en BSON (JSON binario) y permite consultas algo más complejas que algunas otras soluciones NoSQL.

BSON [bee · sahn], short for Binary JSON, is a binary-encoded serialization of JSON-like documents. Like JSON, BSON supports the embedding of documents and arrays with in other documents and arrays. BSON also con­tains extensions that allow representation of data types that are not part of the JSON spec. For example, BSON has a Date type and a BinData type.

Además soporta el uso de índices compuestos, simples o geo-espaciales de dos dimensiones, auto particionado de datos y replicación master-slave entre otras muchas cosas. Importante para los desarrolladores, ¡existe módulo de PHP! ;) No en vano, la gente lo empieza a llamar el MySQL de NoSQL.

Después de esta pequeña introducción vamos a jugar un poco con MongoDB :) Para ello nos bajamos el binario de http://www.mongodb.org/display/DOCS/Downloads La instalación es tan sencilla como descomprimir y lanzar el binario del servidor (mongod):

bin # ./mongod
./mongod --help for help and startup options
Fri May  7 21:16:00 Mongo DB : starting : pid = 307 port = 27017 dbpath = /data/db/ 
master = 0 slave = 0  64-bit 
Fri May  7 21:16:00 db version v1.4.1, pdfile version 4.5
Fri May  7 21:16:00 git version: nogitversion
Fri May  7 21:16:00 sys info: Darwin iMac-Punisher.local 10.3.0 Darwin Kernel Version 10.3.0: Fri 
Feb 26 11:58:09 PST 2010; root:xnu-1504.3.12~1/RELEASE_I386 i386 BOOST_LIB_VERSION=1_42
Fri May  7 21:16:00 waiting for connections on port 27017
Fri May  7 21:16:00 web admin interface listening on port 28017

A primera vista ya vemos datos interesantes. Los datos se van a guardar en la ruta /data/db, que debemos crear con antelación o indicar otra mediante parámetro, y que el puerto de escucha será 27017. Si no le indicamos nada, el demonio se lanzará en foreground y la consola se quedará "bloqueada". Podremos parar el demonio con un simple Control+C.

La consola para empezar a usar mongoDB se llama "mongo", que se encuentra en la misma ruta de binarios que el resto.

bin $ ./mongo
MongoDB shell version: 1.4.1
url: test
connecting to: test
type "help" for help
\> 

Vemos las bases de datos existentes y nos creamos la nuestra propia:

> show dbs
admin
local
\> use midb
switched to db midb

Como veis no es necesario crearla antes de usarla, simplemente dile que la quieres usar y si no existe la creará. Ahora vamos a meter una serie de datos:

\>a = { nombre : "Miguel Angel", apellidos : "Nieto Salazar", edad : "27", localidad : "Barakaldo" }
{
    "nombre" : "Miguel Angel",
    "apellidos" : "Nieto Salazar",
    "edad" : "27",
    "localidad" : "Barakaldo"
}
\>b = { nombre : "Estibaliz", apellidos : "Sanchez", Nacimiento : "Logroño", estudio : "Comercio" } 
{
    "nombre" : "Estibaliz",
    "apellidos" : "Sanchez",
    "Nacimiento" : "Logroño",
    "estudio" : "Comercio"
}
\> db.coll.save(a);
\> db.coll.save(b);

Y como ya dije anteriormente, no existen tablas ni esquemas, añades datos según lo vas necesitando, sin necesidad de seguir un patrón o estando obligado a rellenar unos datos en concreto. Todos estos documentos se guardarán en la colección "coll" dentro de la base de datos midb. Una colección no es más que una agrupación de datos BSON que estos a su vez se pueden agrupar en namespaces. Por ejemplo blog.post y blog.comments son dos colecciones que cuelgan del mismo namespace. Esto es solamente a nivel organizativo para el usuario, internamente no afecta en nada.

Una vez que tenemos los datos introducidos, podemos hacer consultas:

\> db.coll.find({edad:"27"});
{ "_id" : ObjectId("4be029e11967ab80a305e1f0"), "nombre" : "Miguel Angel", "apellidos" : "Nieto
Salazar", "edad" : "27", "localidad" : "Barakaldo" }
\> db.coll.find({Nacimiento:"Logroño"});
{ "_id" : ObjectId("4be029e41967ab80a305e1f1"), "nombre" : "Estibaliz", "apellidos" : "Sanchez", 
"Nacimiento" : "Logroño", "estudio" : "Comercio" }

Facil, sencillo y para toda la familia. Cuando lo ves así piensas, "esto es lo lógico, el almacenamiento de datos tendría que ser así siempre" :)

También podemos almacenar datos binarios, esto es, ficheros. Puedes olvidarte de todo lo que has sufrido con los tipos BLOB en otras bases de datos. MongoDB guarda los ficheros en la base de datos haciendo uso de las especificaciones GridFS. BSON solo permite almacenar datos de hasta 4 megas, para saltarnos esta limitación GirdFS divide de forma transparente los ficheros en grupos de documentos (lo cual también nos permitira buscar por rangos de bytes).

Listamos el contenido, de momento no hay ningún fichero:

$ mongofiles list
connected to: 127.0.0.1

Añadimos un fichero, en este caso un documento PDF:

$ mongofiles put /Users/punisher/Documents/High\ Performance\ MySQL\ Second\ Edition.pdf 
connected to: 127.0.0.1
added file: { _id: ObjId(4be46d25a409d73217ca227b), filename: "/Users/punisher/Documents/High
Performance MySQL Second Edition.pdf", length: 5904835, chunkSize: 262144, uploadDate: new 
Date(1273261349209), md5: "082f8333d45d1e3b7bce6323d0da59f0" }
done!

Listamos de nuevo:

$ mongoflist
connected to: 127.0.0.1
/Users/punisher/Documents/High Performance MySQL Second Edition.pdf 5904835

Lo sacamos de la base de datos a nuestro sistema de ficheros:

$ mongofiles get "/Users/punisher/Documents/High Performance MySQL Second Edition.pdf"
connected to: 127.0.0.1
done write to: /Users/punisher/Documents/High Performance MySQL Second Edition.pdf

No lo voy a negar, estoy enamorado de mongoDB y del concepto noSQL. Olvídate de las relaciones, de optimizar queries, normalizar tablas... dedicate a lo que importa, guardar y sacar datos. Una revolución para los que nos guiamos por la filosofía KISS (Keep it simple, stupid)