Está en la página 1de 64

See discussions, stats, and author profiles for this publication at: https://www.researchgate.

net/publication/329753879

Procesamiento de Datos en Tiempo Real Utilizando Docker, Zookeeper, Kafka


y Storm

Thesis · March 2017


DOI: 10.13140/RG.2.2.11178.47043

CITATIONS READS

0 1,837

1 author:

Julian Nonino
National University of Cordoba, Argentina
4 PUBLICATIONS 9 CITATIONS

SEE PROFILE

Some of the authors of this publication are also working on these related projects:

DevOps Maturity Model View project

All content following this page was uploaded by Julian Nonino on 18 December 2018.

The user has requested enhancement of the downloaded file.


Procesamiento de Datos en Tiempo Real
Utilizando Docker, Zookeeper, Kafka y Storm

Especialización en Ingenierı́a en Sistemas de Información

Julián Nonino

Universidad Tecnológica Nacional


Facultad Regional Córdoba
Dirección de Posgrado
Córdoba
- 2016 -
Resumen Ejecutivo

En los últimos años, con las llegada de las Redes Sociales, Big Data, Internet
de las Cosas, entre otras, la cantidad de datos generados creció exponencialmente.
Paralelamente, se vió incrementada la necesidad de tomar acciones en base a dichos
datos en el menor tiempo posible. En medio de éste fenómeno, surgen los sistemas
en la nube, con escalabilidad para crecer y decrecer en base a los requerimientos
de procesamiento de cada momento. En ese marco surgen nuevas tecnologı́as como
Docker, Apache Zookeeper, Apache kafka y Apache Storm.
Con Docker es posible utilizar contenedores de software independientes que pue-
den comunicarse con otros contenedores proporcionando una capa de virtualización
sencilla a nivel de sistema operativo Linux. Su principal ventaja radica en la facilidad
de despligue de apliacación que provee su uso.
Apache Zookeeper surge como un servicio de coordinación de alto rendimiento
para aplicaciones distribuidas. Expone servicios comunes, tales como nomenclatura,
manejo de las configuraciones, sincronización, etcétera. Entre otras funciones, se uti-
liza para implementar consenso, manejar grupos, elección de nodo lı́der y protocolos
de presencia.
Apache Kafka es un sistema de mensajes distrubuido, particionado y con repli-
cación. Su principal objetivo es recibir datos desde el mundo exterior al sistema y
garantizar su diponibildiad para otros componentes del sistema que necesiten leerlos
y procesarlos.
Apache Storm es una herramienta de procesamiento de datos en tiempo real de
código abierto y gratuita creada por Twitter y luego liberada bajo la órbita de los
proyectos Apache. La finalidad de Storm es proveer un mecanismo confiable para
procesamiento de flujos de datos ilimitados. De acuerdo a su documentación, Storm
es capaz de procesar un millón de tuplas de datos por segundo por nodo. Provee
caracterı́sticas de escalabilidad, tolerancia a fallos, garantı́as de que todos los datos
serán procesados, etcétera[1].
Utilizando dichas tecnologı́as se generan imágenes de Docker para la conforma-
ción de un clúster de Apache Zookeeper, uno de Apache Kafka y otro de Apache
Storm. Se utilizará Docker Compose para levantar los servicios necesarios. Para el
alcance de ésta prueba de concepto, todos los servicios correrán en el mismo servidor
pero, para aprovechar las propuesta de alta disponibilidad y escalabilidad que ofre-
cen éstas tecnologı́as, serı́a recomedable que cada nodo de cada uno de los servicios
corra en un servidor independiente.
Se desarrolla una aplicación Java para enviar datos a Apache Kafka en el sistema

iii
iv

y una topologı́a de Apache Storm que buscará los datos en el servicio de Apache
Kafka y los dejará listos para ser procesados.
Se mostrará como se realiza el despliegue del sistema con tres nodos de Apache
Zookeeper, tres nodos de Apache Kafka y tres nodos para Apache Storm, uno para
el nodo Nimbus, otro para el nodo Supervisor y un nodo para Interfaz Gráfica.
También se muestra como se carga la topologı́a desarrollada en el clúster de
Apache Storm y luego, como enviarle datos al sistema mediando una aplicación
Java que actúa como productor de datos para Apache Kafka.
Finalmente, se comprueba que el sistema, está recibiendo datos (Apache Kafka)
y efectivamente, llegan al sistema de procesamiento (Apache Storm).
La prueba de concepto puede ser extendida implementado nuevas topologı́as de
Apache Storm para realizar procesamientos más complejos de los datos, utilizar un
servicio en la nube para alojar los servidores. Por otro lado, implementar mecanismos
de auto escalabilidad de cada uno de los servicios para que el sistema crezca y
decrezca dependiendo de las necesidades de procesamiento del momento. También, es
posible implementar mecanismos de auto descubrimiento de servicios para garantizar
que al crear o borrar un nodo de algún servicio el sistema continue funcionando de
manera óptima.
Tabla de contenido

I Introducción 1
1. Introducción 3

2. Marco Teórico 5

II Desarrollo 19
3. Diseño de la Arquitectura del Sistema 21

4. Imágenes de Docker 23

5. Aplicaciones Desarrolladas 33

III Resultados y Conclusiones 41


6. Resultados 43

7. Conclusiones y Trabajo Futuro 51

Bibliografı́a 53

Índice de Contenido 53

Índice de Figuras 57

v
vi TABLA DE CONTENIDO
Parte I

Introducción

1
Capı́tulo 1

Introducción

En los últimos años, con las llegada de las Redes Sociales, Big Data, Internet
de las Cosas, entre otras, la cantidad de datos generados creció exponencialmente.
Paralelamente, se vió incrementada la necesidad de tomar acciones en base a dichos
datos en el menor tiempo posible. En medio de éste fenómeno, surgen los sistemas en
la nube, con escalabilidad para crecer y decrecer en base a los requerimientos de pro-
cesamiento de cada momento. Tecnologı́as como Docker aparecen como alternativas
a las antiguas maneras de desarrollar y desplegar sistemas y servidores, acercando-
nos más a la Infrastructura como Código. Surgen también tecnologı́as como Apache
kafka y Apache Storm para transmitir mensajes y procesar dicho mensajes respec-
tivamente. Apache Zookeeper aparece también como una herramienta robusta para
sincronizar y coordinar servicios.
Éste trabajo surge con la idea de utilizar dichas tecnologı́as para ejemplificar su
uso como parte de un sistema.

1.1. Objetivos
El objetivo principal de éste trabajo es implementar un sistema de procesamiento
de datos en tiempo real como prueba de concepto utilizando las últimas tecnologı́as
de la industria como son Docker, Apache Zookeeper, Apache Kafka y Apache Storm.

1.1.1. Objetivos Secundarios


Plantear un modelo de generación y procesamiento de datos sencillo que ayude
a visualizar el funcionamiento del sistema.

Demostrar el rol de Apache Zookeeper dentro de los sistemas distribuidos.


Mostrar su implementación a través de una prueba de concepto.

Demostrar el rol de Apache Kafka y Apache Storm dentro de los sistemas de


procesamiento de datos en tiempo real.

3
4 CAPÍTULO 1. INTRODUCCIÓN

Estudiar y utilizar Docker como herramienta de despliegue de los componentes


del sistema ayudando a la escalabilidad del sistema.
Utilizar Docker Compose como mecanismo de despliegue del sistema y cone-
xión de los componentes del mismo.

1.2. Manejo de las Configuraciones


1.2.1. Ubicación del Proyecto
Los archivos del proyecto, incluyendo código fuente, documentos, archivos LaTeX
del informe del trabajo y otros recursos, se encuentran en un proyecto privado en la
herramienta BitBucket en la dirección bitbucket.org/jnonino/especialidad-utn.
Dado que es un repositorio privado, se debe contar con un usuario de la herra-
mienta y se deben otorgar permisos de acceso.

1.2.2. Herramientas y Frameworks Utilizados


Para el desarrollo de este trabajo, se utilizan las siguientes herramientas.
Git como herramienta de control de versiones.
BitBucket como repositorio Git.
Eclipse Neon.1 como IDE para la escritura del informe en LaTeX.
IntelliJ IDEA Ultimate 2016.3 para el desarrollo de las imágenes de Docker y
del codigo Java de las aplicaciones desarrolladas.
Maven como herramienta de automatización de compilación.
Java Development Kit 7 como lenguaje de programación y herramientas de
compilación y ejecución.
Entre las piezas de software y/o frameworks utilizados como parte del sistema
desarrollado, se encuentran:
Docker como herramienta de generación de contenedores independientes para
el despliegue del sistema.
Docker Compose como herramienta de orquestación y coordinación entre las
diferentes imágenes de Docker que deben correr.
Apache Zookeeper como herramienta de coordinación de servicios entre los
componentes del sistema.
Apache Kafka 0.9.0.1 como sistema de recepción y envı́o de los datos que
arriban al sistema.
Apache Storm 0.9.7 como sistema de procesamiento de datos.
Capı́tulo 2

Marco Teórico

2.1. Procesamiento de Datos en Tiempo Real


En los últimos tiempos, la demanda de procesamiento de flujos continuos de datos
(data streams) se ha incrementado considerablemente. Esto se debe a que ya no es
suficiente con procesar grandes volúmenes de datos, además, deben ser procesados
rápidamente permitiendo a los sistemas reaccionar ante los eventos lo antes posible.
Ejemplos de sistemas que necesitan éste nivel de procesamiento son los sistemas de
detección de fraude, monitoreo de recursos, comercio, etcétera.

2.1.1. Big Data


El término Big Data, muy utilizado en la actualidad, hace referencia a lo que se
conoce como las Tres V , Volumen, Variedad y Velocidad. Con ello, se quiere indicar
que un sistema Big Data no solo implica trabajar con grandes volúmenes de datos,
sino que estos datos pueden ser muy variados y se deben procesar rápidamente [2].

5
6 CAPÍTULO 2. MARCO TEÓRICO

2.1.2. Procesamiento de Flujos de Datos (Stream Proces-


sing)
En contraste a los modelos de procesamiento de datos tradicionales en los cuales
los datos son primero almacenados, cuando se trabaja un flujo de datos, los datos
son procesados y analizados mientras entran en el sistema. Esto se conoce como
procesar datos en movimiento, conectando los procesadores de datos con las fuentes
que los producen.
Una solución de procesamiento de datos en tiempo real debe ser capaz de:
Procesar cantidades enormes de datos permitiendo filtrado, agregación, pre-
dicción, alertas, reglas, etcétera.
Respuesta en tiempo real a los mensajes/eventos recibdos.
Asegurar rendimiento y escalabilidad cuando el volumen de datos crece en
tamaño y/o complejidad.
Integración fácil y rápida con la infraestructura y fuentes de datos existentes.
Rápida implementación y puesta en producción de nuevos requisitos de pro-
cesamiento.

2.2. Docker
Docker es un proyecto de código abierto que automatiza el despliegue de apli-
caciones dentro de contenedores de software, proporcionando una capa adicional de
abstracción y automatización de virtualización a nivel de sistema operativo en Li-
nux. Docker utiliza caracterı́sticas de aislamiento de recursos del kernel de Linux,
tales como cgroups y espacios de nombres (namespaces) para permitir que contene-
dores independientes se ejecuten dentro de una sola instancia de Linux, evitando la
sobrecarga de iniciar y mantener máquinas virtuales.[3]
Docker implementa una API de alto nivel para proporcionar contenedores li-
vianos que ejecutan procesos de manera aislada. Construido sobre las facilidades
proporcionadas por el kernel de Linux (nombradas anteriormente), un contenedor
Docker, a diferencia de una máquina virtual, no requiere incluir un sistema opera-
tivo independiente. En su lugar, se basa en las funcionalidades del kernel y utiliza
el aislamiento de recursos y namespaces separados para aislar de vista la aplicación
del sistema operativo.[3]
El soporte del kernel de Linux para los espacios de nombres aı́sla de vista una
aplicación del entorno operativo, incluyendo árboles de proceso, red, ID de usuario
y sistemas de archivos montados. A su vez, los cgroups del kernel proporcionan
aislamiento de recursos, incluyendo la CPU, la memoria, el bloque de E/S y de la
red. Desde la versión 0.9, Docker incluye la librerı́a libcontainer como su propia
manera de utilizar directamente las facilidades de virtualización que ofrece el kernel
de Linux.[3]
2.2. DOCKER 7

Figura 2.1: Contenedor Docker (derecha) versus Máquina Virtual (izquierda) [4]

Mediante el uso de contenedores, los recursos pueden ser aislados y los servicios
restringidos. Además, se otorga a los procesos la capacidad de tener una visión casi
completamente privada del sistema operativo con su propio identificador de espacio
de proceso, estructura del sistema de archivos e interfaces de red. Contenedores
múltiples comparten el mismo núcleo, pero cada contenedor puede ser restringido a
utilizar sólo una cantidad definida de recursos como CPU, memoria y E/S.[3]
Usando Docker para crear y gestionar contenedores se puede simplificar la crea-
ción de sistemas altamente distribuidos, permitiendo múltiples aplicaciones funcio-
nar de forma autónoma en una única máquina fı́sica o en varias máquinas virtuales.
Esto permite que el despliegue de nodos se realice a medida que se dispone de recur-
sos o cuando se necesiten, lo que se conoce como un estilo de desplieque Plataforma
como Servicio (PaaS - Plataform as a Service).[3]

2.2.1. Imágenes y Contenedores


Un contenedor (container) es una version de un sistema operativo Linux, solo
con los componentes más básicos. Una imagen es software que se carga dentro del
contenedor al momento de ejecutar el comando run. [5]
d o c k e r run h e l l o −world
El comando run recibe como parámetro requerido el nombre de la imagen que
se desea cargar en un contenedor, en éste caso, hello-world.
Al correr dicho comando, Docker ejecuta las siguientes acciones:

