Documentos de Académico
Documentos de Profesional
Documentos de Cultura
net/publication/329753879
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:
All content following this page was uploaded by Julian Nonino on 18 December 2018.
Julián Nonino
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
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.
3
4 CAPÍTULO 1. INTRODUCCIÓN
Marco Teórico
5
6 CAPÍTULO 2. MARCO TEÓRICO
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]
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
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.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.
Única Imagen del Sistema: Un cliente tiene la misma vista del servicio Zoo-
keeper sin importar a cual de los servidores se conecte.
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].
Kafka corre en un cluster formado por uno o mas servidores. Cada uno de ellos
es llamado broker.
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.
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.
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.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.
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.
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
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
Figura 3.2: Arquitectura general del Clúster conectado a los programas cliente
Capı́tulo 4
Imágenes de Docker
23
24 CAPÍTULO 4. IMÁGENES DE DOCKER
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
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
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
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>
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
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
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.
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 }
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 }
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 }
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
43
44 CAPÍTULO 6. RESULTADOS
Esperando algunos minutos para asegurar que Apache Kafka está corriendo co-
rrectamente, se despliegue el clúster de Apache Storm.
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
Figura 6.13: Registros de Apache Storm mostrando los mensajes que están siendo
procesados
50 CAPÍTULO 6. RESULTADOS
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.
[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
Bibliografı́a 53
Índice de Contenido 53
Índice de Figuras 57
Índice de Figuras
57