lunes, 31 de marzo de 2014

HBase - Importar Datos (SQOOP)


Probablemente una de las primeras cosas que tengamos que hacer tras implantar un sistema de Big Data, en mi caso Cloudera 4.8.1, será importar todos o buena parte de los datos de nuestros diversos sistemas de información.

Cloudera nos facilita diversos proyectos dentro de su ecosistema con los que trabajar y volcar esta información.


El primero que me gustaría presentaros es Apache HBase. Se trata de la "base de datos" de Hadoop, un sistema de almacenamiento de datos totalmente distribuido y escalable. Digo "base de datos" porque HBase realmente es lo que se conoce como una NoSQL, es decir, una base de datos no relacional en las que los datos almacenados no requieren de una estructura fija como hasta ahora ha venido sucediendo con las tradicionales bases de datos relacionales (RDBMS).

La aparición de estas NoSQL surgió de la necesidad por las grandes compañías (Google, Amazon...) de premiar el rendimiento frente a la coherencia de los datos, tarea en la que las tradicionales bases de datos invierten una gran cantidad de tiempo y recursos del sistema.

Así pues, podríamos decir que una NoSQL encaja a la perfección cuando la finalidad de nuestro proyecto es el almacenamiento y tratamiento de enormes tablas, con enormes me refiero a tablas de billones de filas por millones de columnas.

Lógicamente no vamos a tener que introducir toda esta cantidad de información a mano, sería un infierno hacerlo, así que llegados a este punto surge la necesidad de conocer como volcar toda o parte de la información almacenada en un tercer sistema.

En esta ocasión voy a explicar como importar datos de una MySQL pues seguramente muchos de los orígenes de nuestras fuentes de datos vayan a ser éstas.
Aquí es donde hace acto de presencia otra de las herramientas del ecosistema, SQOOP, herramienta que nos facilitará esta tarea y que a su vez utiliza mapreduce, una de las dos características que definen a un sistema Hadoop, para la importación de los datos.

Sqoop también permite la acción contraria, exportación de los mismos.

Antes de empezar deberemos asegurarnos que existen las variables de entorno $HBASE_HOME y $SQOOP_HOME, aunque bueno, en caso de no existir alguna de ellas el comando sqoop fallará, teniendo que definirlas:
 # export $HBASE_HOME = /opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30/lib/hbase
 # export $SQOOP_HOME = /opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30/lib/sqoop

Caso 1: Importar Tabla con Primary Key

Este ejemplo importará todos los datos, filas y columnas, de la tabla <tableMySQL> de la base de datos <db> ubicada en el servidor <host> en la <tableHBase> y column-family <cf1>. Además, el usuario <user> con contraseña <passwd> deberá tener configurados los privilegios/permisos necesarios.
 # $SQOOP_HOME/sqoop import --connect jdbc:mysql://<host>/<db> --username <user> --password <passwd> --table <tableMySQL> --hbase-table <tableHBase>  --column-family <cf1> --hbase-create-table
El argumento hbase-create-table creará la tabla <tableHBase> en caso de no existir.

Caso 2: Importar Tabla sin Primary Key

Ahora bien, si la tabla origen carece de primary key, deberemos decirle nosotros a Sqoop como deberá construir la rowkey de la tabla HBase.

Para ello usaremos el argumento hbase-row-key que constará del nombre de una o más columnas de la tabla MySQL, <column1,column2,columnN>.
$SQOOP_HOME/sqoop import --connect jdbc:mysql://<host>/<db> --username <user> --password <passwd> --table <tableMySQL> --hbase-table <tableHBase> --column-family <cf1> --hbase-row-key <column1,column2,columnN> --hbase-create-table
En caso de haber coincidencias en la rowkey NO se crearán diferentes entradas/filas en HBase, sino una única, pues la rowkey será idéntica. Otra cosa es que HBase almacenará el valor previo como lo que se conoce como version.

El número de versiones para cada celda que por defecto HBase almacena es 3.