Comprobar si existe en el sistema una imagen con el nombre hello-world.


8 CAPÍTULO 2. MARCO TEÓRICO

En caso de que no exista dicha imagen en el sistema descargarla desde el


repositorio de imágenes configurado, por defecto es Docker Hub, un repositorio
propiedad de Docker donde existen miles de imágenes disponibles. Es posible
tener repositorios privados utilizando lo que se conoce como Docker Registry.

Cargar la imagen en el contenedor y ejecutarla.

Por otro lado, una imagen de Docker puede ejecutar desde un simple comando
hasta cargar un complejo sistema de base de datos.
Para construir una imagen de Docker, es necesario crear un archivo llamado
Dockerfile.
FROM ubuntu : 1 6 . 0 4

RUN apt−g e t −y update

CMD[ ” echo Hola ” ]


El Dockerfile anterior buscara una imagen de Ubuntu con la etiqueta (tag) 16.04.
Luego ejecutará un comando para actualizar los paquetes del sistema operativo y
finalmente mostrará el mensaje Hola.
El comando para construir una imagen de Docker es:
d o c k e r b u i l d −t miimagen .
Se ejecutará el comando build para construir la imagen. El argumento -t indica
que se le pondrá la etiqueta miimagen a la imagen y punto al final indica el directorio
de contexto de la imagen, esto permite agregarle archivos al momento de construirla.
En éste caso, el contexto será el directorio donde se encuentra el Dockerfile.
Luego, es posible cargar la imagen en un contenedor mediante el comando:
d o c k e r run miimagen

2.2.2. Crear nuevas etiquetas


Para ponerle una nueva etiqueta a una imagen, primero debemos encontrar el
número de identificación de la misma. Ésto se hace corriendo el comando:
d o c k e r images
El comando anterior, mostrará una lista de las imágenes existentes en el sistema
mostrando la última etiqueta de la misma, el número de identificación, la fecha de
creación y el tamaño de la imagen.
Luego, para aplicarle una nueva etiqueta, se ejecuta el comando:
d o c k e r t ag <IMAGE ID> <NUEVA ETIQUETA>
2.3. APACHE ZOOKEEPER 9

2.2.3. Docker Compose


Docker Compose es una herramienta que permite correr un sistema formado por
múltiples contenedores. Para ello, se debe crear un archivo .yml en el que se definan
los servicios con los que va a contar la aplicación. Cada servicio estará formado por
un contenedor corriendo una imagen de Docker.
Para cada servicio pueden definirse nombres, puertos expuestos, conexiones de
red, etcétera, luego, con los siguientes comandos se puede operar con el sistema.
Para una lista completa de los comandos de Docker Compose, acceder a Docker
Compose Command-Line Reference 1 .

2.2.4. Material
La información de ésta sección ha sido extraı́da mayormente desde la documen-
tación de Docker[5] y Docker Compose[6].

2.3. Apache Zookeeper


Apache Zookeeper es un servicio de coordinación de alto rendimiento para apli-
caciones distribuidas. Expone servicios comunes, tales como nomenclatura, manejo
de las configuraciones, sincronización, etcétera. Entre otras funciones, se utiliza pa-
ra implementar consenso, manejar grupos, elección de nodo lı́der y protocolos de
presencia.
Generalmente, los sistemas de coordinación son muy propensos a errores como
condiciones de carrera y puntos muertos (deadlocks). Zookeeper fue creado para que
las aplicaciones distribuidas ya no necesiten implementar desde cero los servicios de
sincronización.

Figura 2.2: Arquitectura de un servicio Zookeeper[7]

De acuerdo a la documentación de Zookeeper, fué diseñado bajo estos cuatro


pilares[7]:
1
https://docs.docker.com/compose/reference/
10 CAPÍTULO 2. MARCO TEÓRICO

simplicidad : Zookeeper permite la sincronización entre procesos distribuidos


a través de un espacio de nombres jerárquico compartido que se encuentra
organizado de manera similar a un sistema de archivos. El espacio de nombres
consiste en registros de datos (llamados znodes) muy similares a archivos y
directorios.

replicación: Como las aplicaciones distribuidas que coordina, Zookeeper tam-


bién esta diseñado para ser ejecutado de manera distribuida en un conjunto de
servidores. Cada uno de ellos mantiene en memoria una imagen de estado con
los registros de las transacciones y además, guarda capturas en un almacena-
miento permanente. Mientras la mayorı́a de los servidores siga funcionando,
Zookeeper seguirá funcionando.
Cada cliente, se conecta a un único servidor de Zookeeper y mantiene una
conexión TCP a través de la cual envı́a solicitudes, obtiene las respuestas y
obtiene mensajes de eventos. Si dicha conexión deja de funcionar, el cliente se
conectará a un servidor distinto.

orden: Cada actualización en Zookeeper es marcada con un número que refleja


el orden de cada una de las transacciones.

rapidez : Zookeeper es muy rápido para entornos con cargas de trabajo de


muchas lecturas. Su rendimiento es mucho mejor donde las lecturas de datos
son más comunes a las escrituras de datos, a una relación de 10 a 1.

Figura 2.3: Rendimiento de Zookeeper mostrando escrituras versus lecturas[7]


2.3. APACHE ZOOKEEPER 11

2.3.1. El espacio de nombres y el modelo de datos


El espacio de nombres de Zookeeper es muy similar a un sistema de archivos.
Cada nombre es una secuencia de elementos de direcciones (paths) separadas por
una barra (/ ) y cada nodo es identificado por una dirección.

Figura 2.4: El espacio de nombres jerárquico de Zookeeper[7]

A diferencia de los sistemas de archivos, cada nodo en el espacio de nombres


de Zookeeper puede tener datos y subnodos, es como si un sistema de archivos
permitiera a un archivo ser a la vez un directorio. Debido a los objetivos por lo
cuales Zookeeper fue diseñado, los datos almacenados en cada nodo generalemente
son muy pequeños, en el orden de un byte a un kilobyte. Cada nodo de datos, en la
nomenclatura de Zookeeper es llamado znode.
Los znodes mantienen una estructura de datos que incluye numeros de version
para cada cambio en los datos y marcas de tiempo para permitir validaciones de ca-
che y actualizaciones coordinadas. Con cada cambio, el número de versión aumenta.
Los datos de cada znode son escritos y leı́dos de manera atómica. Las lectu-
ras devuelven todos los datos asociados al znode y las escrituras reemplazan todos
los datos. Cada nodo, también, posee una lista de control de acceso (ACL, Access
Control List) que limita quienes pueden ejecutar cada una de las acciones.

2.3.2. Garantı́as
Dado que el objetivo de Zookeeper es ser la base para la construcción de servicios
complejos, como un servicio de sincronización, provee un conjunto de garantı́as:
12 CAPÍTULO 2. MARCO TEÓRICO

Consistencia Secuencial : Todas las actualizaciones que llegan desde los clien-
tes, serán aplicadas en el orden en el que fueron enviadas.

Atomicidad : Las actualizaciones son exitosas o fallidas. No existen resultados


parciales.

Única Imagen del Sistema: Un cliente tiene la misma vista del servicio Zoo-
keeper sin importar a cual de los servidores se conecte.

Confiabildad : Una vez que una actualización es aplicada, persiste en el tiempo


hasta que algún cliente sobreescribe el registro.

Actualizaciones puntuales: El sistema garantiza a los clientes que su vista del


servicio estará actualizada dentro de un cierto espacio temporal.

2.3.3. Material
La información de ésta sección ha sido extraı́da mayormente desde la documen-
tación de Apache Zookeeper[7].

2.4. Apache Kafka


Kafka es un sistema de mensajes distribuido, particionado y con replicación[8].

Kafka mantiene los mensajes agrupados en categorı́as llamadas topics.

Los productores de mensajes se llaman producers.

Los consumidores de mensajes se llaman consumers.

Kafka corre en un cluster formado por uno o mas servidores. Cada uno de ellos
es llamado broker.

Figura 2.5: Kafka, arquitectura de alto nivel[8]


2.4. APACHE KAFKA 13

2.4.1. Topics
Los topics de Kafka son categorı́as de mensajes para los cuales Kafka mantiene
registros particionados.
Cada partición es una secuencia ordenada e inmutable de mensajes. El número
de orden de cada mensaje es llamado offset e identifica unı́vocamente a cada mensaje
de la partición.
Kafka mantiene los mensajes publicados por un perı́odo de tiempo configurable,
sin importar si fueron consumidos o no por algún proceso consumer. Cada consu-
midor se encarga de mantener el offset y tiene libertad para ir hacia atrás y hacia
adelante en los mensajes publicados para procesarlos.
Tener los mensajes de un topic particionados permite separar el topic en varios
servidores. Ésto permite manejar grandes volumenes de datos y además otorogar un
nivel superior de paralelismo.

Figura 2.6: Topics en Kafka[8]

Cada partición está formada por un lı́der (leader ) que se encuentra en uno de
los servidores y por cero o más seguidores (followers) que replican al lı́der todo el
tiempo en servidores distintos. Si el lı́der falla, alguno de los seguidores se convertirá
en el nuevo lı́der garantizando que el sistema siga operando. La configuración ideal
es que cada servidor sea lı́der de alguna partición y seguidor de las otras.

2.4.2. Productores
Los productores en Kafka son programas encargados de publicar datos en los
topics. El productor decide, para cada mensaje, el topic y la partición en el cual
publicarlo. Generalmente la partición es elegida siguiendo un esquema round-robin
para lograr un óptimo balance de carga entre particiones, pero se puede utilizar
cualquier lógica.

2.4.3. Consumidores
Los sitemas de mensajerı́a pueden ser clasificados en dos categorı́as, cola de men-
sajes o publicación-subscripción. En el primero, los mensajes son encolados y cada
mensaje es dirigido hacia alguno de los consumidores. En el segundo, cada mensaje
14 CAPÍTULO 2. MARCO TEÓRICO

es transmitido a todos los consumidores. Apache Kafka maneja ambos mundos con
lo que se conoce como grupos de consumidores (consumer groups).
Cada consumidor debe ubicarse dentro de alguno de los grupos de consumidores
y cuando un mensaje es publicado en un topic, es transmitido a un único consumidor
de cada uno de los grupos de consumidores.

Figura 2.7: Grupos de Consumidores[8]

Si todos los consumidores se encuentran en el mismo grupo, el sistema funciona


como una cola de mensajes distribuyendo la carga entre cada uno de los consumi-
dores.
Si todos los consumidores se encuentran en distintos grupos, el sistema funciona
como un sistema publicación-subscripción y todos los mensajes son transmitidos a
todos los consumidores.

2.4.4. Material
La información de éste capı́tulo ha sido extraı́da mayormente desde la documen-
tación de Apache Kafka 0.9 [8].

2.5. Apache Storm


Apache Storm es una herramienta de procesamiento de datos en tiempo real de
código abierto y gratuita creada por Twitter y luego liberada bajo la órbita de los
proyectos Apache.
La finalidad de Storm es proveer un mecanismo confiable para procesamiento de
flujos de datos ilimitados, haciendo para flujos de datos (realtime stream processing)
lo que Hadoop hace en procesamiento por lotes (batch processing)[1].
De acuerdo a su documentación, Storm es capaz de procesar un millón de tuplas
de datos por segundo por nodo. Provee caracterı́sticas de escalabilidad, tolerancia a
fallos, garantı́as de que todos los datos serán procesados, etcétera[1].

2.5.1. Conceptos Básicos


En ésta sección se analizarán los conceptos básicos que definen a un programa
Storm.
2.5. APACHE STORM 15

2.5.1.1. Topologies
Las topologı́as son los contenedores de la lógica de una aplicación de procesa-
miento de datos en tiempo real en Storm. Consumen flujos de datos, los procesan
y generan nuevos flujos de datos. Es el análogo a un trabajo de MapReduce de Ha-
doop2 . La diferencia principal con éstos últimos es que un trabajo MapReduce de
Hadoop, eventualmente concluye mientras que las topologı́as pueden correr indefini-
damente. Una topologı́a es un grafo formado por Spouts y Bolts conectados a través
de Stream Groupings (2.5.1.5).

2.5.1.2. Streams
Los streams son una secuencia ilimitada de tuplas de datos que son creadas y
procesadas de manera distribuida. Los streams se definen creando un esquema que
contenga todos los campos de datos de cada tupla que forma parte del stream.
Las tuplas pueden contener valores enteros (integer ), bytes, cadenas de caracteres
(strings), valores booleanos, etcétera.

2.5.1.3. Spouts
Los spouts son la fuente de streams para la topologı́a. Generalemente leen tuplas
de datos desde una fuente externa y las emiten dentro de la topologı́a para que sea
procesada. Los spouts pueden ser:
reliable (confiable): es un spout capaz de reenviar una tupla si Storm falló en
procesarla.
unreliable (no confiable): el spout se olvida de las tuplas en el momento en
el que las emite hacia la topologı́a.
2
https://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-
client-core/MapReduceTutorial.html
16 CAPÍTULO 2. MARCO TEÓRICO

2.5.1.4. Bolts

Dentro de una topologı́a, todo el procesamiento sobre los datos es realizado en los
bolts. Los bolts pueden ser programados para realizar cualquier tarea como filtrado,
agregación, uniones con bases de datos, funciones, etcétera. Los bolts reciben uno o
varios streams de datos y pueden emitir nuevamente uno o varios de ellos luego de
procesados.

2.5.1.5. Stream Grouping

Al definir una topologı́a, es necesaria especificar que streams debe recibir como
entrada cada uno de los bolts. Los stream groupings definen como los streams deben
ser particionados en las tareas de cada bolt.
Existen ocho stream groupings predefinidos pero existe la posibilidad de crear
nuevos implementando la interfaz CustomStreamGrouping.

Shuffle grouping: Las tuplas son distribuidas aleatoriamente en las tareas de


los bolts de manera tal que se garantice que todos los bolts reciben las misma
cantidad de tuplas.

Fields grouping: El stream es particionado de acuerdo a los campos de datos


que contenga la tupla. Por ejemplo, si la tupla contiene un campo llamado
usuario y se agrupa el stream por el campo usuario, todos aquellas tuplas que
tengan el mismo valor en dicho campo serán procesadas por el mismo bolt.

Partial Key grouping: El stream es particionado de la misma manera que en


el caso de Fields grouping solo que la carga es balanceada entre dos bolts para
proporcionar una mejor utilización de los recursos.

All grouping: El stream de datos es replicado en TODAS las tareas de los


bolts.

Global grouping: El stream completo es dirigido hacia una única tarea de un


bolt.

None grouping: Al utilizar esta forma de agrupamiento, se está indicando que


no es importante como el stream es dirigido hacia los bolts.

Direct grouping: En éste caso, el productor de la tupla de datos decide a que


tarea del bolt consumidor desea enviar la tupla.

Local grouping: Si el bolt de destino tiene una o más tareas en el mismo proceso
worker, las tuplas irán aleatoriamente hacia alquellas tareas que estén siendo
ejecutadas. En caso contrario, se comportará como un Shuffle grouping.
2.5. APACHE STORM 17

2.5.1.6. Tasks
Cada spout o bolt ejecuta sus tareas en el cluster de Storm. Cada tarea corres-
ponde con un hilo de ejecución (thread ) y los Stream groupings definen como las
tuplas viajan entre las tareas.

2.5.1.7. Workers
Las topologı́as corren sobre uno o más procesos worker. Cada uno de estos pro-
cesos es una JVM que ejecuta un subconjunto de las tareas de la topologı́a.

2.5.2. Material
La información de éste capı́tulo ha sido extraı́da mayormente desde la documen-
tación de Apache Storm 1.0 [1].
18 CAPÍTULO 2. MARCO TEÓRICO
Parte II

Desarrollo

19
Capı́tulo 3

Diseño de la Arquitectura del


Sistema

Para cumplir con el objetivo de armar un sistema de procesamiento de dato en


tiempo real utilizando tecnologı́as como Docker, Apache Zookeeper, Apache Kafka
y Apache Storm. Se plantea un sistema multinodo, con cada nodo corriendo un
determinado servicio basado en la imagen de Docker generada.
Como se observa en la figura 3, se generarán tres nodos de Apache Zookeeper, al
igual que con Apache Kafka. Esto permite garantizar la disponibiliad de los servicios.
En el caso de Apache Storm, se generan tres nodos, uno para el procesamiento de
datos (podrı́an existir más), un único nodo encargado de controlar el clúster, Storm
Nimbus, y un nodo adicional para acceder a la interfaz gráfica de Apache Storm.

Figura 3.1: Arquitectura general del Clúster incluyendo Zookeeper, Kafka y Storm

21
22 CAPÍTULO 3. DISEÑO DE LA ARQUITECTURA DEL SISTEMA

También, se implementan dos simples aplicaciones en Java. Una, para actuar de


productor de datos para Apache Kafka, simulando algún dispositivo o sistema que
envı́a datos al servidor. La otra es la topologı́a de Apache Storm encargada de leer
los datos desde Apache Kafka y efectuarles algún procesamiento. Ésta topologı́a,
será subida al nodo Storm Nimbus.

Figura 3.2: Arquitectura general del Clúster conectado a los programas cliente
Capı́tulo 4

Imágenes de Docker

El sistema desarrollado se compone de una serie de imágenes de Docker que al


ejecutarse como Docker Containers, formarán el sistema de procesamiento de datos
en tiempo real que se quiere demostrar. Las imágenes desarrolladas son:

Nodo de Apache Zookeeper (4.1).

Nodo de Apache Kafka (4.2).

Imagenes de Apache Storm (4.3).

• Nodo de Apache Storm Nimbus (4.3.1).


• Nodo de Apache Storm Supervisor (4.3.2).
• Nodo de Apache Storm UI (4.3.3).

Figura 4.1: Jerarquı́a de las imágenes de Docker desarrolladas

23
24 CAPÍTULO 4. IMÁGENES DE DOCKER

4.1. Nodo Apache Zookeeper


Basado en una imagen de Ubuntu 14.04 Trusty, se desarrolla la imagen de Apache
Zookeeper.
1 # Imagen b a s a d a en una imagen de Ubuntu 1 4 . 0 4
2 FROM ubuntu : t r u s t y
3 MAINTAINER J u l i a n Nonino <n o n i n o j u l i a n @ o u t l o o k . com>
4
5 # I n s t a l a r Z o o k e e p e r y l i m p i a r l a imagen l u e g o de l a i n s t a l a c i o n
6 # Hacer que l a c o n s o l a s e a e l d e s t i n o p o r d e f e c t o d e l l o g de Z o o k e e p e r
7 RUN apt−g e t u p d a t e −y && \
8 apt−g e t i n s t a l l −y t a r wget o p e n j d k −7−j r e −h e a d l e s s && \
9 apt−g e t i n s t a l l −y z o o k e e p e r && \
10 apt−g e t c l e a n && rm − r f / v a r / l i b / a p t / l i s t s /∗ /tmp/∗ / v a r /tmp/∗
11
12 # Exponer p u e r t o d e l c l i e n t e de Z o o k e e p e r ( 2 1 8 1 )
13 # Exponer e l p u e r t o de e m p a r e j a m i e n t o de Z o o k e e p e r ( 2 8 8 8 )
14 # Exponer e l p u e r t o p a r a e l l i d e r de Z o o k e e p e r ( 3 8 8 8 )
15 EXPOSE 2181 2888 3888
16
17 # C o p i a r e l s c r i p t de i n i c i o de Z o o k e e p e r
18 # El s c r i p t de i n i c i o de Z o o k e e p e r va a e s t a r en un a r c h i v o s e p a r a d o p a r a
19 # f a c i l i t a r l o s c a m b i o s de c o n f i g u r a c i o n .
20 # Lo recomendado e s que Z o o k e e p e r c o r r a r en un c l u s t e r de a l menos 3 n o d os
21 COPY s t a r t . s h / u s r / l o c a l / b i n
22 CMD [ ” / u s r / l o c a l / b i n / s t a r t . s h ” ]

Código 4.1: Dockerfile para un nodo de Apache Zookeeper


Para comenzar, se realiza una actualización del sistema operativo y luego se
procede con la instalación de Apache Zookeeper a través de la herramienta apt-get de
Ubuntu. Luego, se limpian todos los restos que hayan quedado de la instalación para
reducir el tamaño de la imagen. Posteriormente, se exponen los puertos necesarios
para la ejecución.
Con eso ya se podrı́a iniciar un único nodo de Apache Zookeeper agregando a la
imagen el siguiente comando.
1 CMD [ ” / u s r / s h a r e / z o o k e e p e r / b i n / z k S e r v e r . sh ” , ” s t a r t −f o r e g r o u n d ” ]

Como se desea correr un clúster de Apache Zookeeper, se deben hacer ciertas


configuraciones para que cada nodo pueda comunicarse con los demás. Por ésta
razón se implementa el siguiente script de inicio.
1 #! / b i n / bash
2
3 if [ [ −z ” $ {ZK ID} ” | | −z ” $ {ZK SERVERS} ” ]]; then
4 exit 1
5 fi
6
7 e c h o ” $ {ZK SERVERS} ” | t r ’ ’ ’ \ n ’ | t e e −a / e t c / z o o k e e p e r / c o n f / z o o . c f g
8 e c h o ” $ {ZK ID} ” | t e e / v a r / l i b / z o o k e e p e r / myid
9 / u s r / s h a r e / z o o k e e p e r / b i n / z k S e r v e r . s h s t a r t −f o r e g r o u n d

Código 4.2: Script de inicio para un nodo de Apache Zookeeper


Lo que hace el script 4.2 es recibir como parámetros una identificación para
el nodo y una lista de todos los nodos y configura Apache Zookeeper con esos
datos antes de iniciar el servicio. Se hace de ésta manera debido al alcance de ésta
demostración, en producción deberı́a utilizarse algún sistema de auto descubrimiento
de servicios para que los nodos se encuentren automáticamente al levantarse.
La construcción de ésta imagen, se realiza situado en la carpeta zookeeper del
repositorio, mediante el siguiente comando.
1 docker b u i l d −t jnonino / zookeeper .
4.2. NODO DE APACHE KAFKA 25

Luego de construir la imagen de Zookeeper, el clúster de tres nodos puede ser


lanzado mediante Docker Compose utilizando el siguiente comando:
1 d o c k e r −compose −f s t a r t z o o k e e p e r . yml up −d

El comando anterior inicia tres nodos de Zookeeper basados en la imagen cons-


truida anteriormente, el valor -d al final indica que se desea que los containers corran
en segundo plano.
1 version : ’2 ’
2
3 services :
4 zookeeper1 :
5 image : j n o n i n o / z o o k e e p e r
6 environment :
7 ZK SERVERS : ” s e r v e r .1=<ZOOKEEPER NODE IP> : 2 8 8 8 : 3 8 8 8
s e r v e r .2=<ZOOKEEPER NODE IP> : 2 8 8 9 : 3 8 8 9 s e r v e r .3=<ZOOKEEPER NODE IP> : 2 8 9 0 : 3 8 9 0 ”
8 ZK ID : 1
9 ports :
10 − ” 2181:2181 ”
11 − ” 2888:2888 ”
12 − ” 3888:3888 ”
13 zookeeper2 :
14 image : j n o n i n o / z o o k e e p e r
15 environment :
16 ZK SERVERS : ” s e r v e r .1=<ZOOKEEPER NODE IP> : 2 8 8 8 : 3 8 8 8
s e r v e r .2=<ZOOKEEPER NODE IP> : 2 8 8 9 : 3 8 8 9 s e r v e r .3=<ZOOKEEPER NODE IP> : 2 8 9 0 : 3 8 9 0 ”
17 ZK ID : 2
18 ports :
19 − ” 2182:2181 ”
20 − ” 2889:2889 ”
21 − ” 3889:3889 ”
22 zookeeper3 :
23 image : j n o n i n o / z o o k e e p e r
24 environment :
25 ZK SERVERS : ” s e r v e r .1=<ZOOKEEPER NODE IP> : 2 8 8 8 : 3 8 8 8
s e r v e r .2=<ZOOKEEPER NODE IP> : 2 8 8 9 : 3 8 8 9 s e r v e r .3=<ZOOKEEPER NODE IP> : 2 8 9 0 : 3 8 9 0 ”
26 ZK ID : 3
27 ports :
28 − ” 2183:2181 ”
29 − ” 2890:2890 ”
30 − ” 3890:3890 ”

Código 4.3: Script de inicio de Docker Compose para levantar un clúster de 3 nodos
de Apache Zookeeper
Notar que se utiliza la misma dirección IP para los tres nodos, ésto se debe a
que para la demostración que se hace en éste trabajo, todos los servicios correran en
un mismo servidor. Lógicamente, en un sistema en producción, atendiendo datos de
clientes, dichos servicios deberı́an correr en instancias diferenciadas para garantizar
la alta disponibilidad que Apache Zookeeper promete al utilizar más de un nodo.
Para comprobar que los tres nodos están corriendo correctamente, ejecutar el
siguiente comando:
1 f o r i i n { 2 1 8 1 . . 2 1 8 3 } ; do
2 e c h o mntr | nc <IP DEL HOST> $ i | grep zk followers ;
3 done

Se debe obtener un resultado como el siguiente mostrando que Zookeeper está


corriendo y que hay dos nodos además del nodo lı́der.
1 zk followers 2

4.2. Nodo de Apache Kafka


De la misma manera que los nodos de Apache Zookeeper, ésta imagen es ba-
sada en la imagen Ubuntu 14.04 Trusty. Y, como primera medida, se procede a la
actualización del sistema operativo.
26 CAPÍTULO 4. IMÁGENES DE DOCKER

1 # Imagen b a s a d a en una imagen de Ubuntu 1 4 . 0 4


2 FROM ubuntu : t r u s t y
3 MAINTAINER J u l i a n Nonino <n o n i n o j u l i a n @ o u t l o o k . com>
4
5 # Se u t i l i z a l a v e r s i o n de Kafka 0.9.0.1 con S c a l a 2.11
6 ENV SCALA VERSION 2 . 1 1
7 ENV KAFKA VERSION 0 . 9 . 0 . 1
8
9 # Obtener Apache Kafka 0 . 9 . 0 . 1
10 RUN apt−g e t u p d a t e −y && \
11 apt−g e t i n s t a l l −y t a r wget o p e n j d k −7−j r e −h e a d l e s s && \
12 apt−g e t c l e a n && rm − r f / v a r / l i b / a p t / l i s t s /∗ /tmp/∗ / v a r /tmp/∗ && \
13 wget −q
h t t p : / / a r t f i l e s . o r g / a p a c h e . o r g / k a f k a /$KAFKA VERSION/kafka $SCALA VERSION−$KAFKA VERSION . t g z
−O /tmp/kafka $SCALA VERSION−$KAFKA VERSION . t g z && \
14 t a r x f z /tmp/kafka $SCALA VERSION−$KAFKA VERSION . t g z −C / o p t && \
15 rm − r f /tmp/kafka $SCALA VERSION−$KAFKA VERSION . t g z
16
17 # C o p i a r e l s c r i p t de i n i c i o de Kafka e I n i c i a r Kafka
18 ENV KAFKA HOME / o p t /kafka $SCALA VERSION−$KAFKA VERSION
19 COPY s t a r t . s h / u s r / l o c a l / b i n
20 CMD [ ” / u s r / l o c a l / b i n / s t a r t . s h ” ]

Código 4.4: Dockerfile para un nodo de Apache Kafka