De paso sea dicho, NO  penséis en modificar este valor a digamos infinito para tener un histórico de los valores. Esto es totalmente desaconsejable y afectará al rendimiento de vuestra plataforma.

Para solucionarlo o bien pensamos en incluir la fecha como parte de nuestra rowkey o implementamos lo que se conoce como time-series database. Un ejemplo de esto último sería el proyecto de monitorización OpenTSDB

Caso 3: Importar algunas columnas

No siempre querremos importar todas las columnas de una tabla así que podremos limitarlo gracias al argumento columns de la siguiente manera:
$SQOOP_HOME/sqoop import --connect jdbc:mysql://<host>/<db> --username <user> --password <passwd> --table <tableMySQL> --hbase-table <tableHBase> --columns <column1,column5,column10> --column-family <cf1> --hbase-row-key <column1> --hbase-create-table
Ahora bien, deberemos asegurarnos que la columna que vayamos a usar como rowkey deba estar incluida en la lista de columnas a importar, de otro modo Sqoop finalizará satisfactoriamente, pero no grabará nada en HBase.

Caso 4: Consulta SQL

Sqoop también nos permite importar el resultado de una sentencia SQL. Esto lo conseguiremos gracias al argumento query que nos permitirá definir dicha consulta tal cual lo hacemos en el lenguaje habitual de SQL. En caso de declararlo ya no será necesario incluir el argumento table.

La única limitación o más bien obligación que existe es incluir en ella la siguiente cláusula: where $CONDITIONS.
 # $SQOOP_HOME/sqoop import --connect jdbc:mysql://<host>/<db> --username <user> -password <passwd> --query 'select * from tablaMySQL where $CONDITIONS and column5 > 10' --split-by <column1> --hbase-create-table --hbase-table <tableHBase> --hbase-row-key <column1,column5,column10> --column-family <cf1>
Bueno, en caso de usar comillas dobles para declarar el argumento query debido a que en la consulta SQL hagamos uso de las comillas simples, deberemos escapar el carácter '$' de $CONDITIONS => \$CONDITIONS. Por ejemplo:
 # $SQOOP_HOME/sqoop import --connect jdbc:mysql://<host>/<db> --username <user> -password <passwd> --query "select * from tablaMySQL where \$CONDITIONS and column5 > 10 and column1 like ''" --split-by <column1> --hbase-create-table --hbase-table <tableHBase> --hbase-row-key <column1,column5,column10> --column-family <cf1>




Pues hasta aquí llega este breve tutorial acerca de cómo importar datos de una MySQL en HBase.

En caso de necesitar mayor información os remito a la siguiente guía: User Sqoop Guide; O al libro Apache Sqoop Cookbook (100% recomendable).

miércoles, 5 de marzo de 2014

[HowTo] Create a Simple Hadoop Cluster with VirtualBox

Entorno Propuesto
Me gustaría compartir con todos vosotros este gran tutorial que publicaron en el blog de Cloudera sobre cómo crear en CentOS un cluster de Hadoop gracias a su propia herramienta, Cloudera, y VirtualBox.


Este entorno que nos proponen podría llegar a servirnos como un buenísimo entorno de desarrollo o preproducción.

Lógicamente depende un poco de las características de las máquinas que dispongamos, pero yo haría algunas modificaciones:
  • Hadoop1: 2 cpu's.
  • Hadoop2, Hadoop3 y Hadoop4: 4 GB RAM.
  • En caso de ser posible aumentar en todos ellos los espacios en disco, yo me iría a los 80GB, al menos en el Hadoop1.

sábado, 1 de marzo de 2014

[HowTo] Instalar R, RHadoop y RStudio



En la presente entrada trataré de explicar cómo instalé R, RHadoop y RStudio sobre un sistema Linux (Centos 6.4) de 64 bits con Cloudera 4.8.1 previamente instalado.