En éste caso, se proceden a instalar herramientas como wget, tar y Java 7 JRE
necesarias para que corra Apache Kafka. Debido a que Kafka no puede ser instalado
mediante apt-get, es necesario descargar el archivo .tar, descomprimirlo y ejecutarlo.
Luego de configurar el nodo, es posible lanzar Kafka mediante un script utilizado
para configurar variables antes de la ejecución.
1 #! / b i n / bash
2
3 i f [ [ −z ”$KAFKA BROKER ID” ] ] ; t h e n
4 e x p o r t KAFKA BROKER ID=$KAFKA ADVERTISED PORT
5 fi
6 i f [ [ −z ”$KAFKA LOG DIRS” ] ] ; t h e n
7 e x p o r t KAFKA LOG DIRS=” / k a f k a / k a f k a −l o g s −$KAFKA BROKER ID”
8 fi
9 i f [ [ −n ”$KAFKA HEAP OPTS” ] ] ; t h e n
10 s e d −r − i ” s / ( e x p o r t KAFKA HEAP OPTS)=\” ( . ∗ ) \ ”/\1=\”$KAFKA HEAP OPTS\ ” / g ”
$KAFKA HOME/ b i n / k a f k a −s e r v e r −s t a r t . s h
11 u n s e t KAFKA HEAP OPTS
12 f i
13
14 f o r VAR i n ‘ env ‘
15 do
16 i f [ [ $VAR =˜ ˆKAFKA && ! $VAR =˜ ˆKAFKA HOME ] ] ; t h e n
17 k a f k a n a m e =‘ e c h o ”$VAR” | s e d −r ” s /KAFKA ( . ∗ ) =.∗/\1/ g ” | t r ’ [ : u p p e r : ] ’ ’ [ : l o w e r : ] ’ | t r
.‘
18 e n v v a r =‘ e c h o ”$VAR” | s e d −r ” s / ( . ∗ ) =.∗/\1/ g ” ‘
19 i f e g r e p −q ” ( ˆ | ˆ # ) $ k a f k a n a m e=” $KAFKA HOME/ c o n f i g / s e r v e r . p r o p e r t i e s ; t h e n
20 s e d −r − i ”s@ ( ˆ | ˆ # ) ( $ k a f k a n a m e ) = ( . ∗ )@\2=$ { ! e n v v a r }@g”
$KAFKA HOME/ c o n f i g / s e r v e r . p r o p e r t i e s #n o t e t h a t no c o n f i g v a l u e s may c o n t a i n an ’@’
char
21 else
22 e c h o ” $ k a f k a n a m e=$ { ! e n v v a r } ” >> $KAFKA HOME/ c o n f i g / s e r v e r . p r o p e r t i e s
23 fi
24 fi
25 done
26
27 $KAFKA HOME/ b i n / k a f k a −s e r v e r −s t a r t . s h $KAFKA HOME/ c o n f i g / s e r v e r . p r o p e r t i e s

Código 4.5: Script de inicio para un nodo de Apache Kafka


La construcción de ésta imagen, se realiza situado en la carpeta kafka del repo-
sitorio, mediante el siguiente comando.
1 docker b u i l d −t jnonino / kafka .

Luego de construir la imagen, el clúster de tres nodos puede ser lanzado mediante
Docker Compose utilizando el siguiente comando:
1 d o c k e r −compose −f s t a r t k a f k a . yml up −d

El comando anterior inicia tres nodos de Kafka basados en la imagen construida


anteriormente, el valor -d al final indica que se desea que los containers corran en
segundo plano.
4.2. NODO DE APACHE KAFKA 27

1 version : ’2 ’
2
3 services :
4 kafka1 :
5 image : j n o n i n o / k a f k a
6 environment :
7 KAFKA HEAP OPTS : ”−Xmx256M −Xms256M”
8 KAFKA BROKER ID : 1
9 KAFKA ZOOKEEPER CONNECT:
”<ZOOKEEPER NODE IP>:2181 , <ZOOKEEPER NODE IP>:2182 , <ZOOKEEPER NODE IP>:2183 ”
10 KAFKA ADVERTISED PORT : 9092
11 KAFKA ADVERTISED HOST NAME : <KAFKA NODE IP>
12 ports :
13 − ” 9092:9092 ”
14 kafka2 :
15 image : j n o n i n o / k a f k a
16 environment :
17 KAFKA HEAP OPTS : ”−Xmx256M −Xms256M”
18 KAFKA BROKER ID : 2
19 KAFKA ZOOKEEPER CONNECT:
”<ZOOKEEPER NODE IP>:2181 , <ZOOKEEPER NODE IP>:2182 , <ZOOKEEPER NODE IP>:2183 ”
20 KAFKA ADVERTISED PORT : 9093
21 KAFKA ADVERTISED HOST NAME : <KAFKA NODE IP>
22 ports :
23 − ” 9093:9092 ”
24 kafka3 :
25 image : j n o n i n o / k a f k a
26 environment :
27 KAFKA HEAP OPTS : ”−Xmx256M −Xms256M”
28 KAFKA BROKER ID : 3
29 KAFKA ZOOKEEPER CONNECT:
”<ZOOKEEPER NODE IP>:2181 , <ZOOKEEPER NODE IP>:2182 , <ZOOKEEPER NODE IP>:2183 ”
30 KAFKA ADVERTISED PORT : 9094
31 KAFKA ADVERTISED HOST NAME : <KAFKA NODE IP>
32 ports :
33 − ” 9094:9092 ”

Código 4.6: Script de inicio de Docker Compose para levantar un clúster de 3 nodos
de Apache Kafka
Notar que se utiliza la misma dirección IP para los tres nodos, ésto se debe a
que para la demostración que se hace en éste trabajo, todos los servicios correran en
un mismo servidor. Lógicamente, en un sistema en producción, atendiendo datos de
clientes, dichos servicios deberı́an correr en instancias diferenciadas para garantizar
la alta disponibilidad que Apache Kafka promete al utilizar más de un nodo.
Para comprobar que los tres nodos están corriendo correctamente, es necesario
descargar Kafka para interactuar con el servidor corriendo1 .
Correr los siguientes comandos para crear un tópico de prueba y obtener infor-
mación del mismo:
1 . / b i n / k a f k a −t o p i c s . s h −−c r e a t e −−t o p i c t e s t −− p a r t i t i o n s 3 −−z o o k e e p e r
2 <IP ZOOKEEPER> −−r e p l i c a t i o n −f a c t o r 2
3 . / b i n / k a f k a −t o p i c s . s h −−d e s c r i b e −−t o p i c t e s t −−z o o k e e p e r <IP ZOOKEEPER>

Para enviar datos al servidor de Kafka, se debe generar una lista de los nodos
de Kafka (brokers) para posteriormente enviarle mensajes y finalmente, con otra
aplicación provista por Kafka, leerlos.
1 BROKER LIST=<IP NODO KAFKA>:9092 , <IP NODO KAFKA>:9093 , <IP NODO KAFKA>:9094
2 / b i n / k a f k a −c o n s o l e −p r o d u c e r . s h −−t o p i c t e s t −−b r o k e r − l i s t =”$BROKER LIST”
3 . / b i n / k a f k a −c o n s o l e −consumer . sh −−z o o k e e p e r <IP ZOOKEEPER> −−t o p i c t e s t
4 −−from−b e g i n n i n g

1
Descargar Apache Kafka desde https://www.apache.org Version 0.9.0.1 con Scala 2.11
28 CAPÍTULO 4. IMÁGENES DE DOCKER

4.3. Apache Storm


Un clúster de Apache Storm necesita contar con varios tipos de nodos para su
operación. En las siguientes secciones se mostrarán las imágenes de Docker creadas
para cada uno de ellos y se comentará cual es el propósito de dicho nodo.

Figura 4.2: Arquitectura básica de un clúster de Apache Storm[9]

También basada en una imagen de docker de Ubuntu 14.04 Trusty se crea la


imagen base para todos los servicios de Storm. Cada nodo de Storm debe correr dos
4.3. APACHE STORM 29

aplicaciones storm-supervisor y storm-logviewer. Para correr ambos procesos en un


container de Docker, se utiliza un sistema de control de procesos hecho en Python
llamado supervisord.
Es necesario correr tres servicios, Storm Nimbus, Storm UI y al menos un nodo
Storm Supervisor. Dado que los tres compartes muchas configuraciones, se crea una
imagen base de Apache Storm de la cual derivarán los tres servicios necesarios.
1 # Imagen b a s a d a en una imagen de Ubuntu 1 4 . 0 4
2 FROM ubuntu : t r u s t y
3 MAINTAINER J u l i a n Nonino <n o n i n o j u l i a n @ o u t l o o k . com>
4
5 ENV STORM VERSION 0 . 9 . 7
6
7 # I n s t a l a r d e p e n d e n c i a s de Python p a r a Storm y S u p e r v i s o r D
8 # L i m p i a r l a imagen l u e g o de l a s i n s t a l a c i o n e s y o b t e n e r Storm
9 RUN apt−g e t u p d a t e −y && \
10 apt−g e t i n s t a l l −y t a r wget o p e n j d k −7−j r e −h e a d l e s s && \
11 apt−g e t i n s t a l l −y python−s e t u p t o o l s python−v i r t u a l e n v s u p e r v i s o r −−f i x −m i s s i n g && \
12 apt−g e t c l e a n && rm − r f / v a r / l i b / a p t / l i s t s /∗ /tmp/∗ / v a r /tmp/∗ && \
13 wget −q −O −
h t t p : / / a p a c h e . m i r r o r . d i g i o n l i n e . de / s to r m / apache−storm−$STORM VERSION/ apache−storm−$STORM VERSION . t a r . g z
| t a r −x z f − −C / o p t
14
15 ENV STORM HOME / o p t / apache−storm−$STORM VERSION
16
17 RUN groupadd s t or m ; u s e r a d d −−g i d s to r m −−home−d i r /home/ st o r m −−c r e a t e −home −− s h e l l / b i n / bash
s to r m ; chown −R s t or m : s t o rm $STORM HOME; mkdir / v a r / l o g / s to r m ; chown −R s t or m : s t or m
/ v a r / l o g / s t or m && \
18 l n −s $STORM HOME/ b i n / s t or m / u s r / b i n / st o r m
19
20 COPY s t o rm . yaml $STORM HOME/ c o n f
21 COPY c l u s t e r . xml $STORM HOME/ l o g b a c k
22 COPY c o n f i g −s u p e r v i s o r d . s h / u s r / b i n
23 COPY s t a r t −s u p e r v i s o r . sh / u s r / b i n
24
25 RUN e c h o [ s u p e r v i s o r d ] | t e e −a / e t c / s u p e r v i s o r / s u p e r v i s o r d . c o n f ; e c h o nodaemon=t r u e | t e e −a
/ etc / supervisor / supervisord . conf

Código 4.7: Dockerfile para la imagen base de Storm

Se comienza actualizando el sistema operativo, luego se instalan todas las herra-


mientas necesarias como tar, wget y supervisord. Como no es posible la instalación
de Apache Storm desde la herramienta apt-get, se debe realizar un procedimien-
to similar al seguido con Apache Kafka. Por último, se copian varios archivos de
configuración que son necesarios para la ejecución de supervisord y Apache Storm.
1 s t or m . z o o k e e p e r . s e r v e r s :
2 %zk %
3 nimbus . h o s t : ” %nimbus %”
4 drpc . s e r v e r s :
5 − ” %nimbus %”
6 d r p c . p o r t : 3772
7 d r p c . i n v o c a t i o n s . p o r t : 3773
8
9 d r p c . c h i l d o p t s : ”−Xmx64m −Djava . n e t . p r e f e r I P v 4 S t a c k=t r u e ”
10 nimbus . c h i l d o p t s : ”−Xmx64m −Djava . n e t . p r e f e r I P v 4 S t a c k=t r u e ”
11 u i . c h i l d o p t s : ”−Xmx64m −Djava . n e t . p r e f e r I P v 4 S t a c k=t r u e ”
12 s u p e r v i s o r . c h i l d o p t s : ”−Xmx64m −Djava . n e t . p r e f e r I P v 4 S t a c k=t r u e ”
13 w o r k e r . c h i l d o p t s : ”−Xmx64m −Djava . n e t . p r e f e r I P v 4 S t a c k=t r u e ”
14 l o g v i e w e r . c h i l d o p t s : ”−Xmx64m −Djava . n e t . p r e f e r I P v 4 S t a c k=t r u e ”
15 s t or m . c l u s t e r . mode: ” d i s t r i b u t e d ”

Código 4.8: Archivo de configuración de Apache Storm storm.yaml

1 <? xml v e r s i o n=” 1 . 0 ” e n c o d i n g=”UTF−8” ?>


2
3 <c o n f i g u r a t i o n s c a n=” t r u e ” s c a n P e r i o d=” 60 s e c o n d s ”>
4 <a p p e n d e r name=”A1” c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . R o l l i n g F i l e A p p e n d e r ”>
5 < f i l e >/ v a r / l o g / s t o rm /${ l o g f i l e . name}</ f i l e >
6 < r o l l i n g P o l i c y c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . F i x e d W i n d o w R o l l i n g P o l i c y ”>
7 <f i l e N a m e P a t t e r n>/ v a r / l o g / s t or m /${ l o g f i l e . name}. % i</ f i l e N a m e P a t t e r n>
8 <minIndex>1</ minIndex>
9 <maxIndex>9</ maxIndex>
10 </ r o l l i n g P o l i c y>
11
12 < t r i g g e r i n g P o l i c y c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . S i z e B a s e d T r i g g e r i n g P o l i c y ”>
13 <m a x F i l e S i z e>100MB</ m a x F i l e S i z e>
30 CAPÍTULO 4. IMÁGENES DE DOCKER

14 </ t r i g g e r i n g P o l i c y>
15
16 <e n c o d e r>
17 <p a t t e r n> %d{ yyyy−MM−dd HH:mm:ss} %c {1} [ %p ] % m%n</ p a t t e r n>
18 </ e n c o d e r>
19 </ a p p e n d e r>
20
21 <a p p e n d e r name=”ACCESS” c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . R o l l i n g F i l e A p p e n d e r ”>
22 < f i l e >/ v a r / l o g / s t o rm / a c c e s s . l o g</ f i l e >
23 < r o l l i n g P o l i c y c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . F i x e d W i n d o w R o l l i n g P o l i c y ”>
24 <f i l e N a m e P a t t e r n>/ v a r / l o g / s t or m / a c c e s s . l o g . % i</ f i l e N a m e P a t t e r n>
25 <minIndex>1</ minIndex>
26 <maxIndex>9</ maxIndex>
27 </ r o l l i n g P o l i c y>
28
29 < t r i g g e r i n g P o l i c y c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . S i z e B a s e d T r i g g e r i n g P o l i c y ”>
30 <m a x F i l e S i z e>100MB</ m a x F i l e S i z e>
31 </ t r i g g e r i n g P o l i c y>
32
33 <e n c o d e r>
34 <p a t t e r n> %d{ yyyy−MM−dd HH:mm:ss} %c {1} [ %p ] % m%n</ p a t t e r n>
35 </ e n c o d e r>
36 </ a p p e n d e r>
37
38 <a p p e n d e r name=”METRICS” c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . R o l l i n g F i l e A p p e n d e r ”>
39 < f i l e >/ v a r / l o g / s t o rm / m e t r i c s . l o g</ f i l e >
40 < r o l l i n g P o l i c y c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . F i x e d W i n d o w R o l l i n g P o l i c y ”>
41 <f i l e N a m e P a t t e r n>m e t r i c s . l o g . % i</ f i l e N a m e P a t t e r n>
42 <minIndex>1</ minIndex>
43 <maxIndex>9</ maxIndex>
44 </ r o l l i n g P o l i c y>
45
46 < t r i g g e r i n g P o l i c y c l a s s=” ch . q o s . l o g b a c k . c o r e . r o l l i n g . S i z e B a s e d T r i g g e r i n g P o l i c y ”>
47 <m a x F i l e S i z e>2MB</ m a x F i l e S i z e>
48 </ t r i g g e r i n g P o l i c y>
49
50 <e n c o d e r>
51 <p a t t e r n> %d %−8r % m%n</ p a t t e r n>
52 </ e n c o d e r>
53 </ a p p e n d e r>
54
55 <r o o t l e v e l =”INFO”>
56 <appender−r e f r e f=”A1” />
57 </ r o o t>
58
59 <l o g g e r name=” b a c k t y p e . s t or m . m e s s a g i n g . n e t t y ”>
60 < l e v e l v a l u e=”WARN” />
61 <appender−r e f r e f=”A1” />
62 </ l o g g e r>
63
64 <l o g g e r name=” b a c k t y p e . s t or m ”>
65 < l e v e l v a l u e=”INFO” />
66 <appender−r e f r e f=”A1” />
67 </ l o g g e r>
68
69 <l o g g e r name=” b a c k t y p e . s t or m . s e c u r i t y . auth . a u t h o r i z e r ” a d d i t i v i t y=” f a l s e ”>
70 < l e v e l v a l u e=”INFO” />
71 <appender−r e f r e f=”ACCESS” />
72 </ l o g g e r>
73
74 <l o g g e r name=” b a c k t y p e . s t or m . m e t r i c . L o g g i n g M e t r i c s C o n s u m e r ” a d d i t i v i t y=” f a l s e ” >
75 < l e v e l v a l u e=”INFO” />
76 <appender−r e f r e f=”METRICS” />
77 </ l o g g e r>
78
79 </ c o n f i g u r a t i o n>

Código 4.9: Archivo de configuración de Apache Storm cluster.xml


1 #! / u s r / b i n / env bash
2
3 e c h o [ program : storm−$1 ] | t e e −a / e t c / s u p e r v i s o r / c o n f . d/ storm−$1 . c o n f
4 e c h o command=s t o rm $1 | t e e −a / e t c / s u p e r v i s o r / c o n f . d/ storm−$1 . c o n f
5 e c h o d i r e c t o r y =/home/ s t o rm | t e e −a / e t c / s u p e r v i s o r / c o n f . d/ storm−$1 . c o n f
6 e c h o a u t o r e s t a r t=t r u e | t e e −a / e t c / s u p e r v i s o r / c o n f . d/ storm−$1 . c o n f
7 e c h o u s e r=r o o t | t e e −a / e t c / s u p e r v i s o r / c o n f . d/ storm−$1 . c o n f

Código 4.10: Archivo de configuración de SupervisorD config-supervisord.sh


1 #! / b i n / bash
2
3 ZK SED=” ”
4 f o r i i n ‘ e c h o ”$ZK” | t r ’ , ’ ” \\n” ‘ ; do
5 i f [ ” x $ i ” != ” x ” ] ; t h e n
6 i f [ ”x$ZK SED” != ” x ” ] ; t h e n
7 ZK SED=‘ e c h o ”$ZK SED\\n − \ ” $ i \ ” ” ‘
8 else
4.3. APACHE STORM 31

9 ZK SED=‘ e c h o ” − \” $ i \”” ‘
10 fi
11 fi
12 done
13
14 s e d − i −e ” s/ %zk %/$ZK SED/ g ” $STORM HOME/ c o n f / s t o rm . yaml
15 s e d − i −e ” s/ %nimbus %/$NIMBUS HOST/ g ” $STORM HOME/ c o n f / s t o rm . yaml
16 e c h o ” s t o rm . l o c a l . hostname : $HOST NAME” >> $STORM HOME/ c o n f / s t o rm . yaml
17
18 supervisord

Código 4.11: Script de inicio de Storm

4.3.1. Storm Nimbus


Storm Nimbus, es el nodo maestro y coordinador de Apache Storm. Es el encar-
gado de asignar las tareas a los nodos trabajadores (workers), monitorear el cluster
buscando fallas y distribuir el código (topologı́as) entre todos los nodos del cluster.
Corre un proceso demonio llamado nimbus.
1 FROM j n o n i n o / storm−b a s e
2 MAINTAINER J u l i a n Nonino <n o n i n o j u l i a n @ o u t l o o k . com>
3
4 RUN / u s r / b i n / c o n f i g −s u p e r v i s o r d . s h nimbus && \
5 / u s r / b i n / c o n f i g −s u p e r v i s o r d . s h d r p c
6
7 # Exponer nimbus . t h r i f t . p o r t
8 # Exponer d r p c . p o r t
9 # Exponer d r p c . i n v o c a t i o n s . p o r t
10 EXPOSE 6627 3772 3773
11
12 # Iniciar supervisor
13 CMD / u s r / b i n / s t a r t −s u p e r v i s o r . s h

Código 4.12: Dockerfile para la imagen de Storm Nimbus

4.3.2. Storm Supervisor


Son los nodos trabajadores (workers). Corren un proceso demonio llamado su-
pervisor . Son nodos que estan escuchando permanente al nodo nimbus esperando
que les sean asignadas tareas.
1 FROM j n o n i n o / storm−b a s e
2 MAINTAINER J u l i a n Nonino <n o n i n o j u l i a n @ o u t l o o k . com>
3
4 # P u e r t o Workers 1 , 2 , 3 y 4
5 # P u e r t o LogViewer
6 EXPOSE 6700 6701 6702 6703 8000
7
8 RUN / u s r / b i n / c o n f i g −s u p e r v i s o r d . s h s u p e r v i s o r && \
9 / u s r / b i n / c o n f i g −s u p e r v i s o r d . s h l o g v i e w e r
10
11 # I n i c i a r Supervisor
12 CMD / u s r / b i n / s t a r t −s u p e r v i s o r . s h

Código 4.13: Dockerfile para la imagen de Storm Supervisor

4.3.3. Storm UI
Ésta imágen de Docker es la encargada de levantar la interfaz gráfica de Apache
Storm.
1 FROM j n o n i n o / storm−b a s e
2 MAINTAINER J u l i a n Nonino <n o n i n o j u l i a n @ o u t l o o k . com>
3
4 # P u e r t o I n t e r f a z Web
5 EXPOSE 8080
32 CAPÍTULO 4. IMÁGENES DE DOCKER

6
7 RUN / u s r / b i n / c o n f i g −s u p e r v i s o r d . s h u i
8
9 # I n i c i a r Supervisor
10 CMD / u s r / b i n / s t a r t −s u p e r v i s o r . s h

Código 4.14: Dockerfile para la imagen de Storm UI

4.3.4. Iniciar Apache Storm


Finalizada la construcción de todas las imágenes anteriores, es posible poner a
correr un servidor de Apache Storm utilizando el siguiente comando:
1 d o c k e r −compose −f s t a r t s t o r m . yml up −d

El comando anterior inicia los tres servicios de Storm necesarios, Storm Nim-
bus, Storm Supervisor y Storm UI, el valor -d al final indica que se desea que los
containers corran en segundo plano.
1 version : ’2 ’
2
3 services :
4 storm−nimbus :
5 image : j n o n i n o / storm−nimbus
6 environment :
7 NIMBUS HOST : ”<NIMBUS HOST IP>”
8 ZK : ”<ZOOKEEPER NODE IP>”
9 ports :
10 − ” 3772:3772 ”
11 − ” 3773:3773 ”
12 − ” 6627:6627 ”
13 storm−u i :
14 image : j n o n i n o / storm−u i
15 environment :
16 HOST NAME: ”<UI NODE IP>”
17 NIMBUS HOST : ”<NIMBUS HOST IP>”
18 ZK : ”<ZOOKEEPER NODE IP>:2181 , <ZOOKEEPER NODE IP>:2182 , <ZOOKEEPER NODE IP>:2183 ”
19 ports :
20 − ” 8080:8080 ”
21 storm−s u p e r v i s o r :
22 image : j n o n i n o / storm−s u p e r v i s o r
23 environment :
24 HOST NAME: ”<SUPERVISOR NODE IP>”
25 NIMBUS HOST : ”<NIMBUS HOST IP>”
26 ZK : ”<ZOOKEEPER NODE IP>”
27 ports :
28 − ” 6700:6700 ”
29 − ” 6701:6701 ”
30 − ” 6702:6702 ”
31 − ” 6703:6703 ”
32 − ” 8000:8000 ”

Código 4.15: Script de inicio de Docker Compose para levantar Apache Storm
Luego, es posible comprobar el estado de Storm accediendo a la interfaz web
http://<DOCKER_HOST_IP>:8080.
Capı́tulo 5

Aplicaciones Desarrolladas

En éste capı́tulo se mostrarán las aplicaciones desarrolladas para hacer uso del
sistema de procesamiento de datos presentado en secciones anteriores (3).
Los datos ingresan al sistema a través de Apache Kafka, por ello, se comenzó
generando una aplicación para enviar datos al servicio de Apache Kafka y otra para
consumirlos directmente desde Apache Kafka.
También, se desarrollo una aplicación de Apache Storm, topologı́a, que recupe-
rará los datos del servicio de Apache Kafka teniendolos disponibles para procesarlos
en Apache Storm.

5.1. Productor de Datos de Kafka


Para generar una entrada de datos al sistema, se implementó una aplicación Java
encargada de publicar datos en Apacker Kafka. Paralelamente, se implementó una
aplicación consumidora de datos con el objetivo de comprobar que la producción de
datos es correcta.

Existe una clase Main encargada de recibir los parámetros de entrada de la


aplicación e inicializar hilos de ejecución productores y/o consumidores de datos
según sea necesario.

33
34 CAPÍTULO 5. APLICACIONES DESARROLLADAS

1 p a c k a g e a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . k a f k a ;
2
3 import org . apache . kafka . c l i e n t s . consumer . ConsumerConfig ;
4 import org . apache . kafka . c l i e n t s . consumer . KafkaConsumer ;
5 import org . apache . kafka . c l i e n t s . producer . KafkaProducer ;
6 import org . apache . kafka . c l i e n t s . producer . ProducerConfig ;
7 import org . apache . k a f k a . common . serialization . IntegerDeserializer ;
8 import org . apache . k a f k a . common . serialization . IntegerSerializer ;
9 import org . apache . k a f k a . common . serialization . StringDeserializer ;
10 import org . apache . k a f k a . common . serialization . StringSerializer ;
11 import org . apache . logging . log4j . LogManager ;
12 import org . apache . logging . log4j . Logger ;
13
14 import java . u t i l . Arrays ;
15 import java . u t i l . P r o p e r t i e s ;
16
17 /∗ ∗
18 ∗ C r e a t e d by j n o n i n o on 3 0 / 0 3 / 2 0 1 6 .
19 ∗/
20 p u b l i c c l a s s Main {
21
22 private static final Logger l o g g e r = LogManager . g e t L o g g e r ( Main . c l a s s ) ;
23
24 public static v o i d main ( S t r i n g [ ] args ) {
25
26 String kafkaBrokerList = null ;
27
28 l o n g producerRateTime = 1 0 0 0 ;
29
30 if( a r g s . l e n g t h != 4 ) {
31 l o g g e r . e r r o r ( ” S h o u l d run w i t h f o u r a r g u m e n t s ” ) ;
32 l o g g e r . e r r o r ( ” Uso : j a v a − j a r k a f k a −<VERSION>−j a r −with−d e p e n d e n c i e s . j a r
<KAFKA BROKER LIST> <ENABLE PRODUCER> <MESSAGE PRODUCTION RATE>
<ENABLE TEST CONSUMER>” ) ;
33 l o g g e r . e r r o r ( ”<ENABLE PRODUCER>: <y e s | no>” ) ;
34 l o g g e r . e r r o r ( ”<MESSAGE PRODUCTION RATE>: <l o n g v a l u e >” ) ;
35 l o g g e r . e r r o r ( ”<ENABLE TEST CONSUMER>: <y e s | no>” ) ;
36 System . e x i t ( 1 ) ;
37 } else {
38 kafkaBrokerList = args [ 0 ] ;
39 String enableProducer = args [ 1 ] ;
40 p r o d u c e r R a t e T i m e = Long . p a r s e L o n g ( a r g s [ 2 ] ) ;
41 S t r i n g en a bl e Co n su m e r = a r g s [ 3 ] ;
42
43 l o g g e r . i n f o ( ” Kafka B r o k e r List : ” + kafkaBrokerList ) ;
44
45 switch ( enableProducer ) {
46 case ” yes ” :
47 l o g g e r . i n f o ( ” S t a r t i n g Kafka P r o d u c e r ” ) ;
48 D a t a P r o d u c e r p r o d u c e r T h r e a d = new D a t a P r o d u c e r ( k a f k a B r o k e r L i s t ,
producerRateTime ) ;
49 producerThread . s t a r t ( ) ;
50 break ;
51 c a s e ” no ” :
52 l o g g e r . i n f o ( ”The p r o d u c e r w i l l n o t run ” ) ;
53 break ;
54 default :
55 l o g g e r . e r r o r ( ”No v a l i d v a l u e f o r ENABLE PRODUCER − Use y e s o r no ” ) ;
56 System . e x i t ( 1 ) ;
57 break ;
58 }
59
60 s w i t c h ( e n ab l eC o ns u me r ) {
61 case ” yes ” :
62 l o g g e r . i n f o ( ” S t a r t i n g Kafka T e s t Consumer ” ) ;
63 DataConsumer consumerThread = new DataConsumer ( k a f k a B r o k e r L i s t ) ;
64 consumerThread . s t a r t ( ) ;
65 break ;
66 c a s e ” no ” :
67 l o g g e r . i n f o ( ”The t e s t consumer w i l l n o t run ” ) ;
68 break ;
69 default :
70 l o g g e r . e r r o r ( ”No v a l i d v a l u e f o r ENABLE TEST CONSUMER − Use y e s o r no ” ) ;
71 System . e x i t ( 1 ) ;
72 break ;
73 }
74 }
75 }
76 }

Código 5.1: Clase Main de la aplicación de Apache Kafka


Como parámetros, se reciben la lista de brokers de Apache Kafka, indicadores
yes—no para habilitar el hilo productor y/o el hilo consumidor y finalmente, un
parámetro númerico indicando, en milisegundos, la tasa de producción de mensajes.
5.1. PRODUCTOR DE DATOS DE KAFKA 35

La producción de datos se hacer en una clase llamada DataProducer (5.2) que


extiende de la clase Thread de Java. Al momento de instanciar un objeto de ésta
clase, se debe enviar la lista de brokers de Kafka y el valor de milisegundos que
indica la tasa de producción de mensajes.
1 p a c k a g e a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . k a f k a ;
2
3 import org . apache . kafka . c l i e n t s . producer . KafkaProducer ;
4 import org . apache . kafka . c l i e n t s . producer . ProducerConfig ;
5 import org . apache . kafka . c l i e n t s . producer . ProducerRecord ;
6 import org . apache . k a f k a . common . serialization . IntegerSerializer ;
7 import org . apache . k a f k a . common . serialization . StringSerializer ;
8 import org . apache . logging . log4j . LogManager ;
9 import org . apache . logging . log4j . Logger ;
10
11 import java . i o . ∗ ;
12 import java . u t i l . P r o p e r t i e s ;
13
14 /∗ ∗
15 ∗ C r e a t e d by J u l i à ¡ n on 2 / 4 / 2 0 1 6 .
16 ∗/
17 p u b l i c c l a s s D a t a P r o d u c e r e x t e n d s Thread {
18
19 private static final Logger l o g g e r = LogManager . g e t L o g g e r ( D a t a P r o d u c e r . c l a s s ) ;
20
21 private f i n a l K a f k a P r o d u c e r<I n t e g e r , S t r i n g > p r o d u c e r ;
22 private long producingRateMillis = 1000;
23
24 p u b l i c DataProducer ( S t r i n g k a f k a B r o k e r L i s t , l o n g p r o d u c i n g R a t e I n M i l l i s ) {
25 P r o p e r t i e s p r o p e r t i e s = new P r o p e r t i e s ( ) ;
26 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . BOOTSTRAP SERVERS CONFIG, k a f k a B r o k e r L i s t ) ;
27 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . ACKS CONFIG, ” a l l ” ) ;
28 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . RETRIES CONFIG , 2 ) ;
29 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . BATCH SIZE CONFIG , 1 6 3 8 4 ) ;
30 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . LINGER MS CONFIG , 0 ) ;
31 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . KEY SERIALIZER CLASS CONFIG ,
I n t e g e r S e r i a l i z e r . c l a s s . getName ( ) ) ;
32 p r o p e r t i e s . put ( P r o d u c e r C o n f i g . VALUE SERIALIZER CLASS CONFIG ,
S t r i n g S e r i a l i z e r . c l a s s . getName ( ) ) ;
33 this . producingRateMillis = producingRateInMillis ;
34 t h i s . p r o d u c e r = new K a f k a P r o d u c e r <>( p r o p e r t i e s ) ;
35 }
36
37 p u b l i c v o i d run ( ) {
38 try {
39 InputStream i n = g e t C l a s s ( ) . getResourceAsStream ( ”/ data . t x t ” ) ;
40 B u f f e r e d R e a d e r b u f f e r e d R e a d e r = new B u f f e r e d R e a d e r ( new I n p u t S t r e a m R e a d e r ( i n ) ) ;
41 String line = null ;
42 i n t count = 0 ;
43 w h i l e ( ( l i n e = b u f f e r e d R e a d e r . r e a d L i n e ( ) ) != n u l l ) {
44 try {
45 sleep ( this . producingRateMillis ) ;
46 } catch ( InterruptedException e ) {
47 l o g g e r . e r r o r ( ” I n t e r r u p t e d Exception w h i l e w a i t i n g producing r a t e time ” ) ;
48 }
49 c o u n t += 1 ;
50 P r o d u c e r R e c o r d<I n t e g e r , S t r i n g > r e c o r d = new P r o d u c e r R e c o r d <>(” m e t r i c s ” , count ,
line ) ;
51 l o g g e r . i n f o ( ” Sending message : ” + l i n e ) ;
52 t h i s . producer . send ( r e c o r d ) ;
53 }
54 bufferedReader . c l o s e () ;
55 l o g g e r . i n f o ( ”SUCCESS − ” + c o u n t + ” m e t r i c r e c o r d s were p u b l i s h e d . ” ) ;
56 } c a t c h ( F i l e N o t F o u n d E x c e p t i o n ex ) {
57 l o g g e r . e r r o r ( ” Unable t o open f i l e d a t a . t x t ” ) ;
58 ex . p r i n t S t a c k T r a c e ( ) ;
59 } c a t c h ( I O E x c e p t i o n ex ) {
60 l o g g e r . e r r o r ( ” Error r e a d i n g f i l e data . t x t ” ) ;
61 ex . p r i n t S t a c k T r a c e ( ) ;
62 } finally {
63 producer . c l o s e () ;
64 }
65 }
66 }

Código 5.2: Productor de datos de la aplicación de Apache Kafka


El método run lee datos desde un archivo texto donde cada lı́nea es un mensaje
independiente y los publica en Apache Kafka. El formato de los mensajes incluye
pais, provincia, ciudad, tiempo, temperatura, humedad, presión, velocidad y direc-
ción del viento.
1 A r g e n t i n a , Catamarca , A c o n q u i j a , 1 2 0 7 1 0 5 2 0 0 0 0 0 , 2 5 . 2 , 4 9 . 7 , 9 6 2 , 5 . 1 ,SUR
36 CAPÍTULO 5. APLICACIONES DESARROLLADAS

Para el caso del consumidor de datos, se repite el mismo esquema de instanciar


un objeto que extiende la clase Thread de Java recibiendo como parámetro la lista
de brokers de Kafka.
En el método run, se recuperan mensajes desde el servidor y se los imprimi en
la consola de usuario.
1 p a c k a g e a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . k a f k a ;
2
3 import org . apache . kafka . c l i e n t s . consumer . ConsumerConfig ;
4 import org . apache . kafka . c l i e n t s . consumer . ConsumerRecord ;
5 import org . apache . kafka . c l i e n t s . consumer . ConsumerRecords ;
6 import org . apache . kafka . c l i e n t s . consumer . KafkaConsumer ;
7 import org . apache . k a f k a . common . serialization . IntegerDeserializer ;
8 import org . apache . k a f k a . common . serialization . StringDeserializer ;
9 import org . apache . logging . log4j . LogManager ;
10 import org . apache . logging . log4j . Logger ;
11
12 import java . u t i l . Arrays ;
13 import java . u t i l . P r o p e r t i e s ;
14
15 /∗ ∗
16 ∗ C r e a t e d by J u l i à ¡ n on 1 2 / 5 / 2 0 1 6 .
17 ∗/
18 p u b l i c c l a s s DataConsumer e x t e n d s Thread {
19
20 private static final Logger l o g g e r = LogManager . g e t L o g g e r ( DataConsumer . c l a s s ) ;
21
22 private final KafkaConsumer<I n t e g e r , S t r i n g > consumer ;
23
24 p u b l i c DataConsumer ( S t r i n g k a f k a B r o k e r L i s t ) {
25 P r o p e r t i e s p r o p e r t i e s = new P r o p e r t i e s ( ) ;
26 p r o p e r t i e s . put ( ConsumerConfig . BOOTSTRAP SERVERS CONFIG, k a f k a B r o k e r L i s t ) ;
27 p r o p e r t i e s . put ( ConsumerConfig . GROUP ID CONFIG , ” t e s t ” ) ;
28 p r o p e r t i e s . put ( ConsumerConfig . ENABLE AUTO COMMIT CONFIG, t r u e ) ;
29 p r o p e r t i e s . put ( ConsumerConfig . KEY DESERIALIZER CLASS CONFIG ,
I n t e g e r D e s e r i a l i z e r . c l a s s . getName ( ) ) ;
30 p r o p e r t i e s . put ( ConsumerConfig . VALUE DESERIALIZER CLASS CONFIG ,
S t r i n g D e s e r i a l i z e r . c l a s s . getName ( ) ) ;
31 p r o p e r t i e s . put ( ConsumerConfig . SESSION TIMEOUT MS CONFIG , 1 0 0 0 0 ) ;
32 p r o p e r t i e s . put ( ConsumerConfig . FETCH MIN BYTES CONFIG , 5 0 0 0 0 ) ;
33 p r o p e r t i e s . put ( ConsumerConfig . RECEIVE BUFFER CONFIG , 2 6 2 1 4 4 ) ;
34 p r o p e r t i e s . put ( ConsumerConfig . MAX PARTITION FETCH BYTES CONFIG , 2 0 9 7 1 5 2 ) ;
35 t h i s . consumer = new KafkaConsumer<>( p r o p e r t i e s ) ;
36 t h i s . consumer . s u b s c r i b e ( A r r a y s . a s L i s t ( ” m e t r i c s ” ) ) ;
37 }
38
39 p u b l i c v o i d run ( ) {
40 while ( true ) {
41 ConsumerRecords<I n t e g e r , S t r i n g > r e c o r d s = t h i s . consumer . p o l l ( 2 0 0 ) ;
42 f o r ( ConsumerRecord<I n t e g e r , S t r i n g > r e c o r d : r e c o r d s ) {
43 S t r i n g m e s s a g e = ” Message : ” + r e c o r d . key ( ) + ” − Value : ” + r e c o r d . v a l u e ( ) ;
44 l o g g e r . i n f o ( message ) ;
45 }
46 }
47 }
48 }