Lo primero quiero compartir estos dos enlaces que me fueron de gran utilidad para llevar a cabo esta tarea, aunque hay cosas que considero que no son correctas o no están lo suficientemente documentadas (espero conseguirlo yo :P).
  • http://ashokharnal.wordpress.com/2013/08/25/installing-r-rhadoop-and-rstudio-over-cloudera-hadoop-ecosystem/
  • https://github.com/cloudera/poisson_sampling/blob/master/INSTALL.md

¡¡¡ IMPORTANTE !!! Con la versión 7 de Java no fuí capaz de instalar nada. La versión utilizada fue JRE 6 Update 31 (1.6.0.31).
 # java -version
   java version "1.6.0_30"
   OpenJDK Runtime Environment (IcedTea6 1.13.1) (rhel-3.1.13.1.el6_5-x86_64)
   OpenJDK 64-Bit Server VM (build 23.25-b01, mixed mode)

Actualización 2014-04-09: Con Cloudera 5 y JRE 1.7u45 SÍ conseguí instalar R, RHadoop y RStudio siguiendo estos mismos pasos, eso sí, primero debí asegurarme que los enlaces existentes en /etc/alternatives/ y relativos a Java apuntaran correctamente a esta versión en concreto y muy recomendable que la variable de entorno CLASSPATH no estuviera definida.
  1. Instalar R.
    Habilitamos la descarga de paquetes del repositorio epel  (Extra Packages for Enterprise Linux)
     # rpm -ivh http://mirror.chpc.utah.edu/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 
    Ahora ya podemos instalar R tras actualizar nuestros repositorios:
     # yum update
       ...
     # yum -y --enablerepo=epel install R R-devel
  2. Iniciar R.
  3. Un primer paso:
     # R CMD javareconf
       Java interpreter : /usr/java/default/jre/bin/java
       Java version     : 1.6.0_31
       Java home path   : /usr/java/default
       Java compiler    : /usr/java/default/bin/javac
       Java headers gen.: /usr/java/default/bin/javah
       Java archive tool: /usr/java/default/bin/jar
    
       trying to compile and link a JNI progam 
      detected JNI cpp flags    : -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux detected JNI linker flags : -L/usr/lib/jdk1.6.0_31/jre/lib/amd64/server -ljvm
       gcc -m64 -std=gnu99 -I/usr/include/R -DNDEBUG -I/usr/java/default/include -I/usr/java/default/include/linux -I/usr/local/include    -fpic  -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic  -c conftest.c -o conftest.o
       gcc -m64 -std=gnu99 -shared -L/usr/local/lib64 -o conftest.so conftest.o -L/usr/lib/jdk1.6.0_31/jre/lib/amd64/server -ljvm -L/usr/lib64/R/lib -lR
    
       JAVA_HOME        : /usr/java/default
       Java library path: /usr/lib/jdk1.6.0_31/jre/lib/amd64/server
       JNI cpp flags    : -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
       JNI linker flags : -L/usr/lib/jdk1.6.0_31/jre/lib/amd64/server -ljvm
       Updating Java configuration in /usr/lib64/R
       Done.
    ¡Importante! Fijarse que la variable JNI cpp flags NO se encuentre vacía. Versiones de JAVA soportadas para Cloudera 4 JRE 1.6u31 y para Cloudera 5 JRE 1.7u45.

    Ahora sí, iniciamos la shell que nos ofrece R:
     # R
       R version 3.0.2 (2013-09-25) -- "Frisbee Sailing"
       Copyright (C) 2013 The R Foundation for Statistical Computing
       Platform: x86_64-redhat-linux-gnu (64-bit)
       >
  4. Instalar paquetes de R.
  5.  > install.packages(c('Rcpp', 'RJSONIO', 'itertools', 'digest'), repos="http://cran.revolutionanalytics.com", INSTALL_opts=c('--byte-compile') )
       ...
     > install.packages(c('functional', 'stringr', 'plyr'), repos="http://cran.revolutionanalytics.com", INSTALL_opts=c('--byte-compile') )
       ...
     > install.packages(c('caTools'), repos="http://cran.revolutionanalytics.com" )
       ...
     > install.packages(c('rJava'), repos="http://cran.revolutionanalytics.com" )
       ...
     > install.packages(c('randomForest'), repos="http://cran.revolutionanalytics.com" )
       ...
     > install.packages(c('reshape2'), repos="http://cran.revolutionanalytics.com" )
       ...
     > q()
       Save workspace image? [y/n/c]: y
    
  6. Instalar Git.
    Lógicamente en caso de no disponer de él.
  7.  # yum install git -y 
  8. Instalar RMR2.
  9. Lo primero será descargarnos el paquete rmr2.
     # cd /tmp/
     # git clone https://github.com/RevolutionAnalytics/rmr2.git
       Initialized empty Git repository in /root/software/rmr2/.git/
       remote: Reusing existing pack: 5534, done.
       remote: Counting objects: 22, done.
       remote: Compressing objects: 100% (22/22), done.
       remote: Total 5556 (delta 9), reused 0 (delta 0)
       Receiving objects: 100% (5556/5556), 23.59 MiB | 838 KiB/s, done.
       Resolving deltas: 100% (3740/3740), done.
    ¡¡¡ Precaución !!! El paquete rmr2 requiere por su configuración la instalación de una versión en concreto del paquete Apache-Ant, en mi caso la 1.9.2. Ahora bien, la URL de la que tratará descargarse el paquete es incorrecta y por lo tanto fallará. 

    Es por ello por lo que deberemos modificar el fichero rmr2/pkg/src/hbase-io/build_linux.sh:
     # vim rmr2/pkg/src/hbase-io/build_linux.sh
    Sustituyendo la línea "wget http://mirror.nus.edu.sg/apache/ant/binaries/apache-ant-1.9.2-bin.tar.gz" por el enlace correspondiente a este paquete. En nuestro caso por "http://archive.apache.org/dist/ant/binaries/apache-ant-1.9.2-bin.tar.gz".

    Y ahora sí, por fin procederemos a la instalación del paquete rmr2:
     # R CMD INSTALL --byte-compile rmr2/pkg/
       ...
       ** building package indices
       ** testing if installed package can be loaded
       * DONE (rmr2)
       Making 'packages.html' ... done
  10. Hadoop - Variables de Entorno.
    Nuestro siguiente paso será asegurarse de que estén declaradas en nuestro sistema las variables de entorno HADOOP_HOME, HADOOP_CMD y HADOOP_STREAMING.
     # echo $HADOOP_HOME
       /opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30
     # echo $HADOOP_CMD
       /usr/bin/hadoop
     # echo $HADOOP_STREAMING
       /opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming-2.0.0-mr1-cdh4.5.0.jar
    
    En caso contrario tendremos que añadir al final de los ficheros /etc/profile y ~/.bashrc las iguientes líneas, eso sí, de acuerdo a nuestra instalación de Cloudera.
  11.  export HADOOP_HOME=/opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30
     export HADOOP_CMD=/usr/bin/hadoop
     export HADOOP_STREAMING=/opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming-2.0.0-mr1-cdh4.5.0.jar
    
    Y por último ejecutar:
     # source /etc/profile
     # source ~/.bashrc
    
    Si consultamos ahora estas variables de entorno, deberían devolvernos sus correspondientes valores. 

  12. Instalar RHDFS.
  13. Nos descargamos el paquete rhdfs:
     # cd /tmp/
     # git clone https://github.com/RevolutionAnalytics/rhdfs.git
       Initialized empty Git repository in /tmp/rhdfs/.git/
       remote: Reusing existing pack: 494, done.
       remote: Total 494 (delta 0), reused 0 (delta 0)
       Receiving objects: 100% (494/494), 714.69 KiB | 285 KiB/s, done.
       Resolving deltas: 100% (200/200), done.
    
    Y lo instalamos:
     # R CMD INSTALL –byte-compile rhdfs/pkg/
       ...
       ** building package indices
       ** testing if installed package can be loaded
       * DONE (rhdfs)
       Making 'packages.html' ... done
    Alcanzado este punto R y RHadoop estarán instalados.
  14. R - Pruebas.
  15. Primero deberemos configurar correctamente los permisos sobre el directorio /user de nuestro sistema de ficheros HDFS.
     # su hdfs
     $ hadoop -fs chmod -R 777 /user
     $ exit
    
    Ahora podremos comprobar que todo funciona correctamente con este sencillo y típico ejemplo antes de instalar RStudio o bien saltarnos este paso y hacer dichas comprobaciones tras instalar la herramienta en cuestión.
     # R
      > Sys.setenv(HADOOP_HOME=”/opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30”)
      > Sys.setenv(HADOOP_CMD=”/usr/bin/hadoop”)
      > Sys.setenv(HADOOP_STREAMING=”/opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming-2.0.0-mr1-cdh4.5.0.jar”)
      > library(rmr2)
      > library(rJava)
      > library(rhdfs)
      > hdfs.init()
      > ints = to.dfs(1:100)
      > calc = mapreduce(input = ints, map = function(k, v) cbind(v, 2*v))
      ...
      > from.dfs(calc)
       $key
       NULL
    
       $val
       v
       [1,]   1   2
       [2,]   2   4
       [3,]   3   6
       [4,]   4   8
       [5,]   5  10
       ...
    
  16. Instalar Servidor RStudio.
  17. Desde la propia web de RStudio descargaremos el paquete de la versión servidor o buscaremos y copiaremos el enlace a dicho paquete ejecutando a continuación alguna de las siguientes opciones.
    En caso de ser el sistema de 64-bits:
     # wget http://download2.rstudio.org/rstudio-server-0.98.501-x86_64.rpm
     # yum install --nogpgcheck rstudio-server-0.98.501-x86_64.rpm
    En caso de ser de 32-bits:
     # wget http://download2.rstudio.org/rstudio-server-0.98.501-i686.rpm
     # yum install --nogpgcheck rstudio-server-0.98.501-i686.rpm
    RStudio se instalará como un servicio más de linux por lo que podremos ejecutar sobre él las operaciones típicas de start, stop o status.
     # service rstudio-server stop
     # service rstudio-server start
     # service rstudio-server status
    Al haber descargado la opción de RStudio Server tendremos la posibilidad de acceder a esta herramienta desde cualquier otro equipo de nuestra LAN visitando la siguiente dirección:

    http://<ip_server>:8787/

    El usuario y contraseña que por defecto nos crea la herramienta es rhadoop para ambos campos.

    Por cierto, decir que las sesiones van asociadas al usuario por lo que sólo un usuario podrá estar conectado con él. En caso de conectarse con el mismo usuario desde otro equipo este último nos 'robará' la sesión anteriormente iniciada.

    Por suerte la solución es simple, los usuarios de RStudio Server son usuarios del propio sistema operativo (con root no será accesible) así que nos será suficiente con crearnos los que deseemos (no hace falta incluirlos en ningún tipo de grupo de usuarios):
     # useradd usuario1
     # passwd usuario1
    Consejo: Una vez autenticado, crear en el home del usuario el fichero .Rprofile y añadir en él las siguiente líneas.
     Sys.setenv(HADOOP_HOME=”/opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30”)
     Sys.setenv(HADOOP_CMD=”/usr/bin/hadoop”)
     Sys.setenv(HADOOP_STREAMING=”/opt/cloudera/parcels/CDH-4.5.0-1.cdh4.5.0.p0.30/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming-2.0.0-mr1-cdh4.5.0.jar”)
     library(rmr2)
     library(rJava)
     library(rhdfs)
     hdfs.init()
    
    Esto os evitará tener que escribirlas cada vez que iniciemos R con dicho usuario pues en su inicio R las ejecutará automáticamente por nosotros.
Bien, pues o volvemos a ejecutar las comprobaciones del punto 8 o ya estaría todo listo.

Espero que os haya sido de utilidad este tutorial y ahora ya sólo falta que cada uno le saque el partido o rendimiento deseado a R.

¡¡¡ Muchas gracias !!!