Código 5.3: Consumidor de prueba de la aplicación de Apache Kafka

5.2. Topologı́a de Procesamiento de Datos de Storm

La tolopologı́a de Apache Storm diseñada, tiene como objetivo leer datos de


Apache Kafka con un spout y, un bolt que recibirá esos datos imprimiendo en los
registros el dato leido. Como trabajo futuro serı́a posible diseñar e implementar bolts
más complejos para guardar los datos en una base de datos, implementar agregación
de datos para cálculo de promedios, etcétera ??.
5.2. TOPOLOGÍA DE PROCESAMIENTO DE DATOS DE STORM 37

La clase Main tiene como responsabilidad recibir los parámetros el modo de


prueba de la topologı́a, puede ser local o cluster. En el primero, la prueba se realizará
en un entorno local de Apache Storm, el segundo, es para utilizar la topologı́a
en un entorno como el desarrollado en capı́tulos anteriores. También, recibe como
parámetros, la dirección de Apache Zookeeper, que será utilizada para encontrar las
particiones de Apache Kafka para leer los datos.
1 p a c k a g e a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . s t o rm ;
2
3 import b a c k t y p e . s to r m . C o n f i g ;
4 import b a c k t y p e . s to r m . L o c a l C l u s t e r ;
5 import b a c k t y p e . s to r m . S t o r m S u b m i t t e r ;
6 import b a c k t y p e . s to r m . g e n e r a t e d . A l r e a d y A l i v e E x c e p t i o n ;
7 import b a c k t y p e . s to r m . g e n e r a t e d . I n v a l i d T o p o l o g y E x c e p t i o n ;
8 import b a c k t y p e . s to r m . s p o u t . SchemeAsMultiScheme ;
9 import b a c k t y p e . s to r m . t o p o l o g y . T o p o l o g y B u i l d e r ;
10 import o r g . a p a c h e . l o g 4 j . LogManager ;
11 import org . apache . l o g 4 j . Logger ;
12 import st o r m . k a f k a . ∗ ;
13
14 i m p o r t j a v a . u t i l . UUID ;
15
16 /∗ ∗
17 ∗ C r e a t e d by j n o n i n o on 1 2 / 0 4 / 2 0 1 6 .
18 ∗/
19 p u b l i c c l a s s Main {
20
21 private static final Logger l o g g e r = LogManager . g e t L o g g e r ( Main . c l a s s ) ;
22
23 private static final String localModeOption = ” l o c a l ” ;
24 private static final String clusterModeOption = ” c l u s t e r ” ;
25
26 public static v o i d main ( S t r i n g [ ] args ) {
27
28 S t r i n g mode = n u l l ;
29 S t r i n g zookeeperConnStr = n u l l ;
30
31 if ( a r g s . l e n g t h == 2 ) {
32 mode = a r g s [ 0 ] ;
33 zookeeperConnStr = args [ 1 ] ;
34 } else {
35 l o g g e r . e r r o r ( ” S h o u l d run w i t h two a r g u m e n t s ” ) ;
36 l o g g e r . e r r o r ( ” Usage : s t o rm j a r storm−<VERSION>−j a r −with−d e p e n d e n c i e s . j a r <MODE>
<ZOOKEEPER IP : ZOOKEEPER PORT>” ) ;
37 l o g g e r . e r r o r ( ”<MODE>: v a l i d c h o i c e s a r e : ” + l o c a l M o d e O p t i o n + ” o r ” +
clusterModeOption ) ;
38 System . e x i t ( 1 ) ;
39 }
40 ZkHosts h o s t s = new ZkHosts ( z o o k e e p e r C o n n S t r ) ;
41 S t r i n g topic ToRead = ” m e t r i c s ” ;
42 S p o u t C o n f i g s p o u t C o n f i g = new S p o u t C o n f i g ( h o s t s , topicToRead , ” / ” + topicToRead ,
UUID . randomUUID ( ) . t o S t r i n g ( ) ) ;
43 s p o u t C o n f i g . scheme = new SchemeAsMultiScheme ( new S t r i n g S c h e m e ( ) ) ;
44 KafkaSpout k a f k a S p o u t = new KafkaSpout ( s p o u t C o n f i g ) ;
45
46 T o p o l o g y B u i l d e r b u i l d e r = new T o p o l o g y B u i l d e r ( ) ;
47 b u i l d e r . setSpout ( ” kafkaSpout ” , kafkaSpout ) ;
48 b u i l d e r . s e t B o l t ( ” p r o c e s s B o l t ” , new D a t a P r o c e s s B o l t ( ) ) . s h u f f l e G r o u p i n g ( ” k a f k a S p o u t ” ) ;
49
38 CAPÍTULO 5. APLICACIONES DESARROLLADAS

50 C o n f i g c o n f = new C o n f i g ( ) ;
51 c o n f . setNumWorkers ( 1 ) ;
52 c o n f . setNumAckers ( 1 ) ;
53 c o n f . setMaxSpoutPending ( 2 0 0 0 ) ;
54 conf . setMessageTimeoutSecs (60) ;
55
56 if ( mode . e q u a l s I g n o r e C a s e ( l o c a l M o d e O p t i o n ) ) {
57 l o g g e r . i n f o ( ” D e p l o y i n g t o p o l o g y i n l o c a l mode” ) ;
58 c o n f . setDebug ( t r u e ) ;
59 L o c a l C l u s t e r c l u s t e r = new L o c a l C l u s t e r ( ) ;
60 c l u s t e r . submitTopology ( ” t e s t ” , conf , b u i l d e r . createTopology ( ) ) ;
61 } e l s e i f ( mode . e q u a l s I g n o r e C a s e ( c l u s t e r M o d e O p t i o n ) ) {
62 l o g g e r . i n f o ( ” D e p l o y i n g t o p o l o g y i n c l u s t e r mode” ) ;
63 try {
64 StormSubmitter . submitTopology ( ” e s p e c i a l i d a d ” , conf , b u i l d e r . createTopology ( ) ) ;
65 } catch ( AlreadyAliveException e ) {
66 l o g g e r . e r r o r ( ” Topology a l r e a d y r u n n i n g ” ) ;
67 e . printStackTrace () ;
68 } catch ( InvalidTopologyException e ) {
69 l o g g e r . e r r o r ( ” I n v a l i d Topology ” ) ;
70 e . printStackTrace () ;
71 }
72 } else {
73 l o g g e r . e r r o r ( ”The s e l e c t e d d e p l o y mode i s n o t v a l i d ” ) ;
74 System . e x i t ( 1 ) ;
75 }
76 }
77 }

Código 5.4: Clase Main de la topologı́a para Apache Storm

Dicha clase (5.4), también es reponsable de armar el spout que leerá los datos de
Apache Kafka, instanciar un objeto de la clase DataProcessBolt (5.5) y con ambos,
armar la topologı́a.
El bolt (5.5) de procesamiento de datos, recibe una tupla de datos, el mensaje
leı́do de Apache Kafka, y registra su procesamiento utilizando un logger. Luego,
envı́a un mensaje de ackowledgement avisando que dicha tupla fue procesada.
1 p a c k a g e a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . s t o rm ;
2
3 import b a c k t y p e . s to r m . t a s k . O u t p u t C o l l e c t o r ;
4 import b a c k t y p e . s to r m . t a s k . T o p o l o g y C o n t e x t ;
5 import b a c k t y p e . s to r m . t o p o l o g y . O u t p u t F i e l d s D e c l a r e r ;
6 import b a c k t y p e . s to r m . t o p o l o g y . b a s e . B a s e R i c h B o l t ;
7 import b a c k t y p e . s to r m . t u p l e . Tuple ;
8 import o r g . a p a c h e . l o g 4 j . LogManager ;
9 import org . apache . l o g 4 j . Logger ;
10
11 i m p o r t j a v a . u t i l . Map ;
12
13 /∗ ∗
14 ∗ C r e a t e d by J u l i à ¡ n on 2 4 / 9 / 2 0 1 6 .
15 ∗/
16 p u b l i c c l a s s DataProcessBolt extends BaseRichBolt {
17
18 private s t a t i c f i n a l L o g g e r l o g g e r = LogManager . g e t L o g g e r ( Main . c l a s s ) ;
19 private OutputCollector c o l l e c t o r = null ;
20
21 @Ov err ide
22 p u b l i c v o i d p r e p a r e (Map map , T o p o l o g y C o n t e x t t o p o l o g y C o n t e x t , OutputCollector
outputCollector ) {
23 this . c o l l e c t o r = outputCollector ;
24 }
25
26 @Ov err ide
27 p u b l i c v o i d e x e c u t e ( Tuple t u p l e I n p u t ) {
28 S t r i n g message = t u p l e I n p u t . g e t S t r i n g ( 0 ) ;
29 M e t r i c R e c o r d m e t r i c = new M e t r i c R e c o r d ( m e s s a g e ) ;
30 l o g g e r . i n f o ( ” Procesando e l mensaje : ” + metric . t o S t r i n g ( ) ) ;
31 t h i s . c o l l e c t o r . ack ( t u p l e I n p u t ) ;
32 }
33
34 @Ov err ide
35 public void declareOutputFields ( OutputFieldsDeclarer outputFieldsDeclarer ) { }
36 }

Código 5.5: Bolt de procesamiento de datos de la topologı́a para Apache Storm

La clase MetricRecord (5.6) tiene como objetivo representar al objeto que con-
tiene las métricas recibidas en cada mensaje para facilitar su procesamiento.
5.2. TOPOLOGÍA DE PROCESAMIENTO DE DATOS DE STORM 39

1 p a c k a g e a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . s t o rm ;
2
3 import java . u t i l . Arrays ;
4 import java . u t i l . L i s t ;
5
6 /∗ ∗
7 ∗ C r e a t e d by J u l i à ¡ n on 1 3 / 5 / 2 0 1 6 .
8 ∗/
9 p u b l i c c l a s s MetricRecord {
10
11 private final String country ;
12 private final String state ;
13 private final String city ;
14 private final l o n g timestamp ;
15 private final f l o a t temperature ;
16 private final f l o a t humidity ;
17 private final float pressure ;
18 private final f l o a t windSpeed ;
19 private final String windDirection ;
20 private final S t r i n g owner ;
21
22 p u b l i c MetricRecord ( S t r i n g l i n e ) {
23 L i s t <S t r i n g > v a l u e s = A r r a y s . a s L i s t ( l i n e . s p l i t ( ” \\ s ∗ , \ \ s ∗ ” ) ) ;
24 t h i s . country = values . get (0) ;
25 t h i s . state = values . get (1) ;
26 t h i s . c i t y = values . get (2) ;
27 t h i s . timestamp = Long . p a r s e L o n g ( v a l u e s . g e t ( 3 ) ) ;
28 t h i s . temperature = Float . parseFloat ( values . get (4) ) ;
29 t h i s . humidity = Float . p a r s e F l o a t ( v a l u e s . get ( 5 ) ) ;
30 t h i s . pressure = Float . parseFloat ( values . get (6) ) ;
31 t h i s . windSpeed = F l o a t . p a r s e F l o a t ( v a l u e s . g e t ( 7 ) ) ;
32 t h i s . windDirection = values . get (8) ;
33 t h i s . owner = v a l u e s . g e t ( 9 ) ;
34 }
35
36 public boolean isValidMetric () {
37 boolean i n v a l i d M e t r i c = t h i s . country . equalsIgnoreCase ( ”” )
38 | | t h i s . s t a t e . equalsIgnoreCase ( ”” )
39 | | t h i s . c i t y . equalsIgnoreCase ( ”” ) ;
40 return ! invalidMetric ;
41 }
42
43 public String getCountry ( ) { r e t u r n country ; }
44
45 public String getState () { return state ; }
46
47 public String getCity () { return city ; }
48
49 public l o n g getTimestamp ( ) { r e t u r n timestamp ; }
50
51 public float getTemperature ( ) { r e t u r n temperature ; }
52
53 public float getHumidity ( ) { r e t u r n humidity ; }
54
55 public float getPressure () { return pressure ; }
56
57 public float getWindSpeed ( ) { r e t u r n windSpeed ; }
58
59 public String getWindDirection ( ) { return windDirection ; }
60
61 public S t r i n g getOwner ( ) { r e t u r n owner ; }
62
63 @Ov err ide
64 public String toString () { return t h i s . c o u n t r y + ”−” + t h i s . c i t y + ”−” + t h i s . timestamp ; }
65 }

Código 5.6: Objeto utilizado para procesar las tuplas recibidas en la topologı́a para
Apache Storm
40 CAPÍTULO 5. APLICACIONES DESARROLLADAS
Parte III

Resultados y Conclusiones

41
Capı́tulo 6

Resultados

En este capı́tulo, se mostrará como desplegar el sistema y como ponerlo en fun-


cionamiento. Se comienza listando las imágenes de Docker que han sido creadas,
para mas información, ir al capı́tulo 4. Esto se hace mediante el comando:
1 docker images

Figura 6.1: Listado de imágenes de Docker creadas para el sistema

Mediante Docker Compose, se hace el despliegue de los clúster de Apache Zoo-


keeper, Apache Kafka y Apache Storm.

Figura 6.2: Inicio del clúster de Apache Zookeeper

43
44 CAPÍTULO 6. RESULTADOS

Luego de esperar algunos minutos, se procede a iniciar el clúster de Apache


Kafka. Esto se hace para garantizar que Apache Zookeeper está listo para recibir
conexiones.

Figura 6.3: Inicio del clúster de Apache Kafka

Esperando algunos minutos para asegurar que Apache Kafka está corriendo co-
rrectamente, se despliegue el clúster de Apache Storm.

Figura 6.4: Inicio del clúster de Apache Storm

Si no se tratara de una prueba de concepto y fuera un sistema productivo real, se


deberı́a establecer un mecanismo de sincronización automático para garantizar que
un servicio está disponible antes de iniciar el siguiente. A los fines de esta prueba de
concepto, es suficiente con esperar algunos minutos antes de desplegar cada servicio.
Cuando todo el sistema se encuentra corriendo correctamente, es posible acceder
a la interfaz gráfica de Apache Storm utilizando la dirección http://<IP_SERVICIO_
STORM_UI>:8080.
6.1. COMPILACIÓN DE APLICACIONES 45

Figura 6.5: Interfaz gráfica de Apache Storm con el sistema limpio

6.1. Compilación de aplicaciones


Ambas aplicaciones desarrolladas, tanto el productor de datos de Apache Kafka
como la topologı́a de Apache Storm, han sido creadas como un multi-proyecto de
Apache Maven. De esta manera, se pueden compilar al mismo tiempo con el comando
mvn clean install.

Figura 6.6: Compilación de las aplicaciones Java desarrolladas


46 CAPÍTULO 6. RESULTADOS

6.2. Despliegue de la topologı́a de Apache Storm


Para el despliegue de la topologı́a de Apache Storm, se debe contar con el ejecu-
table descargado desde el sitio oficial1 version 0.9.7, esto permite tener el comando
storm disponible en la lı́nea de comandos.
1 s t or m j a r storm −1.0−SNAPSHOT−j a r −with−d e p e n d e n c i e s . j a r
2 a r . edu . utn . f r c . p o s g r a d o . j n o n i n o . s t or m . Main <IP NODO ZOOKEEPER>:<PUERTO NODO ZOOKEEPER>

Figura 6.7: Ejecución del comando de despliegue de la topologı́a de Apache Storm

La siguiente imagen muestra en la interfaz gráfica de Apache Storm que la to-


pologı́a fue desplegada correctamente.

Figura 6.8: Topologı́a de Apache Storm desplegada correctamente

Luego de dicho procedimiento, la topologı́a queda lista para recibir datos nuevos
y procesarlos.
1
http://storm.apache.org/downloads.html
6.3. PRODUCCIÓN DE DATOS 47

6.3. Producción de datos


La producción de datos se realiza con una aplicación Java desarrollada para co-
nectarse con Apache Kafka y enviarle mensajes. El comando para la ejecución de
dicha aplicación comienza con java -jar ¡JAR-A-UTILIZAR¿ seguido de los argu-
mentos de ejecución. El primer argumento es lista de brokers de Apache Kafka. Le
sigue un parámetro yes—no habilitando o deshabilitando la producción de mensajes
y un argumento de numero entero indicando en milisegundos la tasa de producción
de mensajes. Y, finalmente, otro parámetro yes—no indicando si se habilita o no el
consumidor de datos de prueba.

Figura 6.9: Comienzo de la producción de datos de Apache Kafka

En la siguiente imagen (6.3) se observará como la aplicación registra en la consola


cada dato que va enviando al clúster de Apache Kafka.

Figura 6.10: Producción de datos de Apache Kafka


48 CAPÍTULO 6. RESULTADOS

6.4. Procesamiento de datos


Con la aplicación productora de datos enviando mensajes a Apache Kafka, la
topologı́a de Apache Storm comenzara a recolectarlos a través de un spout, ver
imagen 6.4.

Figura 6.11: Estadı́sticas de procesamiento del spout de Apache Storm

Luego, el bolt, es encargado de recibir dichos mensajes desde el spout y procesar-


los, para esta prueba de concepto, el procesamiento consiste en imprimir un registro
del mensaje procesado en los registros de Apache Storm. Aplicaciones productivas
reales, podrı́an hacer agregación de datos, lanzar alarmas ante algún mensaje en
particular, etcétera. Ver imagen 6.4.

Figura 6.12: Estadı́sticas de procesamiento del bolt de Apache Storm


6.4. PROCESAMIENTO DE DATOS 49

La siguiente imagen (figura 6.4) muestra desde la interfaz gráfica de Apache


Storm los registros de ejecución (logs). En ellos, se puede apreciar como el bolt de
procesamiento está haciendo los registros correspondientes tal como fue programado,
ver sección 5.2.

Figura 6.13: Registros de Apache Storm mostrando los mensajes que están siendo
procesados
50 CAPÍTULO 6. RESULTADOS

6.5. Limpieza del sistema


Para detener el sistema, se utiliza el comando docker stop ¡numero-contenedor¿.
De esta manera, se procede a detener cada uno de los contenedores corriendo.

Figura 6.14: Deteniendo contenedores de Docker

Luego, si se desea, es posible remover esos contenedores detenidos mediante el


comando docker rm ¡numero-contenedor¿, dejando el sistema limpio nuevamente.

Figura 6.15: Removiendo contenedores de Docker


Capı́tulo 7

Conclusiones y Trabajo Futuro

7.1. Conclusiones
El procesamiento de flujos de datos es requerido y toma mucha relevancia cuando
cada dato debe ser procesado rápidamente y/o continuamente, por ejemplo, cuando
hay que tomar acciones en tiempo real[2], es especialmente importante en productos
Big Data e Internet de las Cosas (Internet of Things).
En este trabajo se ha investigado sobre el uso de algunas de las herramientas de
procesamiento de datos en tiempo real más utilizadas en la industria, cumpliendo
el objetivo de desarrollar una prueba de concepto sobre el uso de estas tecnologı́as,
Apache Zookeeper, Apache Kafka y Apache Storm.
Se ha cumplido el objetivo de utilizar Docker como mecanismo de definición y
despliegue de los diferentes servicios que forman parte del sistema. En el trabajo
puede apreciarse como el uso de imágenes de Docker brinda un enfoque más simple
y económico que el uso de servidores fı́sicos y/o virtualizados. Docker permite ga-
rantizar que se utilizan las versiones correctas de cada uno de los componentes, con
las librerı́as necesarias, el entorno apropiado y el sistema operativo correspondiente.
Docker simplifica el trabajo del operador del sistema ya que evita la necesidad de
instalación de programas, generación de variables, etcétera. Ası́ mismo, el uso de
Docker Compose simplifica aún más la puesta en marcha del sistema y la forma de
comunicación entre cada uno de los servicios.
Se generaron aplicaciones Java para interactuar con Apache Kafka, cumpliendo
el objetivo de demostrar el rol de esta herramienta en el procesamiento de datos.
Queda claro como una aplicación simple puede contactarse y emitir mensajes para
que Apache Kafka los procese.
De la misma manera queda expuesto la simplicidad para comenzar el desarrollo
de una topologı́a de Apache Storm y como la complejidad reside en la lógica de
negocio que uno desee implementar porque tanto el despliegue como la configuración
de la topologı́a son sencillos.
Habiendo cumplido todos los objetivos planteados, el trabajo sirve como punto
de partida para el uso de estas herramientas. Además, abre las puertas para conti-
nuar experimentando y ampliando la prueba de concepto hasta alcanzar un sistema

51
52 CAPÍTULO 7. CONCLUSIONES Y TRABAJO FUTURO

productivo.

7.2. Trabajo Futuro


Como se ha dicho en la sección anterior (7.1), es posible llegar a un sistema listo
para ser puesto en producción a partir de la prueba de concepto realizada en éste
trabajo.
Como primera mejora, es posible trabajar en el autodescubrimiento de servi-
cios. Esto implica mejorar el uso de Apache Zookeeper y/o implementar alguna
herramienta para evitar poner en duro direcciones IP para comunicar los diferentes
servicios. Luego de ésta mejora, deberı́a ser posible levantar un nuevo nodo de Apa-
che Zookeeper mediante la imagen de Docker y que, por sus propios medios, el nodo
se conecte al sistema y que disponible para realizar su tarea. Lo mismo aplica para
los nodos de Apache Kafka y Apache Storm. Con esto, se podrı́a decidir aumentar
la cantidad de nodos de un servicio ante una mayor carga de trabajo o disminuirla
en el caso contrario. Serı́a un sistema escalable.
Contando con un sistema escalable, el siguiente paso serı́a ir a un proveedor de
servicios en la nube como Amazon Web Services, Microsoft Azure, Google Cloud
Platform, OpenStack, etcétera. Estos proveedores brindan mecanismos de auto es-
calabilidad mediante alarmas y reglas que harán que la cantidad de nodos aumente
o disminuya automáticamente de acuerdo a métricas como cantidad de solicitudes,
uso de CPU, uso de memoria, tercera.
Otro componente que no ha sido contemplado en este trabajo y podrı́a ser agre-
gado es un servicio de almacenamiento de datos como Apache HBase, Cassandra,
etcétera. Esto permitirá al sistema tomar decisiones en base a la historia y no solo
en base a los mensajes que arriban en tiempo real.
Con un sistema de persistencia de datos implementado, es posible diseñar e
implementar un servicio de analytics. El mismo, permitirá analizar los datos, generar
reportes, etcétera.
La siguiente mejora posible es diseñar e implementar bolts de Apache Storm
para realizar un procesamiento de datos más complejo, como por ejemplo, tomar
promedios, calcular máximos y mı́nimos, evaluar reglas para enviar notificaciones o
ejecutar acciones ante algún determinado valor, etcétera.
Habiendo aplicado las mejoras anteriores se tendrı́a un sistema productivo capaz
de procesar millones de mensajes por segundo tomando decisiones y generando aler-
tas y notificaciones en base a cada mensaje recibido o en base al historial. Pero aún es
posible ir un paso más allá y replantear el sistema de acuerdo a un diseño basado en
micro servicios independientes. Esto permitirı́a activar y desactivar caracterı́sticas
del sistema sin afectar el funcionamiento normal del mismo.
Bibliografı́a

[1] Apache. Apache Storm 0.9.7 Documentation. 2015. url: http : / / storm .
apache.org/releases/0.9.7/index.html (visitado 23-05-2016).
[2] Kai Wahner. Real-Time Stream Processing as Game Changer in a Big Data
World with Hadoop and Data Warehouse. 2014. url: https://www.infoq.
com/articles/stream-processing-hadoop (visitado 17-05-2016).
[3] Wikipedia. Docker (Software). 2016. url: https://es.wikipedia.org/wiki/
Docker_(software) (visitado 20-05-2016).
[4] Docker. What is Docker? 2016. url: https://www.docker.com/what-docker
(visitado 20-05-2016).
[5] Docker. Get Started with Docker Engine. 2016. url: https://docs.docker.
com/linux/ (visitado 23-05-2016).
[6] Docker. Docker Compose. 2016. url: https://docs.docker.com/compose/
(visitado 20-05-2016).
[7] Apache. Apache Zookeeper 3.4.8 Documentation. 2016. url: https://zookeeper.
apache.org/doc/r3.4.8/ (visitado 20-02-2016).
[8] Apache. Apache Kafka 0.9.0 Documentation. 2016. url: http : / / kafka .
apache.org/090/documentation.html (visitado 23-05-2016).
[9] Chandan Prakash. Apache Storm: Architecture Overview. 2016. url: https:
/ / www . linkedin . com / pulse / apache - storm - architecture - overview -
chandan-prakash (visitado 18-11-2016).

53
54 BIBLIOGRAFÍA
Índice

I Introducción 1
1. Introducción 3
1.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1. Objetivos Secundarios . . . . . . . . . . . . . . . . . . . . . . 3
1.2. Manejo de las Configuraciones . . . . . . . . . . . . . . . . . . . . . . 4
1.2.1. Ubicación del Proyecto . . . . . . . . . . . . . . . . . . . . . . 4
1.2.2. Herramientas y Frameworks Utilizados . . . . . . . . . . . . . 4

2. Marco Teórico 5
2.1. Procesamiento de Datos en Tiempo Real . . . . . . . . . . . . . . . . 5
2.1.1. Big Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.2. Procesamiento de Flujos de Datos (Stream Processing) . . . . 6
2.2. Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2.1. Imágenes y Contenedores . . . . . . . . . . . . . . . . . . . . . 7
2.2.2. Crear nuevas etiquetas . . . . . . . . . . . . . . . . . . . . . . 8
2.2.3. Docker Compose . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2.4. Material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3. Apache Zookeeper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.1. El espacio de nombres y el modelo de datos . . . . . . . . . . 11
2.3.2. Garantı́as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3.3. Material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4. Apache Kafka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.1. Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.2. Productores . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.3. Consumidores . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.4. Material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5. Apache Storm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5.1. Conceptos Básicos . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5.1.1. Topologies . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5.1.2. Streams . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5.1.3. Spouts . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5.1.4. Bolts . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.5.1.5. Stream Grouping . . . . . . . . . . . . . . . . . . . . 16
2.5.1.6. Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . 17

55
56 ÍNDICE

2.5.1.7. Workers . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.5.2. Material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

II Desarrollo 19
3. Diseño de la Arquitectura del Sistema 21

4. Imágenes de Docker 23
4.1. Nodo Apache Zookeeper . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.2. Nodo de Apache Kafka . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.3. Apache Storm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.3.1. Storm Nimbus . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3.2. Storm Supervisor . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3.3. Storm UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3.4. Iniciar Apache Storm . . . . . . . . . . . . . . . . . . . . . . . 32

5. Aplicaciones Desarrolladas 33
5.1. Productor de Datos de Kafka . . . . . . . . . . . . . . . . . . . . . . 33
5.2. Topologı́a de Procesamiento de Datos de Storm . . . . . . . . . . . . 36

III Resultados y Conclusiones 41


6. Resultados 43
6.1. Compilación de aplicaciones . . . . . . . . . . . . . . . . . . . . . . . 45
6.2. Despliegue de la topologı́a de Apache Storm . . . . . . . . . . . . . . 46
6.3. Producción de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.4. Procesamiento de datos . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.5. Limpieza del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

7. Conclusiones y Trabajo Futuro 51


7.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7.2. Trabajo Futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

Bibliografı́a 53

Índice de Contenido 53

Índice de Figuras 57
Índice de Figuras

2.1. Contenedor Docker (derecha) versus Máquina Virtual (izquierda) [4] . 7


2.2. Arquitectura de un servicio Zookeeper[7] . . . . . . . . . . . . . . . . 9
2.3. Rendimiento de Zookeeper mostrando escrituras versus lecturas[7] . . 10
2.4. El espacio de nombres jerárquico de Zookeeper[7] . . . . . . . . . . . 11
2.5. Kafka, arquitectura de alto nivel[8] . . . . . . . . . . . . . . . . . . . 12
2.6. Topics en Kafka[8] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.7. Grupos de Consumidores[8] . . . . . . . . . . . . . . . . . . . . . . . 14

3.1. Arquitectura general del Clúster incluyendo Zookeeper, Kafka y Storm 21


3.2. Arquitectura general del Clúster conectado a los programas cliente . . 22

4.1. Jerarquı́a de las imágenes de Docker desarrolladas . . . . . . . . . . . 23


4.2. Arquitectura básica de un clúster de Apache Storm[9] . . . . . . . . . 28

6.1. Listado de imágenes de Docker creadas para el sistema . . . . . . . . 43


6.2. Inicio del clúster de Apache Zookeeper . . . . . . . . . . . . . . . . . 43
6.3. Inicio del clúster de Apache Kafka . . . . . . . . . . . . . . . . . . . . 44
6.4. Inicio del clúster de Apache Storm . . . . . . . . . . . . . . . . . . . 44
6.5. Interfaz gráfica de Apache Storm con el sistema limpio . . . . . . . . 45
6.6. Compilación de las aplicaciones Java desarrolladas . . . . . . . . . . . 45
6.7. Ejecución del comando de despliegue de la topologı́a de Apache Storm 46
6.8. Topologı́a de Apache Storm desplegada correctamente . . . . . . . . . 46
6.9. Comienzo de la producción de datos de Apache Kafka . . . . . . . . . 47
6.10. Producción de datos de Apache Kafka . . . . . . . . . . . . . . . . . 47
6.11. Estadı́sticas de procesamiento del spout de Apache Storm . . . . . . . 48
6.12. Estadı́sticas de procesamiento del bolt de Apache Storm . . . . . . . . 48
6.13. Registros de Apache Storm mostrando los mensajes que están siendo
procesados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.14. Deteniendo contenedores de Docker . . . . . . . . . . . . . . . . . . . 50
6.15. Removiendo contenedores de Docker . . . . . . . . . . . . . . . . . . 50

57

View publication stats

También podría gustarte