Stream Processing en redes de alta capacidad

Anuncio
 Stream Processing en redes de alta capacidad Jose Manuel Ramos Valverde Resumen Actualmente, estamos en una sociedad en gran medida dependiente de la tecnología, y, en concreto, de la interconexión que permite la red entre personas de cualquier parte del mundo. Es un hecho que esta tendencia va en aumento, y cada vez más las aplicaciones que requieren internet para su funcionamiento crecen en número de usuarios, necesitando así una forma más compleja de gestionar correctamente los contenidos que reciben de los mismos, para así, poder satisfacer las funcionalidades ofrecidas, ya que muchas de ellas tienen una urgente necesidad de un procesado de los datos en tiempo real. Este documento pretende adentrarse en este tipo de tecnologías, que conforman la parte de ingestión de datos de este tipo de aplicaciones. Se verán tres plataformas en concreto: Blockmon, Apache S4 y Storm. Las dos primeras resultaron infructuosas en algún sentido, y sobre la última, Storm, es sobre la que realmente se ha trabajado como parte final del proyecto, implementando funcionalidades sobre un derivado de una aplicación de Storm llamada COMPOSE, servioTicy, para comprender y aprender su funcionamiento. Nowadays, out society is largely dependent on technology, and specifically on the interconnection network that allows people from anywhere in the world to get in touch with each other. It’s a fact that this trend is escalating, and applications that require the internet in order to operate correctly grow in terms of users, thus requiring a more complex way to properly manage the content they recieve from them, to meet the expected functionality. The problem is that most of this applications with a huge load of users have a blatant need for processing this data in real time. This paper aims to delve into these technologies, which take part in the ingestion layer of these kind of applications. Three platforms will be seen concretely: Blockmon, Apache S4 and Storm. The two first were unsucessful in some aspects, and the latter, Storm, is the one who II actually has worked as the final part of the project, implementing functionalities on a derivative of a platform implemented for Storm called COMPOSE, servioTicy to understand and learn their modus operandi. III Datos del proyecto Título del proyecto: Stream processing en redes de alta capacidad Nombre del estudiante: Jose Manuel Ramos Valverde Titulación: Grado en ingeniería informática, especialidad TI Créditos: 18 ECTS Director: David Carrera Departamento: Arquitectura de Computadores Centro de estudios: Universitat Politècnica de Catalunya Miembros del tribunal Presidente: Jordi Guitart Fernandez Vocal: Jordi Boronat Medico Vocal: German Santos Boada Vocal: Josep-­‐Llorenç Cruz Diaz IV Índice 1 INTRODUCCIÓN 1 1.1 Motivación del proyecto 1 1.2 Estado del arte 1.2.1 Antecedentes: Procesado de datos en modo batch 1.2.1.1 Apache hadoop 1.2.1.2 Hadoop Distributed File System 1.2.1.3 El motor MapReduce 1.2.2 Solución: Stream processing 1.2.2.1 Aplicaciones de stream processing 1.2.2.1.1 Blockmon 1.2.2.1.2 Apache S4 1.2.2.1.3 Storm 1.2.2.2 La plataforma COMPOSE 4 4 4 5 5 7 7 8 9 9 10 2 ALCANCE DEL PROYECTO 11 2.1 Público objetivo 11 2.2 Objetivos 11 2.3 Fases del proyecto 2.3.1 Primera fase 2.3.1.1 Objetivos 2.3.1.2 Tecnologías adicionales 2.3.1.2.1 Que es infiniband? 2.3.1.2.2 Que es OFED? 2.3.2 Segunda fase 2.3.3 Fase final 12 13 13 14 14 15 16 16 2.4 Plan de acción final 2.4.1 Escenario de partida 2.4.2 Escenario objetivo 2.4.3 Planificación 2.4.3.1 Familiarizarse con las tecnologías adicionales usadas 2.4.3.2 Familiarizarse el proyecto COMPOSE 2.4.3.3 Montar un entorno de trabajo 2.4.3.4 Entender la metodología de programación de Storm 2.4.3.5 Entender el código hecho de la plataforma COMPOSE 2.4.3.6 Implementar data provenance 2.4.3.7 Implementar popularidad de COMPOSE 2.4.3.8 Implementar entorno de testing 2.4.3.9 Documentar 2.4.3.10 Diagrama de Gantt 2.4.4 Coste del proyecto estimado 17 17 17 17 17 18 19 19 20 20 21 21 22 23 23 3 DESARROLLO DEL PROYECTO 24 3.1 Primera Fase 3.1.1 Instalación de blockmon 24 24 V 3.1.1.1 Requisitos iniciales 3.1.1.2 Portabilidad 3.1.1.3 Instalación 3.1.2 Funcionamiento de blockmon 3.1.2.1 Estructura de directorios 3.1.2.2 Documentación 3.1.2.3 Composiciones 3.1.2.3.1 Creando composiciones 3.1.2.3.2 Creando bloques: 3.1.3 Implementación de una aplicación customizada de blockmon 3.1.3.1 Timestamping app 3.1.3.1.1 Topología del nodo fuente 3.1.3.1.2 Topología del nodo destino 3.1.3.1.3 Bloque customizado 3.1.4 Conclusiones y problematica 24 25 25 27 28 28 28 30 31 32 32 32 33 34 39 3.2 Segunda Fase 3.2.1 Apache S4 3.2.1.1 Instalación de apache S4 3.2.1.2 Prueba de una aplicación 3.2.1.2.1 WordCount 3.2.1.2.2 Correr la aplicación 3.2.2 Conclusiones y problemática 40 40 41 41 41 43 44 3.3 Fase Final 3.3.1 Familiarización con las tecnologías y entorno de trabajo 3.3.1.1 Máquina virtual 3.3.1.2 Zookeeper 3.3.1.2.1 Configuración en el entorno 3.3.1.3 Kestrel 3.3.1.3.1 Configuración en el entorno 3.3.1.4 Jetty 3.3.1.4.1 Configuración en el entorno 3.3.1.5 Couchbase 3.3.1.5.1 Configuración en el entorno 3.3.1.6 Elasticsearch 3.3.1.6.1 Configuración en el entorno 3.3.1.7 Kibana 3.3.1.7.1 Instalación 3.3.1.8 Storm 3.3.1.8.1 Configuración en el entorno 3.3.2 El proyecto COMPOSE 3.3.2.1 Introducción a COMPOSE 3.3.2.2 Composite Service Object (CSO) 3.3.2.2.1 Identidad 3.3.2.2.2 Output 3.3.2.2.3 Tipos de datos 3.3.2.2.4 Data Provenance 3.3.2.3 Pipeline de procesado 3.3.2.4 Especificación del CSO 3.3.2.5 Elementos del CSO 3.3.2.6 Integración en la plataforma COMPOSE 3.3.3 servioTicy 3.3.4 Modificaciones hechas sobre servioTicy 3.3.4.1 Data provenance 3.3.4.2 Tracking de popularidad 3.3.4.2.1 Couchbase 3.3.4.2.2 Elasticsearch 3.3.4.2.3 Kibana 45 45 45 46 47 48 48 49 50 51 52 54 55 57 57 57 58 59 59 61 64 64 64 64 65 66 67 72 73 79 79 86 86 89 91 VI 4 DESVIACIONES EN LA PLANIFICACIÓN Y COSTES FINALES 94 4.1 Desviaciones respecto a la planificación inicial 94 4.2 Planificación inicial (Primera fase) 4.2.1 Estudio preliminar de blockmon 4.2.2 Estudio de la API de OFED 4.2.3 Identificación de elementos en blockmon 4.2.4 Diseño de la solución 4.2.5 Cambio de los elementos identificados 4.2.6 Evaluación del rendimiento 4.2.7 Conclusiones 4.2.8 Arquitectura alternativa (Opcional) 4.2.9 Gantt 95 95 95 96 96 97 97 98 98 99 4.3 Desviaciones respecto al presupuesto inicial 4.3.1 Costes iniciales 4.3.1.1 Estudio preliminar de blockmon 4.3.1.2 Estudio de la API de OFED + identificación de elementos en blockmon 4.3.1.3 Diseño de la solución 4.3.1.4 Implementación de los cambios 4.3.1.5 Evaluación del rendimiento 4.3.1.6 Conclusiones, redacción final y presentación 99 100 100 100 100 100 100 101 5 COMPROMISO SOCIAL 102 5.1 Entorno legal 102 5.2 Entorno medioambiental 103 6 CONCLUSIONES 104 7 REFERENCIAS 106 8 ANEXO 110 8.1 Código de la aplicación WordCount 8.1.1 Clase Sentence 8.1.2 Clase Word 8.1.3 clase WordReceiverPE 8.1.4 Clase SentenceReciever 8.1.5 WordCountconf xml 8.1.6 TextMessageSender 110 110 111 111 113 114 116 8.2 Servioticy_queues.scala 119 8.3 byUser.ddoc 121 VII Índice de figuras Ilustración 1 : Representación del modelo de Storm Ilustración 2: Diagrama de Gantt del proyecto Ilustración 3: cmake de blockmon Ilustración 4: Build de blockmon Ilustración 5: Apache S4 Ilustración 6: Servicio Zookeeper Ilustración 7: Arranque de Elasticsearch Ilustración 8: Relación entre el plano de los SOs y el de Servicio Ilustración 9: Pila de los SO Ilustración 10: Resultado de ser un sistema conducido por eventos Ilustración 11: Fuentes de datos de un SO Ilustración 12: Pipeline de procesado Ilustración 13: Pipeline de procesado de datos de COMPOSE Ilustración 14: Ejemplo de SU Ilustración 15: funcionamiento de lastUpdate Ilustración 16: Estructura del proyecto en Storm Ilustración 17: Topología de servioTicy en Storm Ilustración 18: Creación de un CSO Ilustración 19: Envío de un SU Ilustración 20: Paso de parámetros entre bolts Ilustración 21: Recogida de parámetros Ilustración 22: Paso de parámetros entre bolts II Ilustración 23: Paso de parámetros entre bolts III Ilustración 24: Recogida de parámetros II Ilustración 25: Data provenance Ilustración 26: Last update con data provenance Ilustración 27: Crear una referencia a un clúster Ilustración 28: Crear una referencia a un clúster II Ilustración 29: Configuración de réplicas externas en couchbase Ilustración 30: Crear una réplica externa Ilustración 31: plugin head de Elasticsearch Ilustración 32: Dashboard de kibana Ilustración 33: Popularidad por SO Ilustración 34: Popularidad por stream Ilustración 35: Diagrama de Gantt inicial 3 23 26 27 40 46 56 59 60 62 63 66 72 76 76 77 77 78 79 82 83 83 83 84 85 85 86 87 88 88 89 92 93 93 99 VIII Índice de tablas Tabla 1: Elementos de un descriptor de CSO Tabla 2: ejemplo de JSONPath Tabla 3: Ejemplo de queries Tabla 4: Estructura de datos de los grupos Tabla 5: Estructura de datos de los alias Tabla 6: filtros Tabla 7: ejemplo de lógica de transformación en un CSO Tabla 8: Ejemplo del uso de acciones Tabla 9: Ejemplo del uso de links 67 67 68 68 69 69 70 70 71 IX X 1 Introducción 1.1 Motivación del proyecto Actualmente, estamos en un mundo en el que las nuevas tecnologías ganan terreno día a día. Precisamente esta idea hace referencia a algo que en sí es muy mutable en este campo, por decirlo de alguna manera, el concepto de nuevo es algo que va pasando de una cosa a otra cada vez más rápido, por lo que a este ritmo de crecimiento lo único que podemos prever son tendencias. Entonces la pregunta clave en estas circunstancias seria: hacia qué tiende lo nuevo?. Ahora mismo internet juega un papel determinante en la vida de las personas. En un principio las cosas tendían más al ámbito local, al propio ordenador, sin una dependencia tan clara entre la máquina y la nube. También es cierto que esta situación venía dada por el hecho de que las conexiones a internet eran muchísimo más lentas y las posibilidades que ofrecía debido a esto eran menores (días para descargar una película, cuando ahora en un par de horas la tenemos en alta definición). Otro factor que ha generado una dependencia mayor hacia la red han sido los dispositivos portátiles: máquinas de poca potencia en comparación a un ordenador convencional, que empezaron de la misma manera que éstos, confiando la información de manera local, pero que ahora mismo son “pisapapeles” (no del todo, pero casi) si no están conectados a internet. Estos pueden ser dispositivos como tabletas, smartphones, predominantemente, aunque la gama de posibilidades es amplísima. Debido a la existencia de diferentes tipos de dispositivos, esto es más reciente, muchos usuarios tenemos varios de ellos, y cada vez, debido a que nos gusta tenerlo todo en todos sitos y para evitar información redundante, se tiende hacia el cloud computing. 1 El cloud computing viene a abarcar tanto a las aplicaciones que se ofrecen como servicios a través de internet y al hardware y los sistemas software que proveen estos servicios. Que quiere decir esto? Que detrás de las aplicaciones que usamos cotidianamente en la red, aunque no les llamemos aplicaciones per se (Facebook, twitter…) tienen detrás unos servidores y una serie de recursos para gestionar toda esa información que les entra de los usuarios subscritos al servicio y a su vez proporcionarles la información que necesiten. Conforme el servicio va creciendo cada vez hay una mayor necesidad de gestionar eficientemente los datos enviados por los miles o millones de usuarios que constantemente están comunicándose, ya que sin una buena gestión sería imposible controlar de una forma adecuada el servicio: se perdería información o acabaría colapsándose. Esta ingestión masiva de datos que realizan estos servicios no puede hacerse en modo batch, ya que, una transacción de Wall Street, por ejemplo, no es algo que pueda ir con ningún tipo de retraso, por este motivo este tipo de soluciones a tiempo real requieren un trato especial. Estas soluciones van a ser los motores de stream processing, que nos van a permitir un trato de los datos a tiempo real en la capa de ingestión, tratar los mismos, y acabar ofreciendo unos que tengan sentido al back end de la aplicación. 2 Ilustración 1 : Representación del modelo de Storm Estas aplicaciones de procesado de grandes volúmenes de datos tendrían el siguiente funcionamiento, a grandes rasgos: Dado un stream de un cierto tipo de datos, y disponiendo de varios nodos para su procesado, se pueden distribuir los procesos concretos de cada etapa entre los nodos, pudiendo incluso redundarlos en varios de ellos para obtener siempre un camino entre las distintas etapas del procesado y con ello un sistema más fiable. Igual que esta filosofía en un sistema distribuido local, sin necesidad y urgencia de un procesado de datos a tiempo real se puede llevar a cabo en el paradigma MapReduce, pero para cumplir el requisito de procesado en tiempo real se usan este tipo de aplicaciones. 3 1.2 Estado del arte 1.2.1 Antecedentes: Procesado de datos en modo batch 1.2.1.1 Apache hadoop Hadoop es una infraestructura digital de desarrollo creada en código abierto bajo licencia Apache, un proyecto construido y utilizado por una gran variedad de programadores utilizando Java. Doug Cutting inició su desarrollo cuando estaba en Yahoo! inspirándose en tecnologías liberadas por Google, concretamente MapReduce y Google File System (GFS), con el fin de utilizarla como base para un motor de búsqueda distribuido. Tras dedicarse a tiempo completo a su desarrollo y convertir a Yahoo! en el principal contribuidor del proyecto, Cutting abandonó Yahoo! para unirse a Cloudera, una compañía cuya oferta de productos gira íntegramente en torno a Hadoop. ¿Cuál es la importancia de Hadoop? Básicamente, que permite desarrollar tareas muy intensivas de computación masiva, dividiéndolas en pequeñas piezas y distribuyéndolas en un conjunto todo lo grande que se quiera de máquinas: análisis de petabytes de datos, en entornos distribuidos formados por muchas máquinas sencillas: una propuesta de valor muy razonable en los tiempos hiperconectados que vivimos, y que utilizan hasta la saciedad empresas como Google, Yahoo!, Tuenti, Twitter, eBay o Facebook. 4 1.2.1.2 Hadoop Distributed File System El Hadoop Distributed File System (HDFS) es un sistema de archivos distribuido, escalable y portátil escrito en Java para el framework Hadoop. Cada nodo en una instancia Hadoop típicamente tiene un único nodo de datos; un clúster de datos forma el clúster HDFS. La situación es típica porque cada nodo no requiere un nodo de datos para estar presente. Cada nodo sirve bloques de datos sobre la red usando un protocolo de bloqueo específico para HDFS. El sistema de archivos usa la capa TCP/IP para la comunicación; los clientes usan RPC para comunicarse entre ellos. El HDFS almacena archivos grandes (el tamaño ideal de archivo es de 64 MB ), a través de múltiples máquinas. Consigue fiabilidad mediante replicado de datos a través de múltiples hosts, y no requiere almacenamiento RAID en ellos. Con el valor de replicación por defecto, 3, los datos se almacenan en 3 nodos: dos en el mismo rack, y otro en un rack distinto. Los nodos de datos pueden hablar entre ellos para reequilibrar datos, mover copias, y conservar alta la replicación de datos. HDFS no cumple totalmente con POSIX porque los requerimientos de un sistema de archivos POSIX difieren de los objetivos de una aplicación Hadoop, porque el objetivo no es tanto cumplir los estándares POSIX sino la máxima eficacia y rendimiento de datos. HDFS fue diseñado para gestionar archivos muy grandes. HDFS no proporciona Alta disponibilidad. 1.2.1.3 El motor MapReduce Aparte del sistema de archivos, está el motor MapReduce, que consiste en un Job Tracker (rastreador de trabajos), para el cual las aplicaciones cliente envían trabajos MapReduce. El rastreador de trabajos (Job Tracker) impulsa el trabajo fuera a los nodos Task Tracker disponibles en el clúster, intentando mantener el trabajo tan cerca de los 5 datos como sea posible. Con un sistema de archivos consciente del rack en el que se encuentran los datos, el Job Tracker sabe qué nodo contiene la información, y cuáles otras máquinas están cerca. Si el trabajo no puede ser almacenado en el nodo actual donde residen los datos, se da la prioridad a los nodos del mismo rack. Esto reduce el tráfico de red en la red principal backbone. Si un Task Tracker (rastreador de tareas) falla o no llega a tiempo, la parte de trabajo se reprograma. El TaskTracker en cada nodo genera un proceso separado JVM para evitar que el propio TaskTracker mismo falle si el trabajo en cuestión tiene problemas. Se envía información desde el TaskTracker al JobTracker cada pocos minutos para comprobar su estado. El estado del Job Tracker y el TaskTracker y la información obtenida se pueden ver desde un navegador web proporcionado por Jetty. Si el Job Tracker fallaba en Hadoop 0.20 o anterior, todo el trabajo en curso se perdía. Hadoop versión 0.21 añadió algunos autoguardados al proceso; el rastreador de trabajo graba lo que está en el sistema de archivos. Cuando un Job Tracker comienza, busca datos para recomenzar el trabajo donde lo dejó. En versiones anteriores, todo el trabajo activo se perdía cuando se reiniciaba el Job Tracker. Las limitaciones de esto son: •
La asignación de trabajo de los seguidores de trabajo es muy sencilla. Cada rastreador de tarea tiene un número de plazas disponibles, llamadas ranuras o slots (por ejemplo, "4 slots"). Cada mapa activo o cada “reduce” toma (ocupa) una posición. El Rastreador de Trabajo asigna trabajo para el seguidor más cercano a los datos con una ranura disponible. No hay ninguna consideración de la carga activa actual de la máquina asignada, y por tanto de su disponibilidad real. •
Si una tarea de seguimiento es muy lenta, se puede retrasar toda la operación MapReduce operación -­‐especialmente hacia el final de un trabajo, donde todo puede estar a la espera de una sola tarea lenta-­‐. Con la ejecución especulativa activada, sin embargo, una tarea simple puede ejecutarse en múltiples nodos esclavos. Así que esta tecnología permite una conexión en red muy eficiente. 6 1.2.2 Solución: Stream processing Como solución al principal problema que presenta Hadoop, que es que el sistema de archivos NO proporciona una alta disponibilidad de los datos surgen las tecnologías mencionadas en el preámbulo, lo que se conoce como Stream Processing. Este proyecto ha ido cambiando su objetivo a lo largo del mismo, pero como objetivo final tiene el objetivo de hacer un estudio de tres de estas tecnologías: Blockmon, Apache S4 y Storm. Las tres son tecnologías que están enfocadas al procesamiento de streams, pero en el transcurso de este proyecto se ha visto que dos de ellas no son viables debido a que son proyectos que han quedado abandonados y con bugs que no permiten trabajar bien sobre ellos. Este es el caso de Blockmon y Apache S4, mientras que Storm sigue siendo un proyecto activo por el momento. Después de ver que Storm es la alternativa fiable, se ha trabajado sobre una aplicación de Storm en desarrollo llamada COMPOSE (Collaborative Open Market to Place Objects at your Service) , implementando la parte de provenance y de popularidad de la misma. 1.2.2.1 Aplicaciones de stream processing Para hacer frente al crecimiento de los datos intercambiados Internet, la cada vez más hay una necesidad de centrarse en la ampliación de la captura y el procesamiento en tiempo real de paquetes a 10 Gb/s y más velocidad: han surgido plataformas como CoMo, así como aplicaciones de captura de paquetes de alto rendimiento como NetMap. Al mismo tiempo, la comunidad de TI ha estado diseñando plataformas para escalar el análisis de grandes conjuntos de datos a través de máquinas de propósito general. Sin embargo las soluciones propuestas son en su mayoría construidas en el MapReduce, pero está orientado al procesado de datos almacenados previamente en un sistema distribuido, no al procesado de datos en tiempo real. Para estos propósitos 7 de procesado en tiempo real existen una serie de soluciones que ahora veremos, incluyendo la que se ha elegido para este proyecto. 1.2.2.1.1 Blockmon BlockMon es un sistema modular desarrollado dentro del EU FP7 project DEMONS, para el control flexible y de alto rendimiento del tráfico y su análisis, implementado en C++ 11 y de código abierto bajo la licencia BSD. Los usuarios pueden ejecutar en un solo ordenador una composición de bloques de procesado que intercambian mensajes a través de sus “puertas”: los usuarios pueden desarrollar aplicaciones eficientes de control conectando bloques que capturan el tráfico de NIC de alta velocidad con los bloques que se ejecutan algoritmos de análisis de paquetes. BlockMon también permite agregar, actualizar y eliminar los bloques de una composición en funcionamiento sin necesidad de detenerlo. En la arquitectura BlockMon, los bloques deben implementar al menos dos métodos: Configure, que establece parámetros de configuración pasados a través de XML, y recieve que recibe los datos (mensajes). Un bloque también puede usar el método send para encolar mensajes en las “puertas” de salida. Los bloques que generan mensajes pueden usar el método async para realizar el trabajo de manera asíncrona con alta frecuencia. Finalmente los schedulers de BlockMon trabajan en grupos de subprocesos: cada bloque se asigna a un grupo a través de XML, y las pools se pueden fijar a núcleos específicos. Este modelo permite flexibilidad en términos de la cual se ejecuta el bloque en el que núcleo de la CPU. 8 1.2.2.1.2 Apache S4 Una aplicación Apache S4 está compuesta de elementos de procesado que intercambian elementos, por ejemplo, mensajes que tienen pares de (clave, valor). Cada elemento procesa sólo los eventos con el mismo valor de la clave, a través del método onEvent, y un nuevo elemento se genera para cada nueva clave. Un adaptador externo convierte los datos en eventos Apache S4 y los inyecta en el clúster a través del método put: los elementos, entonces, intercambian eventos sobre TCP (UDP también se soporta). Una tarea elimina periódicamente los elementos que no han recibido los eventos dentro de un intervalo de tiempo: esto impide que la máquina se quede sin recursos, pero hace perder el estado asociado a los elementos eliminados. Dado un nodo, Apache S4 permite asignar sólo un thread por cada elemento, por otro lado, un único thread de-­‐serializa los datos de la red en forma de eventos y los envía a los elementos. La puesta en cola de eventos se basa en llamadas bloqueantes. Los usuarios pueden iniciar más procesos dentro de la misma máquina física para aumentar la escalabilidad de su aplicación, a expensas de una utilización mayor de recursos. Apache S4 está escrito en Java. 1.2.2.1.3 Storm Una aplicación distribuida Storm se llama topología y se compone de las interconexiones de los spouts y los bolts. Los spouts leen datos de fuentes externas (por ejemplo, archivos o streams en vivo) con el método nextTuple; Los bolts implementan un método para procesar los datos de entrada llamado execute. Los spouts y los bolts envían datos por medio de un método emit. Los datos se intercambian en forma de tuplas través ZMQ sockets, que se encargan de la transmisión de datos de forma local o remota sobre TCP. Storm se basa en Nimbus para la distribución de código en el cluster y la asignación de tareas a las máquinas. Los usuarios no tienen control sobre cómo se asignan los recursos dentro del cluster, sino que sólo establecen, para un componente dado, el 9 número de casos para crear, y cada uno de ellos se ejecuta en un hilo separado. Dentro de una máquina, los trabajadores y ejecutores manejan la ejecución de los elementos para una topología determinada. Storm se ejecuta en la máquina virtual de Java, está escrito en Clojure y Java y soporta programación multi-­‐idioma. Se utilizó la versión estable del software v0.8 1, disponible bajo la licencia EPL , y twitter lo usa para su función de trending, por ejemplo. 1.2.2.2 La plataforma COMPOSE En la Fase final del proyecto se trabajará sobre Storm, y en concreto sobre una plataforma desarrollada sobre esta tecnología: COMPOSE. El Internet of Things está compuesto de objetos, ya estén conectados a internet o no lo estén. Estos objetos tendrán una identidad virtual en COMPOSE llamada Service Object. Estos Service Objects pueden combinarse con otros para crear Service Objects Compuestos, y tanto unos como otros son entidades de manejo de datos dentro de COMPOSE. Los Service Objects serían entidades más básicas que solo permiten el guardar y recuperar datos que después serán usados por Web Objects, así como por Servicios y Aplicaciones. Los Service Object Compuestos proveen a parte de esto más operabilidad con los datos. 10 2 Alcance del Proyecto 2.1 Público objetivo En este proyecto el objetivo del mismo podrían ser, por ejemplo, developers de aplicaciones para Iot, y a su vez los usuarios mismos de la plataforma COMPOSE. 2.2 Objetivos Aunque los objetivos han ido cambiando a lo largo del proyecto, los objetivos finales en los que nos hemos centrado finalmente, y por tanto, los correspondientes a la fase final son los siguientes: •
Familiarizarse con las tecnologías siguientes: o Storm: Permite el procesado de streams en tiempo real. o Kestrel: Sistema de colas que alimenta el input de Storm o Zookeeper: Coordina procesos dentro de Storm. o Jetty: Servidor web ligero. o Couchbase: Base de datos no-­‐sql que almacena información procesada desde Storm y recibida desde la API de servioTicy. o Elasticsearch: Un motor de búsqueda de código abierto, con API RESTful, y que presta especial atención a su arquitectura distribuida. o Kibana: Sistema analítico que permite analizar datos procedentes de Logstash o Elasticsearch. •
Familiarizarse con el proyecto COMPOSE. 11 •
Montar un entorno en el que estas tecnologías se coordinen entre ellas. •
Entender la metodología de Storm y el código hecho de la plataforma COMPOSE hasta el momento. •
Modificar la aplicación implementando data provenance. •
Modificar la aplicación para implementar un seguimiento de popularidad. •
Hacer un entorno de testing. Los objetivos y modificaciones correspondientes de las fases anteriores vendrán definidos en el apartado correspondiente más adelante. 2.3 Fases del proyecto A pesar de que las dos primeras fases del proyecto han sido infructuosas, han permitido el estudio y comprensión de las tecnologías de procesamiento de streams y su funcionamiento en dos casos como son Blockmon y Apache S4. También han ayudado a comprender realmente la fugacidad de este tipo de proyectos y lo importante que es tener un buen soporte y documentación para poder trabajar con una tecnología concreta. 12 2.3.1 Primera fase Esta primera fase del proyecto ha consistido en el estudio de la tecnología Blockmon, y los objetivos definidos eran: 2.3.1.1 Objetivos •
Aprender los mecanismos de programación de infiniband. •
Migrar el software Blockmon para que use la tecnología infiniband, mediante la API de OFED. •
Evaluar las ventajas e inconvenientes del uso de esta tecnología, tanto desde el punto de vista de programabilidad como de rendimiento. •
Reconsiderar la arquitectura de blockmon a partir del conocimiento adquirido a través de los otros objetivos (Siendo esto un objetivo secundario y por tanto condicionado al correcto cumplimiento en el tiempo adecuado de los otros objetivos). En el apartado de cambio de objetivos y en el desarrollo de la fase correspondiente se detalla porqué no se han podido alcanzar estos objetivos, pero una serie de problemas con la tecnología hicieron cambiar de rumbo el proyecto hacia la segunda fase. 13 2.3.1.2 Tecnologías adicionales En este apartado se hace mención a las tecnologías adicionales que formaban parte en la primera fase del proyecto y de las cuales no se hace uso en los objetivos finales. 2.3.1.2.1 Que es infiniband? El protocolo de interconexión de redes y sistemas por antonomasia es Ethernet y su última versión Gigabit Ethernet, pero existen alternativas a este sistema en determinados ámbitos y una de ellas es InfiniBand, se centra en un ámbito tan especializado como el de la interconexión en superordenadores y clústers. Ethernet es el estándar que domina la gran mayoría de mercado, presente de manera casi exclusiva tanto en mercado doméstico, como en pequeña y mediana empresa representa también un porcentaje muy significativo en grandes centros de datos. Pero como decíamos existen otros protocolos que se pueden situar en el mismo nivel de calidad que Gigabit Ethernet como InfiniBand. InfiniBand es un bus serie bidireccional de comunicaciones de alta velocidad, llegando a ofrecer velocidades de hasta 2.0Gbps netos en cada dirección del enlace en un nodo simple, 4Gbps netos en un nodo doble y hasta 8Gbps netos en un nodo cuádruple. Estos nodos a su vez se pueden agrupar en grupos de 4 o 12 enlaces llegando a velocidades de hasta 96Gbps netos en un grupo de 12 nodos cuádruples. El factor de velocidad neta viene relacionado con que Infiniband de cada 10 bits que transmite 8 de ellos son datos. Estas enormes velocidades de conexión hacen que Infiniband sea una conexión con una muy importante presencia en superordenadores y clústeres, por ejemplo del top 500 de superordenadores, 226 están conectados internamente con Infiniband,188 lo están con Gigabit Ethernet y el resto con Myrinet, Cray, Fat Tree u otras interconexiones a medida. 14 Una de las principales ventajas de Infiniband sobre Ethernet es su bajísima latencia, por ejemplo y basándonos en los datos del estudio de Qlogic "Introduction to Ethernet Latency, an explanation to Latency and Latency measurement", la latencia en 10Gpbs Ethernet se sitúa en 5 microsegundos mientras que la de Infiniband se sitúa por debajo de los 3 microsegundos. La topología de Infiniband es otro de los puntos diferenciales respecto a Ethernet. A diferencia de Ethernet cuya topología es jerárquica, Inifiband tiene una topología en la que cada nodo tiene una comunicación directa con cualquier otro nodo, permitiendo a la tarjeta de red leer o escribir datos directamente en otros servidores, evitándole este trabajo a los procesadores. 2.3.1.2.2 Que es OFED? La Distribución para Empresa de OpenFabrics (OFED) es un software de código abierto para RDMA y aplicaciones de derivación del núcleo. OFED se utiliza en entornos de negocios, investigación y científicos que requieren redes de alta eficiencia, conectividad del almacenamiento y computación en paralelo. El software proporciona computación de alto rendimiento y centros de datos empresariales con la flexibilidad y protección de la inversión, ya que la informática evoluciona hacia aplicaciones que requieren velocidades extremas, escalabilidad masiva y la fiabilidad. OFED incluye controladores a nivel de núcleo, RDMA orientado al canal y operaciones de envío / recepción. Las tecnologías de red y el cable que proporcionan un rendimiento RDMA con OFED incluyen: legacy Ethernet 10 Gigabit, iWARP para Ethernet, RDMA sobre Ethernet Convergente (ROCE) y 20.10.40 Gigabit InfiniBand. OFED está disponible para muchas distribuciones de Linux y Windows, y cada vez más (en 2010 más del 40%) está presente en las interconexiones de los computadores del top 500. 15 2.3.2 Segunda fase La segunda fase ha consistido en el estudio de la tecnología Apache S4. En esta fase no se han definido unos objetivos marcados, ya que al realizar pruebas sobre esta tecnología surgieron numerosos problemas, tanto en la instalación del entorno como en la ejecución de aplicaciones sobre la misma, por lo que después de un consumo de tiempo considerable y el retraso correspondiente en la planificación se pasó a la fase final del proyecto. 2.3.3 Fase final La última fase del proyecto ha consistido en el estudio de Storm, y, en concreto, en trabajar en el desarrollo de una aplicación para Storm, llamada COMPOSE, implementando la parte de data provenance y de popularidad de la misma. En este caso, la aplicación es dependiente no sólo de Storm, sino de una serie de tecnologías con las que nos hemos tenido que familiarizar para poder montar un entorno de trabajo en el cual todo funcione correctamente. Tanto COMPOSE como esta serie de tecnologías adicionales a Storm vienen explicadas en sus respectivos apartados. 16 2.4 Plan de acción final 2.4.1 Escenario de partida Partimos de una parte del código de COMPOSE ya escrita 2.4.2 Escenario objetivo Tener una máquina virtual en la que COMPOSE funcione, con las modificaciones propuestas implementadas. 2.4.3 Planificación A continuación se detalla la planificación del proyecto y sus fases. 2.4.3.1 Familiarizarse con las tecnologías adicionales usadas Descripción de la tarea: Esta tarea consistirá en familiarizarse con las tecnologías a usar en el proyecto, aunque para algunas de ellas esta fase durará lo que dure el proyecto, ya que algunas al principio no se requieren Riesgo: Bajo 17 Recursos: humanos, mano de obra Dependencia: Ninguna Duración: 8 dias 2.4.3.2 Familiarizarse el proyecto COMPOSE Descripción de la tarea: Esta tarea consistirá en familiarizarse con el proyecto COMPOSE, y su funcionamiento a nivel teórico, sin entrar aun en la especificación en si. Riesgo: Bajo Recursos: humanos, mano de obra Dependencia: 2.4.3.1 Duración: 3-­‐6 días 18 2.4.3.3 Montar un entorno de trabajo Descripción de la tarea: Esta tarea se alargará hasta el fin del proyecto, ya que algunas partes no se añadirán al entorno hasta que la funcionalidad que las requiere no esté implementada Riesgo: Medio Recursos: humanos, mano de obra Dependencia: 2.4.3.1, 2.4.3.2 Duración: 43 días 2.4.3.4 Entender la metodología de programación de Storm Descripción de la tarea: Aquí se estudiarán los métodos de programación para Storm, los conceptos de bolts, spouts y topologías y como se relacionan entre ellos. Riesgo: Medio Recursos: humanos, mano de obra Dependencia: Ninguna 19 Duración: 7 días 2.4.3.5 Entender el código hecho de la plataforma COMPOSE Descripción de la tarea: Consistirá en familiarizarse con la plataforma COMPOSE a nivel de programabilidad, para poder trabajar sobre ella y aplicar las posteriores modificaciones Riesgo: Alto Recursos: humanos, mano de obra Dependencia: 2.4.3.2 Duración: 5 días 2.4.3.6 Implementar data provenance Descripción de la tarea: En esta tarea se implementará la parte de data provenance (explicado en el apartado correspondiente) del proyecto COMPOSE. Riesgo: 2.4.3.5 20 Recursos: humanos, mano de obra Dependencia: Ninguna Duración: 10 días 2.4.3.7 Implementar popularidad de COMPOSE Descripción de la tarea: En esta tarea se implementará la parte de tracking de popularidad de la plataforma COMPOSE Riesgo: Alto Recursos: humanos, mano de obra Dependencia: 2.4.3.5, 2.4.3.6 Duración: 15 días 2.4.3.8 Implementar entorno de testing Descripción de la tarea: En esta tarea se implementará un entorno de testing para validar las modificaciones hechas. 21 Riesgo: Medio Recursos: humanos, mano de obra Dependencia: 2.4.3.7 Duración: 6 días 2.4.3.9 Documentar Descripción de la tarea: En esta tarea se documentará lo hecho durante el proyecto en este documento Riesgo: Bajo Recursos: humanos, mano de obra Dependencia: Ninguna Duración: 17 días 22 2.4.3.10 Diagrama de Gantt Ilustración 2: Diagrama de Gantt del proyecto 2.4.4 Coste del proyecto estimado Se asume que para todas las fases, el coste medio del proyecto por hora en personal (1 empleado) va a ser de 7,5€/h, y una media de trabajo de 4 horas al día. El equipo de trabajo es un portátil que ya está amortizado. Sabiendo los días de trabajo (lunes a viernes) para cada fase y con las horas estimadas al día de trabajo especificadas abajo por cada una, ya que las tareas en paralelo requerirán de más horas, podemos estimar cálculos: Fase 1: 29 días. Fase 2: 19 días. Fase 3: 65 días. (29 + 19 + 65) * 4 * 7.5 = 3390 Así, el proyecto tendría un coste de 3390€. Los cambios respecto a la planificación inicial vienen detallados en el apartado correspondiente más adelante. 23 3 Desarrollo del Proyecto 3.1 Primera Fase La primera fase del proyecto correspondía a los objetivos principales, pero por problemas técnicos y temporales no puedo llevarse a cabo. Este apartado define lo que se hizo y porqué no funcionó. 3.1.1 Instalación de Blockmon 3.1.1.1 Requisitos iniciales Para compilar Blockmon se necesitará una versión de GCC superior a la 4.6, así como cmake en su versión 2.8 o superior. También se necesitará una versión de python superior o igual a la 2.5 para tratar con el cliente, así como el paquete json-­‐rpc (http://pypi.python.org/pypi/txJSON-RPC/) y el paquete json pickle (http://pypi.python.org/pypi/jsonpickle). Adicionalmente, se necesitarán otras librerías que deben instalarse en el directorio /lib/external: 1. Crypto-Pan
mkdir lib/external/crypto
cd lib/external/crypto
wget
http://www.cc.gatech.edu/computing/Networking/projects/cryptopan/Crypt
o-PAn.1.0.tar.gz
tar xzf Crypto-PAn.1.0.tar.gz
mv Crypto-PAn.1.0/* .
mv sample.cpp sample.cpp.exclude
rmdir Crypto-PAn.1.0
cd ../../..
2. PugiXML
mkdir lib/external/pugixml
24 cd lib/external/pugixml
wget http://pugixml.googlecode.com/files/pugixml-1.2.tar.gz
tar xzf pugixml-1.2.tar.gz
cd ../../..
3. libfc (se necesita git instalado para esta)
cd lib
git clone git://github.com/britram/libfc.git fc
cd fc
git checkout af3065e37cdadabf00ed170c9830cfde57708b05
mv ipfix2csv.cpp ipfix2csv.cpp.exclude
mv fcprof.cpp fcprof.cpp.exclude
cd ../..
Este fue de los primeros problemas, las librerías tenían a su vez unas dependencias muy específicas entre versiones. 3.1.1.2 Portabilidad En teoría debería correr en varios sistemas, incluyendo Windows y Linux, aunque sólo se ha probado en Linux. 3.1.1.3 Instalación •
Descargar el proyecto de github ejecutando git clone https://github.com/blockmon/blockmon.git •
En el directorio raíz de Blockmon ejecutar cmake .
•
Si se quieren activar opciones o quitarlas, la sintaxis es: cmake -
D<funcionalidad>=ON|OFF
25 •
Se soportan las siguientes funcionalidades, que activan la compatibilidad con ciertas librerías adicionales, para más información referirse a la documentación de Blockmon (blockmon-­‐master/INSTALL):
o WITH_PFQ o WITH_COMBO o WITH_DAEMON
Ilustración 3: cmake de blockmon ejecutar el comando make, teniendo en cuenta que a la hora de hacerlo, aunque no se indique en la documentación hay que instalar el paquete libboost-­‐all-­‐dev (sudo apt-­‐get install libboost-­‐all-­‐dev), debido a que se hace uso del mismo desde uno de los archivos de la librería fc (libfc) mencionada arriba. También falta la librería pcap, aunque no viene indicado en ningún sitio, exactamente en su versión 0.8 (sudo apt-­‐get install libpcap0.8-­‐dev). Aun así en algunas versiones de Linux durante el make daba errores que consumían una gran cantidad de tiempo, hasta poder al fin hacer correctamente el build. 26 Ilustración 4: Build de blockmon 3.1.2 Funcionamiento de blockmon Blockmon es un software que permite la construcción de nodos de análisis de datos flexibles y de alto rendimiento (en el rango de los 10Gb), donde un nodo puede ser por ejemplo un PC. Blockmon está basado en la noción de bloques, que son unidades pequeñas de procesado. Los bloques están conectados y se comunican vía “gates”, y los bloques conectados entre sí representan una composición, expresada en términos de un fichero XML Para controlar blockmon existen dos opciones: •
Un cliente python, que para correrlo hay que entrar en la carpeta daemon del directorio raíz de blockmon (importante, sino hay que cambiar el script) y ejecutar el comando python cli.py. Se espera que los usuarios escriban sus composiciones xml a mano. •
Un daemon basado en python y json-­‐rpc, que no usaremos •
Había descrita una tercera opción, una GUI, pero no se llegó a implementar por 27 abandono del proyecto 3.1.2.1 Estructura de directorios . ├── bin ejecutable de Blockmon ├── blocks bloques ├── core archivos de core ├── daemon daemons ├── doc documentación ├── lib librerías ├── messages archivos de mensajes ├── tests archivos de testing └── usr bloques de usuario, mensajes, composiciones. 3.1.2.2 Documentación La documentación se genera con doxygen, hay que tenerlo instalado y ejecutar doxygen blockmon.cfg. Esto generará una documentación HTML del código. Adicionalmente las clases que implementan bloques incluyen información adicional sobre la funcionalidad del mismo. Para generar esta documentación hay que entrar en el directorio doc y ejecutar python blocksdoc.p. Para esto la variable de entorno PYTHONPATH tiene que estar fijada en el directorio raíz de blockmon. 3.1.2.3 Composiciones 28 Una composición sencilla consiste en una serie de bloques y sus conexiones, por ejemplo: <composition id="mysnifferctr" app_id="boh">
<install>
<threadpool id="sniffer_thread" num_threads="2" >
<core number="0"/>
</threadpool>
<block id="sniffer" type="PcapSource"
sched_type="active" threadpool="sniffer_thread">
<params>
<source type="live" name="eth0"/>
</params>
</block>
<block id="counter" type="PktCounter"
sched_type="passive">
<params>
</params>
</block>
<connection src_block="sniffer" src_gate="sniffer_out"
dst_block="counter" dst_gate="in_pkt"/>
</install>
</composition>
También es posible modificar composiciones existentes así: <composition id="mysnifferctr">
<update>
29 <reconf>
<block id="sniffer" sched_type="active"
threadpool="sniffer_thread">
<params>
<source type="live" name="wlan0" />
</params>
</block>
</reconf>
</update>
</composition>
Para correr una composición vía cliente hay que ejecutar el cliente y aparecerá una Shell propia: BM shell: start [composition file] Para parar la ejecución: BM shell: stop 3.1.2.3.1 Creando composiciones Para crear una aplicación hay que añadir un subdirectorio bajo /usr con el nombre app_[Nombre de tu app] y poner los ficheros de composición en este directorio. También, bajo este directorio crear otros dos llamados blocks y otro llamado messages. Finalmente habrá que volver a hacer cmake . para incluir los nuevos ficheros al proyecto. 30 3.1.2.3.2 Creando bloques: La manera más fácil para empezar es con un bloque simple como el que hay en blocks/PktCounter. Una vez creado hay que correr la instrucción python core/blockinfoparser.py config para hacer saber al cliente la presencia de un nuevo bloque. 31 3.1.3 Implementación de una aplicación customizada de blockmon 3.1.3.1 Timestamping app La idea era hacer una implementación muy simple de una aplicación que imprimiese un timestamp cada vez que un mensaje pasase por el bloque implementado. Los bloques están implementados en C++ y las topologías de fuente y de destino en XML. 3.1.3.1.1 Topología del nodo fuente Esta topología es la que se ejecutaría en el nodo de origen de los datos. El archivo es source.xml y se encuentra en blockmon-­‐master/usr/app_timestamping/source.xml <composition id="timestamping" app_id="app_timestamping">
<general>
<clock type="wall" />
</general>
<install>
<threadpool id="pol1" num_threads="1"/>
<block id="poraquientran" type="PcapSource"
invocation="async" threadpool="pol1">
<params>
<source type="live" name="eth1"/>
</params>
</block>
32 <block id="export1" type="SerExportermod" export="yes">
<params>
<export host="127.0.0.1" port="12345"/>
</params>
</block>
<connection src_block="poraquientran"
src_gate="source_out" dst_block="export1" dst_gate="in_msg"/>
</install>
</composition> 3.1.3.1.2 Topología del nodo destino Esta es la topología que se ejecutaría en el nodo de destino. El archivo es dest.xml y se encuentra en blockmon-­‐master/usr/app_timestamping/source.xml <composition id="timestamping" app_id="app_timestamping">
<general>
<clock type="wall" />
</general>
<install>
<threadpool id="src_thread" num_threads="1"/>
<block id="importer" type="SerSource"
invocation="async" threadpool="src_thread">
<params>
33 <collect port="12345" msgtype="NewPacket"/>
</params>
</block>
<block id="pprint" type="PacketPrinter">
<params>
</params>
</block>
<connection src_block="importer" src_gate="source_out"
dst_block="pprint" dst_gate="in_pkt"/>
</install>
</composition> 3.1.3.1.3 Bloque customizado Este es el bloque implementado para hacer la función de timestamping. El archivo es SerExportermod.cpp y se encuentra en blockmon-­‐master/usr/app-­‐
timestamping/blocks/SerExportermod.cpp #include <boost/lexical_cast.hpp>
#include <unordered_map>
#include <Block.hpp>
#include <BlockFactory.hpp>
34 #include <Serializer.hpp>
#include <iostream>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netdb.h>
using namespace pugi;
using namespace std;
namespace blockmon {
class SerExportermod : public Block {
private:
int m_in_gate_id;
Serializer ser;
std::string host;
int port;
int sock;
struct sockaddr_in remote;
public:
SerExportermod(const std::string& name, invocation_type
invocation):
Block(name, invocation),
m_in_gate_id(register_input_gate("in_msg")),
sock(-1) {}
/**
* Configure the block given an XML element containing
configuration.
35 * Called before the block will begin receiving
messages.
*
*
* @param xmlnode the <params> XML element containing
block parameters
*/
virtual void _configure(const xml_node& xmlnode)
{
xml_node outspec;
// Don't support reconfiguration yet
if ((outspec = xmlnode.child("export"))) {
// Parse export (transport, host, port)
std::string cfghost =
outspec.attribute("host").value();
std::string cfgport =
outspec.attribute("port").value();
if (cfghost.empty() || cfgport.empty()) {
throw std::runtime_error("Export
specification incomplete");
}
host = cfghost;
port = atoi(cfgport.c_str());
if(port < 0 || port > 65535) {
throw std::runtime_error("Invalid port");
}
} else {
throw std::runtime_error("No export or file
specification");
}
// If not already connected try to
if(sock == -1) {
struct hostent* tmpconversion =
36 gethostbyname(host.c_str());
if(!tmpconversion) {
throw std::runtime_error("Cannot resolve
destination host");
}
remote.sin_family = AF_INET;
remote.sin_addr = *(struct in_addr*)
tmpconversion->h_addr_list[0];
remote.sin_port = htons((short) port);
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1) {
std::cerr << "Cannot create socket: " <<
strerror(errno) << std::endl;
return;
}
if(connect(sock, (struct sockaddr*) &remote,
sizeof(remote) ) < 0) {
std::cerr << "Error connecting to remote
site: " << strerror(errno) << std::endl;
close(sock);
return;
}
}
}
/**
* Receive a BlockMon message
*
* @param m a shared pointer to the message to handle
* @param gate_id the gate ID of the input gate on
which the message was
*
received, as returned by
register_input_gate()
*/
virtual void _receive_msg(std::shared_ptr<const Msg>&&
m, int gate_id)
37 {
// If not already connected try to
if(sock == -1) {
struct hostent* tmpconversion =
gethostbyname(host.c_str());
if(!tmpconversion) {
throw std::runtime_error("Cannot resolve
destination host");
}
remote.sin_family = AF_INET;
remote.sin_addr = *(struct in_addr*)
tmpconversion->h_addr_list[0];
remote.sin_port = htons((short) port);
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1) {
std::cerr << "Cannot create socket: " <<
strerror(errno) << std::endl;
return;
}
if(connect(sock, (struct sockaddr*) &remote,
sizeof(remote) ) < 0) {
std::cerr << "Error connecting to remote
site: " << strerror(errno) << std::endl;
close(sock);
return;
}
}
m->serialize(&ser);
char* buffer;
int len = ser.finalize(&buffer);
time_t now;
struct tm *current;
now = time(0);
current = localtime(&now);
cout << "hour: " << current->tm_hour << endl;
38 cout << "mins: " << current->tm_min << endl;
cout << "sec: " << current->tm_sec << endl;
struct timeval detail_time;
gettimeofday(&detail_time,NULL);
cout << "milli: " << detail_time.tv_usec/1000 <<
endl;
send(sock, buffer, len, 0);
}
};
REGISTER_BLOCK(SerExportermod,"SerExportermod")
}
3.1.4 Conclusiones y problemática Después de implementar estas topologías y bloques, al intentar correrla salta un error, no detecta el tipo de paquete NewPacket. Después de una investigación más profunda se llegó a ver que no estaba registrando a la hora de compilar algunos tipos de mensaje, y para probar que no fuese error de los mensajes por defecto creamos un tipo costumizado derivado de NewPacket, Newpacket2, siendo la implementación la misma pero cambiando el nombre, y tampoco conseguimos registrarlo. Después de todo el retraso que había generado blockmon durante su instalación y comprensión, debido a una evidente falta de documentación, mucha de la cual era errónea o incompleta y al conocimiento del abandono de la plataforma por parte de sus desarrolladores, se decidió abandonar esta línea de investigación en el proyecto, descartando así blockmon como opción para la implementación de COMPOSE. 39 3.2 Segunda Fase 3.2.1 Apache S4 En esta fase se procedió al estudio de la plataforma apache S4. Es una plataforma distribuida de procesamiento de streams de Yahoo. Al ser tolerante a fallos y altamente escalable permite desarrollar aplicaciones de procesado de streams muy grandes, como detectar terremotos o encontrar el trozo perfecto de publicidad que el visitante a tu página esté mas predispuesto a pinchar. Básicamente, una aplicación de S4 consiste en un número de elementos de procesado (EPs), que se interconectan a través de una configuración hecha en spring, que define los EPs y el flujo de eventos en el sistema. Los eventos también son producidos por triggers que envían los eventos al cliente de S4, y éste se encarga de procesarlos y despacharlos. Ilustración 5: Apache S4 40 3.2.1.1 Instalación de apache S4 •
Descargar la versión más reciente de apache S4 (0.6) con el comando git
clone
http://git-wip-us.apache.org/repos/asf/incubator-
s4.git.
•
Compilar e instalar S4 en el repositorio maven local. Para esto hay que tener instalado gradle. S4:incubator-s4$ ./gradlew install -DskipTests
.... verbose logs ... •
Hacer build de los scripts de inicio S4:incubator-s4$ ./gradlew s4-tools:installApp
.... verbose logs
...:s4-tools:installApp 3.2.1.2 Prueba de una aplicación 3.2.1.2.1 WordCount Para probar la plataforma se procedió a probar una aplicación de Word Count que muestra como: •
Mantener el estado en un EP •
Despachar eventos desde un EP •
Procesar múltiples eventos desde un EP •
Escribir un cliente java simple para enviar eventos a S4 41 En esta aplicación se construye un WordReceiverPE, que recibirá eventos en forma de palabras y las imprimirá por la salida estándar. También identificará frases en el stream de palabras y las remitirá para que las procese SentenceReceiverPE. WordReceiverPE también producirá eventos de recepción de frases y las imprimirá por la salida estándar. S4 usa keys, que es un conjunto de propiedades del objeto del evento para despachar o enrutar eventos. En este caso como la clase Word es el punto de entrada sin key al sistema, y no tenemos ninguna llave para ella, pero para la clase Sentence, tenemos un Sentenence.sentenceId como key. El fichero de configuración es un fichero spring para definir un bean. El primer bean, wordCatcher es un objeto de la clase WordReceiverPE e inyecta un dispatcher llamado dispacher a WordReceiverPE. Keys define el stream en el que nuestro EP estará escuchando eventos. RawWords * significa que estará recibiendo todos los eventos en el stream llamado RawWords sean cuales sean sus keys. La misma intención se tiene para Sentence *. El segundo bean es sentenceCatcher, que es un objeto de la clase SentenceReceiverPE, y aceptará todos los eventos en un stream llamado Sentence. La tercera definición es para el dispatcher que hemos inyectado en WordCatcher. El dispatcher necesita un partitioner que particione los eventos basado en una key, y en base a eso se los despache a quien corresponda. En este caso se usa el DefaultPartitioner, cuyas propiedades son definidas después por sentenceIdPartitioner, que dice que la partición de los objetos evento en el stream Sentence viene dada por la propiedad sentenceId. Dispatcher usa la capa de comunicación provista por S4, commLayerEmiter para emitir los eventos. El código de la aplicación viene detallado en el anexo, apartado 9.1 de este documento. 42 3.2.1.2.2 Correr la aplicación •
Crear un paquete jar llamado Wordcount.jar con las clases detalladas en el anexo •
Correr la aplicación de S4 creando la siguiente estructura de directorios: /$S4_IMAGE
/s4-apps
/WordCount
WordCount-conf.xml
/lib
Wordcount.jar
•
Iniciar S4 $S4_IMAGE/scripts/start-s4.sh -r client-adapter & 43 •
Iniciar el cliente $S4_IMAGE/scripts/run-client-adapter.sh -s client-adapter g s4 -d $S4_IMAGE/s4-core/conf/default/client-stub-conf.xml
& Ahora S4 está listo para recibir eventos, TextMessageSender lee una palabra de la entrada estándar y se la envía a S4, así que hay que ejecutar el cliente: java TestMessageSender localhost 2334 RawWords
prueba.s4.Word
3.2.2 Conclusiones y problemática En esta fase se ha aprendido a nivel teórico y de testing a trabajar con S4, pero dio también bastantes problemas de dependencias lo que debía ser una instalación simple, la plataforma está abandonada ahora desde 2013 y desde COMPOSE se había elegido a Storm como plataforma definitiva de desarrollo, por lo que se decidió dejar S4 de lado y pasar a la siguiente parte del proyecto. 44 3.3 Fase Final El desarrollo de esta fase ha consistido en varias etapas. En primer lugar ha habido un proceso de familiarización con las tecnologías necesarias para poder correr el proyecto COMPOSE sobre Storm. En el apartado correspondiente de esta sección se explicará en detalle qué son y como hay que configurar las mismas para poder tener un entorno de trabajo sobre el que poder hacer pruebas y empezar a desarrollar. El siguiente paso fue estudiar la API de COMPOSE para poder empezar a relacionarnos con el sistema. Una vez se han podido efectuar pruebas de manera satisfactoria y sin errores se ha procedido a la familiarización con la metodología de programación para Storm y la comprensión del código (no trivial) hecho hasta el momento, para poder realizar las modificaciones correspondientes. Estas modificaciones han consistido en implementar la parte de data provenance de la aplicación y la posibilidad de hacer un tracking de la popularidad por SO o por stream. Por último se ha preparado un entorno de testing para poder hacer una prueba de incrementos de popularidad en directo. 3.3.1 Familiarización con las tecnologías y entorno de trabajo 3.3.1.1 Máquina virtual Para llevar a cabo el despliegue de aplicaciones necesario para correr la plataforma COMPOSE se ha procedido a la creación de una máquina virtual con las siguientes características: •
Sistema operativo Ubuntu 12.04.4 •
4 CPUs asignadas (2,2 GHz Intel Core i7) •
1,5GB de RAM 45 •
20GB HDD 3.3.1.2 Zookeeper Es un servicio de coordinación open source y distribuido para aplicaciones distribuidas. Expone un conjunto simple de primitivas que las aplicaciones distribuidas pueden usar para implementar servicios de alto nivel. Está diseñado para que sea fácil desde el punto de vista de programabilidad y corre en java. Permite a los procesos distribuidos coordinarse entre si a través de un espacio de nombres jerárquico y compartido, que está organizado de manera similar a un sistema de ficheros estándar. Los servidores que conforman el servicio Zookeeper deben conocerse entre ellos. Mantienen una imagen sobre el estado del sistema en memoria, así como logs de transacciones. Mientras la mayoría de servidores estén disponibles el servicio Zookeeper seguirá estándolo. Los clientes se conectan a un solo servidor Zookeeper, y mantienen una conexión TCP mediante la que envían requests, reciben respuestas, eventos… Si la conexión TCP se rompe, el cliente se conectará a otro servidor. Ilustración 6: Servicio Zookeeper 46 3.3.1.2.1 Configuración en el entorno •
Zookeeper requiere java en su versión 6 o superior •
Descargar la última versión de zookeeper (http://zookeeper.apache.org/releases.html) •
El servidor está contenido en un archivo JAR, así que la instalación consiste en crear una configuración, hay que crear un fichero dentro de la carpeta conf, en la raíz de la carpeta de Zookeeper llamado zoo.cfg con este contenido: tickTime=2000
dataDir=/var/zookeeper
clientPort=2181
Tabla 1: zoo.cfg tickTime: Es la unidad básica en milisegundos usada por Zookeeper. dataDir: La localización para guardar los snapshots de la base de datos interna y logs. clientPort: El puerto para escuchar las conexiones del cliente. •
Iniciar el servidor Zookeeper con el comando bin/zkServer.sh start
Zookeeper lo usará Storm como forma de coordinar sus nodos entre sí. 47 3.3.1.3 Kestrel Un solo servidor kestrel tiene una serie de colas. Una cola de kestrel es una cola de mensajes simple que corre en la Java Virtual Machine y usa el protocolo memcache para comunicarse con los clientes. Cada cola está estrictamente ordenada siguiendo el principio FIFO. Para proveer rendimiento los ítems se ponen en la cache del sistema, aunque solo los primeros 128MB, y cuando se para el servidor la cola se guarda en un fichero de journal. Kestrel es: •
Rápido •
Pequeño •
Duradero •
Fiable Este sistema de colas lo usará Storm para ingerir datos desde ellas, mandando el cliente los datos a las colas de kestrel. 3.3.1.3.1 Configuración en el entorno 3.3.1.3.1.1 Instalación El archivo servioticy_queues.scala hay que descargarlo a parte, y define las colas de datos de la plataforma COMPOSE. El fichero se adjunta en el anexo correspondiente. wget http://twitter.github.io/kestrel/download/kestrel2.4.1.zip
48 rm -rf kestrel-2.4.1
unzip kestrel-2.4.1.zip
mv kestrel-2.4.1 kestrel
cp servioticy_queues.scala kestrel/config
3.3.1.3.1.2 Ejecución rm -rf /tmp/kestrel-queue
rm -rf /tmp/kestrel.log
java -server -Xmx1024m -Dstage=servioticy_queues -jar
kestrel/kestrel_2.9.2-2.4.1.jar &
sleep 20
3.3.1.4 Jetty Jetty es un servidor web ligero, que en el ámbito de COMPOSE se usará para hacer peticiones web a la API, y poder así interactuar con la plataforma. 49 3.3.1.4.1 Configuración en el entorno •
Descargar jetty (http://download.eclipse.org/jetty/stable-­‐9/dist/) •
Descargar la api privada (private.war) y la pública (root.war). •
Dentro de root.war asegurarse que el fichero /WEB-­‐
INF/classes/config.properties tiene la configuración correcta, en este caso sería la siguiente: # Copyright 2014 Barcelona Supercomputing Center (BSC)
#
# Licensed under the Apache License, Version 2.0 (the
"License");
# you may not use this file except in compliance with the
License.
# You may obtain a copy of the License at
#
#
http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
software
# distributed under the License is distributed on an "AS IS"
BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied.
# See the License for the specific language governing
permissions and
# limitations under the License.
#-----------------------------------------------------------------------------# Configure uris for Couchbase configuration
50 public_uris=http://localhost:8091/pools
private_uris=http://localhost:8091/pools
# Configure couchbase bucket name
public_bucket=serviceobjects
private_bucket=privatebucket
# Configure sqlitedb for initial user management (TO DEPRECATE)
sqlitedb=jdbc:sqlite:/home/josep/userDB/users.db Primero hay que configurar la dirección correcta de los pools de Couchbase, después los nombres dentro de Couchbase que tienen los buckets correspondientes en los que se alojarán las instancias públicas y privadas, y finalmente el path hacia la base de datos de usuarios de sqlite. •
Arrancar jetty java –jar start.jar (en la carpeta raíz de jetty). 3.3.1.5 Couchbase Es una base de datos opensource, distribuida y orientada a documentos NoSQL, que está optimizada para aplicaciones interactivas. Estas aplicaciones deben dar servicio concurrentemente a muchos usuarios. Está diseñada para tener baja latencia a la hora de acceder documentos y throughput alto y sostenido. Puede implementarse tanto en una sola máquina como a gran escala. 51 3.3.1.5.1 Configuración en el entorno 3.3.1.5.1.1 Instalando Couchbase •
En el directorio raíz ejecutar: mkdir -p couchbase
rm -rf /tmp/couchbase-data
•
Descargar Couchbase (Se requiere la versión 2.2.0) wget http://packages.couchbase.com/releases/2.2.0/couchbaseserver-enterprise_2.2.0_x86_64_openssl098.deb
dpkg-deb -x couchbase-serverenterprise_2.2.0_x86_64_openssl098.deb couchbase
•
Carpeta de Couchbase cd couchbase/opt/couchbase
52 mkdir -p /tmp/couchbase-data
•
Desde una instalación no-­‐root bin/install/reloc.sh `pwd`
•
Iniciar servidor bin/couchbase-server -- -noinput –detached
•
Parar servidor bin/couchbase-server –k
•
Inicialización de instancias bin/couchbase-cli node-init -c localhost --node-init-datapath=/tmp/couchbase-data
bin/couchbase-cli
username=admin
cluster-init
-c
localhost
--cluster-init-password=password
--cluster-init–cluster-init-
ramsize=600 53 •
Crear buckets bin/couchbase-cli bucket-create -c localhost -bucket=serviceobjects --bucket-type=couchbase --bucketramsize=200 --bucket-replica=1 --user=admin --password=password
bin/couchbase-cli bucket-create -c localhost -bucket=privatebucket --bucket-type=couchbase --bucketramsize=200 --bucket-replica=1 --user=admin --password=password
•
Crear vistas para los Service Objects En el anexo aparece el documento byUser.ddoc. curl
-X
PUT
-H
"Content-Type:
application/json"
http://admin:password@localhost:8092/serviobjects/_design/user
-d @byUser.ddoc
3.3.1.6 Elasticsearch Es un servidor de búsqueda basado en Lucene. Provee un motor de búsqueda de textos con una interfaz web RESTful y documentos JSON libres de esquema. Está desarrollado en Java y está liberado como open source bajo los términos de la licencia Apache. 54 En el proyecto COMPOSE se ha usado como clúster para replicar los datos de Couchbase, siendo así más manejables de lo que la propia API de couchbase ofrece, y pudiendo alimentar a Kibana con datos para producir las estadísticas de popularidad. 3.3.1.6.1 Configuración en el entorno En este proyecto se ha usado la versión 1.0.1 de Elasticsearch y la versión 1.3 del Couchbase-­‐transport plugin. •
Descargar la versión reciente de Elasticsearch •
Entrar al directorio cd elasticsearch-<version>
•
Instalar el plug-­‐in de couchbase bin/plugin -install transport-couchbase -url \
http://packages.couchbase.com.s3.amazonaws.com/releases/elasticsearch-adapter/1.3.0/elasticsearch-transport-couchbase-1.3.0.zip
•
Fijar el username y password para el plugin, y definir como tipo de documento por defecto a trackingDocument echo "couchbase.password: password" >> config/elasticsearch.yml
55 echo "couchbase.username: admin” >> config/elasticsearch.yml
echo "couchbase.defaultDocumentType: trackingDocument” >>
config/elasticsearch.yml
•
Instalar el plugin head, para poder tener desde el navegador una consola de control de Elasticsearch bin/plugin -install mobz/elasticsearch-head
•
Iniciar Elasticsearch bin/elasticsearch
•
Al iniciar elasticsearch: Ilustración 7: Arranque de Elasticsearch La información marcada pertenece a la IP del plugin de Couchbase, que es la que habrá que indicar al hacer la referencia al clúster, en el apartado de réplicas de la consola de administración de Couchbase. 56 3.3.1.7 Kibana Kibana es una interfaz de búsqueda y analíticas web para sets de datos almacenados en Elasticsearch. Permite visualizar los datos de una manera comprensiva y visual en tiempo real. Requiere como mínimo la versión 0.90.9 de Elasticsearch, un navegador y un servidor web (en este caso se ha usado Apache2). 3.3.1.7.1 Instalación •
Descargar y extraer kibana en la carpeta de aplicaciones del servidor web (http://download.elasticsearch.org/kibana/kibana/kibana-­‐latest.zip) •
Editar el archivo config.js para apuntar al servidor elasticsearch •
Entrar a kibana desde el navegador 3.3.1.8 Storm En el alcance del proyecto se ha explicado en qué consiste Storm. 57 3.3.1.8.1 Configuración en el entorno •
Obtener Storm del repositorio wget
http://apache.rediris.es/incubator/storm/apache-storm-
0.9.1-incubating/apache-storm-0.9.1-incubating.tar.gz •
Descomprimir y renombrar tar xzf apache-storm-0.9.1-incubating.tar.gz
mv apache-storm-0.9.1-incubating storm
Ahora, solo queda descargar los paquetes del proyecto COMPOSE, compilarlos y abrir en algún entorno de desarrollo (eclipse en su versión Kepler en este caso) el proyecto mediante el fichero pom.xml provisto, a partir de aquí podemos ejecutar y desarrollar el proyecto. 58 3.3.2 El proyecto COMPOSE 3.3.2.1 Introducción a COMPOSE El Internet Of Things (IoT) está compuesto por objetos (ej. Sensores) que para poder comunicarse con la plataforma COMPOSE deben estar conectados a internet, y deben implementar el protocolo específico de COMPOSE. Una vez cumplido esto, pasan a ser Web Objects (WO), que tienen una identidad virtual dentro de COMPOSE. Los llamados Service Objects (SO) son las representaciones estándar internas de COMPOSE de objetos físicos, y se provee una API de los SO para comunicarse con los WO. La API también está disponible para los componentes internos dentro de COMPOSE. Los SO pueden combinarse con otros SO para crear Composite Service Objects (CSO), o pueden combinarse con servicios, añadiendo lógica operacional. Services: Business Logic Plane
Composite Services
Services
DATA
End-users
and
Applications
Composite
Service
Objects
Service Objects
Web Objects
Service Objects: Data Plane
Ilustración 8: Relación entre el plano de los SOs y el de Servicio Es importante destacar la diferencia entre “Servicios” de alto nivel y el ecosistema de los SO. Los SO proveen herramientas de procesado de datos a los Servicios de alto 59 nivel, y éstos a su vez proveen la lógica operacional introducida por los desarrolladores de COMPOSE. La figura resume la relación entre entidades en COMPOSE, en el plano de datos y en el de lógica operacional. Es importante notar que un CSO es tratado en COMPOSE como un elemento de procesado en el pipeline de la plataforma (de WOs a servicios y aplicaciones), lo que implica que el flujo de datos es controlado por la plataforma, al contrario de cómo pasa en servicios de alto nivel y servicios compuestos que corren código de desarrollador. Tanto los SO como los CSO son entidades de manjeo de datos en COMPOSE. Los SO proveen una interfaz básica para guardar y recuperar información, para ser usados por WO o servicios y aplicaciones. Los CSO quieren proveer un manejo avanzado de los datos en el data plane de COMPOSE: pueden hacer queries, agregación de datos y transformación de los mismos en cima de la capa de los SO. La interfaz para desplegar y acceder a un CSO es muy similar a la de un SO, ya que los dos son descritos usando documentos JSON que serán parseados e interpretados en tiempo real. La siguiente figura provee un breve diagrama de la relación entre CSOs y SOs. COMPOSE Service Objects Infrastructure
Data Repository and Registry
CSO
 D2.3.1
SO
 D2.1.1
API
Web Objects
Proxies
Smart
Objects
Other
COMPOSE
Components
Objects
Ilustración 9: Pila de los SO 60 3.3.2.2 Composite Service Object (CSO) Un CSO es un tipo de SO que puede subscribirse a streams de datos que provienen de otros SO y añadirles lógica computacional, que la ponen los desarrolladores. Como los SO, su entrada será un conjunto de streams que pueden ser accedidos por otros CSO o clientes. Los Streams generados por un CSO pueden ser vistos como un conjunto de variables que son calculadas en función de streams que provienen de otro SO o CSOs. Si un stream dentro de un CSO es una fuente de datos para otro stream de otro CSO, cuando la fuente emita un nuevo Sensor Update (SU) (Que no es más que un documento JSON) entonces los streams del CSO receptor actualizarán sus valores y potencialmente emitirán otro SU. Internamente, en la plataforma COMPOSE, un CSO es un JSON que define el comportamiento del CSO como respuesta a datos entrantes. El SU producido por un SO es un derivado de lo que le entra, usando expresiones algebraicas, booleanas, de strings y de arrays. El diseño de un CSO pretende ser extremadamente escalable, teniendo como referente estos tres principios: 1. Conducido por eventos 2. Libre de bloqueos (locks) 3. Orientado a streams Para diseñar un sistema que cumpla los principios 1 y 3, cada CSO es responsable de producir un SU cada vez que se recibe uno. La figura siguiente indica la situación. SO
2
SO
1
CSO
SO
3
61 Ilustración 7: Flujo de datos para un CSO Desde el punto de vista de la plataforma COMPOSE, los SU que cualquier SO o CSO generan son espontáneos, ya que son provocados por eventos que se generan fuera de la plataforma por los WO (Sobre los cuales COMPOSE no tiene control alguno). Esto nos lleva a una complicación en el diseño, ya que múltiples SU son requeridos por un CSO para producir otro SU de salida, pero los inputs no llegarán de forma síncrona porque los WO no están coordinados de ninguna manera entre ellos, ni tienen porque saber de la existencia de otros. SO
2
SO
1
SO
2
CSO
SO
3
SO
1
SO
2
CSO
SO
3
SO
1
CSO
SO
3
Ilustración 10: Resultado de ser un sistema conducido por eventos La figura anterior ilustra la situación, el CSO puede estar recibiendo SU de cualquiera de los 3, y por cada SU que recibe debería producir uno el, pero para hacerlo necesita datos de los tres. Como hemos dicho que el sistema debía estar libre de bloqueos, el modelo del flujo de datos propone que los CSO deberán tener dos fuentes de datos: SUs originados en algún lugar de COMPOSE y enviados al CSO (de lo que hablábamos hasta ahora), y datos requeridos por el CSO a otros componentes de la plataforma. Esto último significa que el CSO puede extraer datos guardados de otras entidades de COMPOSE que serán usados para producir el SU de salida. La siguiente figura ilustra esto. 62 SO
2
Subsription
SO
1
CSO
Queries
SO
3
Ilustración 11: Fuentes de datos de un SO Cada CSO en la plataforma COMPOSE se guarda internamente y se describe como un documento JSON que contiene toda la información necesaria para proveer la lógica de procesado de datos que encapsula. Esta lógica es expresada usando operaciones lógicas básicas, strings y operadores aritméticos. Las fuentes de los datos a procesar, como se ha mencionado, serán otros SO o queries al back-­‐end. La comunicación entre diferentes SO y CSO en el pipeline está impulsada por eventos, y las conexiones entre ellos se hace mediante subscripciones. La descripción de un CSO incluye un pipeline de procesado compuesto por diferentes etapas, que se siguen cada vez que un SU es recibido por el CSO. En este proceso de transformación de los datos, la plataforma COMPOSE hará un tracking de la manipulación de los datos que se realiza y enriquecerá los datos con información de provenance para asegurarar unas reglas de seguridad y privacidad. Los CSOs pueden desplegarse en la plataforma COMPOSE usando una API RESTful de una manera similar a los SO, pero con un conjunto de operaciones más limitado. 63 3.3.2.2.1 Identidad Cada CSO tendrá un ID único dado en tiempo de creación y será responsabilidad de la plataforma garantizar que así sea. 3.3.2.2.2 Output El output de un CSO consistirá en una estructura de datos JSON que incluirá los datos asociados a streams y canales que se provean en la definición del CSO. La principal diferencia entre un SO y un CSO es que el primero contiene datos que son directamente introducidos a la plataforma mediante un WO, mientras el CSO produce datos en función a la lógica interna del mismo. Cualquier par de outputs se dice que son compatibles entre ellos si sus streams y canales son de la misma naturaleza, formato y unidades. 3.3.2.2.3 Tipos de datos • Numéricos • Booleanos • String • Array de cualquier tipo de los anteriores 3.3.2.2.4 Data Provenance Esto puede ser visto como metadatos que reflejan el origen de los datos, así como operaciones o propiedades de los mismos. Principalmente se usará para respaldar la política de seguridad antes de que los datos se compartan en la plataforma o antes de que salgan. 64 Aunque la información de provenance no se guarda como parte del descriptor del CSO, se añadirá a todo SU producido por cualquier CSO. En la misma se incluirá información sobre por dónde ha pasado ese SU y por qué streams. 3.3.2.3 Pipeline de procesado Una vez un SU alcanza un CSO, va por una seria de etapas para transformarlo en un nuevo output. Primeramente, cuando un CSO recibe un SU de una fuente a la que está suscrito, puede necesitar acceder a los datos guardados de otras fuentes de datos involucradas en la transformación de los mismos (datos de otros SO o CSOs). En esta etapa se hacen queries al back end para obtener los datos que complementarán al SU original, y tener así la información completa para las siguientes etapas. Aunque esta es la primera etapa también se podrán realizar queries de manera implícita en las siguientes. La siguiente etapa consiste en la compilación de todas las expresiones JSONPath usadas en la descripción del CSO para substituirlas por los datos en sí a los que apuntan en los SU. La siguiente etapa es la substitución de los alias. Ahora que todos los JSONPaths en los valores de los alias han sido porcesados, los alias que aparecen en la definición del CSO son reemplazados por sus valores. Ahora, la definición de CSO, que depende de los datos de la entrada está completa, por lo que las siguientes etapas de procesado están solamente relacionadas a la transformación de los datos para crear nuevos. Por eso, la siguiente etapa es la de pre-­‐
filtros, en la que los SU se descartan si la aserción es falsa, y no pasaría a las siguientes etapas. El objetivo del filtro es evitar cualquier transformación futura si los datos son invalidos. Una vez se han validado los SU entonces el CSO puede empezar con el proceso de 65 transformación de los datos. Este proceso se realiza cogiendo todos los SU que provienen de las fuentes y operando con sus datos asociados para obtener un valor para el SU de salida. Después de la transformación viene el post-­‐filtro. El SU resultante de la transformación es evaluado y si no es válido se descarta. Por último el SU generado se guarda y se emite a los subscriptores del CSO. Adicionalmente, en esta etapa final, se llevan a cabo acciones para enviar el SU a los SO y CSOs que estén subscritos al mismo. La siguiente figura ilustra estas etapas. Emit
Actions
Store
Post-filter
Transform
Pre-filter
Aliases
JSON Path
compilation
Queries
Composite
Service Object
Logging
Ilustración 12: Pipeline de procesado 3.3.2.4 Especificación del CSO Aquí se explican los diferentes elementos que dormán parte de un CSO. Los CSO necesitan extraer información de estructuras SU. Para esto, COMPOSE adopta una sintaxis definida en JSONPath. La primera tabla muestra los campos que componen la definición de un CSO. La segunda un ejemplo de un JSONPath que accede al valor del canal speed en un SU generado por un stream. 66 {
“name”,
“description”,
“ID”,
"queries",
"groups",
"aliases",
"pre-filters",
"streams",
"post-filters",
"actions",
"links"
} Tabla 2: Elementos de un descriptor de CSO $.channels.speed.current-­‐value
Tabla 3: ejemplo de JSONPath 3.3.2.5 Elementos del CSO •
Name, desription, ID: Son strings que proveen información sobre la identidad del CSO. •
Queries: Como se ha dicho anteriormente, para no tener bloqueos ante la llegada de un SU la información que falte se puede requerir al back-­‐end mediante queries. Este es el apartado donde estructurarlas. 67 “queries”: {
"lastUpdate": {
"query": {
"filtered" : {
"query" : { "match_all": {} }, }
}, "sort": [
{
"doc.timestamp": { "order": "desc", "mode" : "max"
}
} ],
"fields" : ["doc.timestamp"]
} }
Tabla 4: Ejemplo de queries •
Groups: Permite la creación de grupos de CSOs y SOs que tienen salidas compatibles. "groups": {
“group1”: {
"members": [
SEMANTIC QUERY LANGUAGE TO BE DEFINED IN
ACCORDANCE TO WP3.1
],
"stream": "location",
Tabla 5: Estructura de datos de los grupos •
Aliases: Nombres cortos para strings que serán usados en la definición del CSO. 68 "aliases":[
{ "@x@": "who is what"},
{
"who": "John Doe",
"what": "who did it"
},
{ "@y@": "who is what"}
] Tabla 6: Estructura de datos de los alias •
Filters: tanto pre como post, controlan que inputs realmente merece la pena procesar/generar. "pre-filter":"$1.channels.temperature.current-value > 40"" "post-filter": "current-value !=
$this.lastUpdate().channels.presence.current-value" Tabla 7: filtros •
Streams: Contiene streams con sus canales correspondientes, y cada canal contiene un campo current-­‐value que representa el output que el CSO emitirá después de ingerir un nuevo SU, asumiendo que no se ha filtrado. “aliases”: [
{“wind-speed”: “$1.channels.wind-speed.currentvalue”},
{“hurricane”: “118”},
{“squall”: “102”}
],
69 (…)
“current-value”: “
wind-speed > hurricane ? \”12\” :
wind-speed > squall ? \”11\” :
\”0-10\”
” Tabla 8: ejemplo de lógica de transformación en un CSO •
Actions: Cuando una acción es invocada a través de la API de SO actions la acción se inicia en el WO correspondiente, que actuará como un proxy para el actor físico. Para mayor entendimiento: "actions":[
{
"name":"reboot devices",
"description":"Reboots the devices in the group",
"group": “group1”,
"action": "reboot",
}
] Tabla 9: Ejemplo del uso de acciones • Links: Contiene información semántica. "links":{
"@context": "http://www.composeprject.eu/contexts/CSO.jsonld",
"@id": "http://superstore.org/resource/shopping_cart",
"color": "Red",
"capacity": "300l",
70 "owner": " http://superstore.org/resource/megastore"
}
Tabla 10: Ejemplo del uso de links 71 3.3.2.6 Integración en la plataforma COMPOSE La plataforma compose tendrá diferentes componentes para implementar interconexiones entre SOs y CSOs. En particular consistirá en un front-­‐end web (RESTful API) y un back-­‐end (Couchbase) en el cual se guardarán los datos y metadatos del CSO. Como el sistema estará orientado a streams de IoT, el componente central de ingestión de datos será una topología escalable de Storm que procesará SUs entrantes en tiempo real mientras despacha subscripciones y consulta el back-­‐end. Para una búsqueda avanzada de texto y la implementación del sistema de popularidad se usará Elasticsearch. La siguiente figura ilustra los principales componentes en el plano de procesado de datos en la arquitectura de la plataforma COMPOSE. Security Module
Pub/Sub
HTTP
CouhBase
Real Time
(future)
Historic
(past)
Analytics + Queries
RDF-store
queries
(JSON-LD)
ElasticSearch and Primitives
DATA
Kestrel
Subscriptions
Task
Descriptor
Process
Service Objects API
(Data and Management ops)
Objects ,
Front – End &
Consumers
STORM
Ilustración 13: Pipeline de procesado de datos de COMPOSE 72 La lógica de los CSOs estará implementada a lo largo de estos componentes. La API estará implementada como una parte del front-­‐end, pero la mayoría de la lógica irá implementada en la tecnología de procesado de streams. Despachar subscripciones será otra de estas etapas, y enviará Sus a otras entidades internas o externas. A lo largo de la topología de procesado de streams, la plataforma contribuirá a mantener la información de data provenance de COMPOSE. 3.3.3 servioTicy servioTicy es un producto derivado de compose, que implementa la parte de procesado y guardado de los datos. Es el proyecto sobre el que se ha trabajado la fase final, implementando en el código que había hasta el momento la parte de data provenance e implementando un seguimiento de la popularidad, así que una vez descargado y compilado al proyecto se ha procedido a la edición del código sobre eclipse, como se irá viendo a lo largo del siguiente apartado. El proyecto, consiste, a grandes rasgos, en que los CSO explicados previamente tendrán la siguiente definición: {
"streams": {
"weather": {
"channels": {
"location": {
"type": "string",
"unit": "degrees"
},
73 "temperature": {
"type": "number",
"unit": "degrees"
}
}
},
"fahrenheit": {
"channels": {
"f": {
"current-value":
"{$weather.channels.temperature.current-value} * 1.8 + 32",
"type": "number"
}
}
},
"aboveSeventy": {
"channels": {
"temperature": {
"current-value":
"{$fahrenheit.channels.f.current-value}",
"type": "number"
}
},
74 "post-filter":
"{$result.channels.temperature.current-value} > 70"
}
}
} Tabla 11: Definición de un CSO en servioTicy Contendrán 3 streams: el primero, weather, contiene la localización y la temperatura actual del sensor. El segundo implementa una lógica de transformación para pasar la temperatura a grados Fahrenheit. El último, en base a la temperatura en Fahrenheit aplica un filtro para procesar solamente las que marquen más de setenta grados. Cada CSO representaría a una entidad que está enviando datos sobre temperatura y localización al sistema mediante SUs. {
"channels": {
"location": {
"current-value": "40.16,-71.34"
},
"temperature":
{
"current-­‐value": 50 } }, 75 "lastUpdate": 11991924234 } Ilustración 14: Ejemplo de SU También puede estar suscrito a otras entidades para recibir información de las mismas. Para asegurar la consistencia de los datos guardados sobre los streams sólo los SU nuevos serán aceptados por el sistema, así que para ser procesados, el campo lastUpdate debe ser mayor que uno contenido en el último SU generado. Si el SU no es nuevo se descartará. Ilustración 15: funcionamiento de lastUpdate A nivel de código, profundizando en Storm sólo se detallarán las partes implementadas y las adyacentes, no el código completo. Remitiéndonos a la explicación de COMPOSE, cada uno de los elementos del pipeline, que interpreta la lógica del CSO, vendría definido por un elemento bolt en Storm. La fuente de información al sistema vendrá definida por un spout que cogerá datos de la cola de Kestrel. Estos elementos se verían dirigidos entre sí por una topología. 76 Ilustración 16: Estructura del proyecto en Storm Ilustración 17: Topología de servioTicy en Storm La topología mostrada en la figura anterior ilustra lo explicado: Cada uno de los bolts es una etapa de procesado de los datos, excepto la línea marcada, el spout, que toma datos del Kestrel para alimentar a la topología. Los bolts y los spouts en Storm se definen con las funciones setBolt y setSpout respectivamente, indicando después con shuffleGrouping, si es un spout cual es para obtener datos y si es un bolt de que stream en concreto son los datos que se requieren para esa etapa de procesado. La 77 topología viene definida en el fichero DispatcherTopology.java, dentro del paquete dispatcher. En este proyecto se ha modificado el bolt StreamProcessorBolt y se ha creado uno nuevo, TrackingOfCheckOpidBolt. Para poder enviar SUs a la API se ha utilizado el programa postman. La API corre sobre jetty en el puerto 8080, si se ha configurado todo como en este documento se indica, y a continuación se indica cómo crear un CSO y cómo enviar SUs. Para crear una nueva instancia de CSO, debe enviarse la definición del mismo a la dirección donde se aloja la API. Como respuesta se obtiene la ID autogenerada (OPID en términos de servioTicy) del mismo y su momento de creación en tiempo epoch. Ilustración 18: Creación de un CSO Para enviar un SU hay que enviar la definición del mismo al stream correspondiente del CSO, que se identifica por su OPID, recibiendo como respuesta se obtiene un accepted, confirme se ha recibido correctamente y la definición del mismo. 78 Ilustración 19: Envío de un SU 3.3.4 Modificaciones hechas sobre servioTicy En este proyecto, una vez comprendido el funcionamiento interno del código de COMPOSE escrito hasta el momento, se han implementado las partes de data provenance y de tracking de popularidad. Aquí se explicará en detalle cómo se han implementado estas funcionalidades dentro de la parte de la topología de procesado de streams, Storm. 3.3.4.1 Data provenance Como se ha explicado anteriormente, la parte del proyecto de data provenance se requiere para cumplir políticas de seguridad. En el caso de COMPOSE no se 79 implementará sobre un CSO, sino sobre cada uno de los SU, ampliando la información que contienen para acoger datos sobre por que SOs ha pasado y qué streams. Para esta parte se ha modificado en primer lugar la definición de SU, incluida en el data model del proyecto: se han añadido dos campos nuevos. El primero, llamado soids, contendrá una lista de los identificadores de los sensores (OPID) por los que ha pasado el update, y el segundo contiene la lista de streams por los que ha pasado. La definición de un SU quedaría así pues: @JsonIgnoreProperties(ignoreUnknown = true)
public class SensorUpdate{
//
Stream location of the SO
//
{
//
"channels":{
//
"latitude": {
//
"current-value": 3.14159,
//
"unit": "degrees"
//
},
//
"longitude": {
//
"current-value": 2.11159,
//
"unit": "degrees"
//
}
//
},
//
"lastUpdate": 1199192939
//
"soids": [
//
],
//
"streamids": [
//
]
//
}
private LinkedHashMap<String, SUChannel> channels;
private Long lastUpdate;
80 private ArrayList<String> soids;
private ArrayList<String> streamids;
public LinkedHashMap<String, SUChannel> getChannels() {
return channels;
}
public void setChannels(LinkedHashMap<String, SUChannel>
channels) {
this.channels = channels;
}
public long getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(long lastUpdate) {
this.lastUpdate = lastUpdate;
}
public ArrayList<String> getSoids(){
return this.soids;
}
public void setSoids(ArrayList<String> soids){
this.soids = soids;
}
public ArrayList<String> getStreamids(){
return this.streamids;
}
public void setStreamids(ArrayList<String> streamids){
this.streamids = streamids;
}
}
81 Una vez se ha modificado la estructura de un SU, hay que modificar la parte del código en la que se guarda el update, para que quede constancia de por donde ha pasado. Para poder tener los datos listos en el bolt mencionado, los datos necesarios, es decir, el OPID y el stream id deben transmitirse a lo largo de la topología, ya que la parte de procesado la constituye el último bolt de la misma, StreamProcessorBolt. Para ello se han seguido una serie de pasos: Primeramente hay que mirar el bolt CheckOpidBolt, que es por el que entra cada SU que procesará el sistema. El SU nos proporciona el stream y el SO del que proviene, por lo que la información que hay que añadir al mismo SU a la hora de procesarlo hacia la base de datos la obtenemos aquí. Obtenemos los elementos del spout que a su vez obtiene las entradas de la cola de Kestrel con el método getStringByField, así que los campos streamid y soid los incluiremos en un stream de datos llamado stream, para que el próximo bolt pueda recogerlas. El campo su se incluyó para otras funcionalidades. Ilustración 20: Paso de parámetros entre bolts El siguiente paso es recoger la información transmitida por el stream stream al siguiente bolt, StreamDisparcherBolt, que recogerá la información y la remitirá a su 82 destino final, StreamProcessorBolt. Ilustración 21: Recogida de parámetros Ilustración 22: Paso de parámetros entre bolts II Ilustración 23: Paso de parámetros entre bolts III La primera figura corresponde a la obtención de las variables del bolt anterior. Posteriormente, mediante al función emit se especifica la tupla de salida, y finalmente en el método declareOutputFields se asignan los valores en el orden correspondiente. 83 Se han añadido los valores soid1 y streamid1, que se usarán en el siguiente bolt. Finalmente, en el bolt StreamProcessorBolt, se recuperan de nuevo los valores y antes de guardar el update y transmitirlo incorpora la información de data provenance, añadiendo los arrays que contienen la lista de identificadores y los streams por los que ha pasado. Por esta razón había que modificar la estructura del SU. Ilustración 24: Recogida de parámetros II 84 Ilustración 25: Data provenance Una vez implementado, al obtener el último update de un stream en concreto, veremos la información de data provenance. Ilustración 26: Last update con data provenance 85 3.3.4.2 Tracking de popularidad Esta parte consistirá en implementar un sistema para comprobar que SO se visitan más y qué streams son los más populares en términos de uso. Para ello intervienen 3 elementos: Couchbase, Elasticsearch y Kibana. El primero guarda en el bucket de tracking todas las instancias de SU que han pasado por el sistema, con el formato indicado anteriormente. El segundo se encarga de replicar la información del bucket de Couchbase en uno de sus nodos, ya que Elasticsearch ofrece unas posiblididades muy superiores que la API de Couchbase a la hora de tratar con los datos guardados, y lo ofrece en tiempo real. Por último, Kibana obtendrá los datos de Elasticsearch para poder mostrar de una forma agradable y visual las estadísticas de popularidad en función a los datos que va recibiendo desde Elasticsearch. 3.3.4.2.1 Couchbase Para esta parte se requiere una configuración concreta en el apartado XDCR de la consola de Couchbase. Se accederá a la misma mediante la dirección localhost:8091, con la identificación correspondiente indicada en la inicialización. En este caso admin y password. Una vez se accede habrá que crear una referencia a un clúster externo, donde se replicará la información. En este caso será un sistema Elasticsearch. Ilustración 27: Crear una referencia a un clúster 86 Una vez pinchamos en Create Cluster Reference: Ilustración 28: Crear una referencia a un clúster II En primer lugar hay que poner el nombre del clúster para identificarlo en la lista, junto con su IP. En este caso la obtenemos al iniciar elasticsearch, como se explicará más adelante. Por seguridad se introduce el usuario y password del plugin de Elasticsearch para Couchbase. Una vez hecho esto aparece la referencia al clúster que hemos definido y se procede a pinchar en Create Replication. 87 Ilustración 29: Configuración de réplicas externas en couchbase Una vez pinchado aparecerá una nueva ventana: Ilustración 30: Crear una réplica externa En ella primeramente se ha de indicar el bucket de couchbase que debe ser replicado, 88 en segundo lugar el clúster al que se replicará, y por último el bucket correspondiente en el clúster remoto al que se copiarán los datos. Es importante que se aplique el proteocolo de transferencia versión 1, o las réplicas fallarán. Con esto Couchbase queda listo para enviar datos a Elasticsearch. 3.3.4.2.2 Elasticsearch En primer lugar habrá que crear desde la consola de administración de Elasticsearch (localhost:9200) un nuevo índice, que será al que hagamos referencia al crear la réplica desde la consola de administración de couchbase. En este caso se le ha llamado tracking. En primer lugar habrá que crear desde la consola de administración de elasticsearch o el terminal: curl -XPUT 'http://localhost:9200/tracking'
Ilustración 31: plugin head de Elasticsearch Si se sigue la documentación también se necesita importar el fichero de mapping de Couchbase, para que así Elasticsearch sepa como indexar ciertos campos. Para esto se 89 ha creado el siguiente script: curl -XPUT 'http://localhost:9200/tracking' -d '{
"mappings" : {
"trackingDocument": {
"_source" : {
"includes" : ["meta.*", "doc.*",
"_timestamp"]
},
"_timestamp" : {
"enabled" : true,
"store" : true
},
"properties": {
"doc": {
"properties": {
"soid": {
"type": "string"
},
"streamid": {
"type": "string"
},
"date": {
"type": "date"
}
}
}
}
}
}
}'
90 En este script se envía un tipo nuevo de documento al índice tracking, trackingDocument. Es importante que en el apartado _source se incluya la referencia a doc (“doc.*”), ya que sino Kibana solo podrá acceder a la metainformación del documento, haciendo imposible acceder a los datos del documento en sí. También, como viene indicado en la instalación de Couchbase hay que hacer referencia a este tipo de documento por defecto, ya que sino el plugin de transporte de Couchbase crea un tipo de documento por defecto, couchbaseDocument, al que sólo podemos acceder a los metadatos, por lo que Kibana no podría obtener ninguna información real de los campos del documento. En este nuevo tipo de documento, solo indicamos que se envíen desde Couchbase a Elasticsearch para cada instancia sus campos soid, streamid y su fecha, ya que nada más será necesario (no hay que enviar información sobre Data Provenance) para hacer un seguimiento de la popularidad. 3.3.4.2.3 Kibana Si todo ha ido correctamente configurando Couchbase, Kibana vendrá prácticamente listo para interpretar los datos que vienen desde Elasticsearch. El apartado fields nos dice qué campos hay disponibles para realizar búsquedas sobre ellos, ofreciendo por defecto estadísticas sobre los hits en cada uno de los campos. Esto es lo que buscábamos. Kibana tiene muchas más opciones, pero debido a unos problemas técnicos (en las múltiples versiones con diferentes servidores web imposibilitaba el guardado del dashboard) no se han podido probar todas ellas, aunque el resultado ha sido satisfactorio. 91 •
Descargar y extraer kibana en la carpeta de aplicaciones del servidor web (http://download.elasticsearch.org/kibana/kibana/kibana-­‐latest.zip) •
Editar el archivo config.js para apuntar al servidor Elasticsearch •
Entrar a kibana desde el navegador Ilustración 32: Dashboard de kibana 92 Ilustración 33: Popularidad por SO En la figura anterior se puede apreciar el apartado fields. Se puede marcar el campo del que se quiere obtener la información y generar a partir de ello una gráfica con el número de hits de cada uno de los diferentes valores que se obtienen de ese campo. Ilustración 34: Popularidad por stream 93 4 Desviaciones en la planificación y costes finales 4.1 Desviaciones respecto a la planificación inicial Las desviaciones respecto a la planificación inicial han planteado una reforma total en cuanto a objetivos, tiempo, y recursos. Estas desviaciones han sido totalmente necesarias, ya que las dos primeras tecnologías resultaron ser infructuosas en cuanto a la cantidad de tiempo invertida y el número de fallos que presentaban, y, aunque facilitaron la comprensión y el estudio de estas tecnologías a nivel teórico y en cierta medida en cuanto a errores prácticos, ha sido necesario buscar algo más estable para poder trabajar sobre ello. Storm ha sido la tecnología que ha satisfecho esa necesidad y respecto a la cual se ha llevado la parte final del proyecto, con un resultado satisfactorio en cuanto a los nuevos objetivos planteados, pero al ser mucho más compleja que las anteriores los objetivos de cambiar la capa de comunicación (y por tanto, el uso de Infiniband y OFED) se vieron truncados por falta de conocimientos técnicos para el poco tiempo que quedaba disponible hasta la fecha límite. Por estos motivos se decidió trabajar directamente sobre una aplicación de Storm, al considerarla compleja como para acoger una gran parte del proyecto. La planificación también se ha visto afectada por una baja imprevista de casi 3 meses, que ha provocado inevitablemente un retraso en el conjunto del proyecto, que en un principio iba a estar listo para abril hasta junio. A continuación se presenta la planificación inicial del proyecto, correspondiente a la primera fase para poder apreciar con más detalle lo que se pretendía y el cambio de objetivos realizado. La planificación de la segunda fase del proyecto no se presenta porque no llegó a haberla en sí, ya que viendo el caso de Blockmon no se quiso correr más riesgo antes de probar que la tecnología funcionase correctamente, y desafortunadamente no fue así. 94 4.2 Planificación inicial (Primera fase) 4.2.1 Estudio preliminar de blockmon Descripción de la tarea: Esta tarea consistirá en el estudio del motor blockmon, para ver su funcionamiento básico y realizar el testing necesario para después poder compararlo con la versión modificada que se implementará del mismo. Riesgo: Bajo Recursos: humanos, mano de obra Dependencia: Ninguna Duración: 10 dias 4.2.2 Estudio de la API de OFED Descripción de la tarea: Esta tarea consistirá en el estudio de la API que ofrece OFED para programar sobre una tarjeta de red infiniband, realizar un programa básico de prueba y verificar que realmente funciona como se espera. Aunque al principio del proyecto esta tarea será más intensiva para coger familiaridad con dicho entorno de programación, se extenderá durante la fase de implementación de esta tecnología sobre blockmon. 95 4.2.3 Identificación de elementos en blockmon Descripción de la tarea: Esta tarea consistirá en la identificación y comprensión de la parte del código de blockmon que realiza la comunicación entre los nodos, actualmente implementada mediante el paradigma TCP/IP. Esta será la parte que posteriormente tengamos que tratar en la siguiente tarea. Riesgo: Medio Recursos: humanos, mano de obra Dependencia: Tarea 1.1 Duración: 10 dias 4.2.4 Diseño de la solución Descripción de la tarea: Esta tarea consistirá en el diseño de una solución antes de empezar a realizar la modificación propiamente dicha en función de los elementos identificados en el paso anterior Riesgo: Medio Recursos: humanos, mano de obra Dependencia: Tarea 1.1 96 Duración: 7 dias 4.2.5 Cambio de los elementos identificados Descripción de la tarea: Esta tarea consistirá en el cambio de la parte identificada en la tarea 1.3 para que trabaje con la tecnología infiniband programada con la API de OFED. Riesgo: Alto Recursos: humanos, mano de obra Dependencia: Tareas 1.2, 1.3 Duración: 41 dias 4.2.6 Evaluación del rendimiento Descripción de la tarea: Una vez implementada la versión custom de blockmon, que trabajará con infiniband se realizarán las mismas pruebas con la misma entrada que se ha usado en la tarea 1.1, obteniendo así los resultados con los que se podrán comparar las 2 versiones. Riesgo: Bajo Recursos: Mano de obra, switch infiniband, clúster de 4 máquinas 97 Dependencia: Tareas 1.1, 1.4 Duración: 7 dias 4.2.7 Conclusiones Descripción de la tarea: Viendo los resultados obtenidos de las 2 implementaciones (y de la arquitectura en caso de hacerse), dar una serie de valoraciones en función a la experiencia del proyecto sobre la programabilidad del mismo y su rendimiento. También acabar el redactado de la memoria y preparar la presentación Riesgo: Bajo Recursos: humanos, mano de obra Dependencia: Tarea 1.4 Duración: 7 dias 4.2.8 Arquitectura alternativa (Opcional) Descripción de la tarea: Si el proyecto va según lo previsto y queda margen de tiempo suficiente, se podría replantear la arquitectura de blockmon para obtener distintos beneficios de ello, o ver cómo afectaría al sistema. Riesgo: Alto 98 Recursos: humanos, mano de obra Dependencia: Tarea 1.5 Duración: Indeterminada 4.2.9 Gantt Ilustración 35: Diagrama de Gantt inicial 4.3 Desviaciones respecto al presupuesto inicial Las desviaciones en cuanto al presupuesto inicial vienen dadas por haberse alargado el proyecto y la innecesidad posterior a la primera fase de disponer de un equipo infiniband. Todo esto ha acabado representando un coste de 3390€, frente a los 4440€ + alquiler de equipamiento iniciales 99 4.3.1 Costes iniciales 4.3.1.1 Estudio preliminar de blockmon Costes Fijos: 10 días de trabajo * 6 horas/día * 7,5€/h = 450€ Costes variables: 2 días de margen * 6 horas/día * 7,5€/h = 90€ 4.3.1.2 Estudio de la API de OFED + identificación de elementos en blockmon Costes Fijos: 20 días de trabajo * 8 horas/día * 7,5€/h = 1200€ Costes variables: 2 días de margen * 8 horas/día * 7,5€/h = 120€ 4.3.1.3 Diseño de la solución Costes Fijos: 7 días de trabajo * 6 horas/día * 7,5€/h = 315€ Costes variables: 2 días de margen * 6 horas/día * 7,5€/h = 90€ 4.3.1.4 Implementación de los cambios Costes Fijos: 41 días de trabajo * 6 horas/día * 7,5€/h = 1845€ Costes variables: 2 días de margen * 6 horas/día * 7,5€/h = 90€ 4.3.1.5 Evaluación del rendimiento Costes Fijos: 7 días de trabajo * 6 horas/día * 7,5€/h = 315€ Costes de maquinaria: •
1 switch infiniband •
4 máquinas en clúster para realizar pruebas 100 * Las máquinas llevan software opensource, y no se tienen por tanto en cuenta las licencias 4.3.1.6 Conclusiones, redacción final y presentación Costes Fijos: 7 días de trabajo * 6 horas/día * 7,5€/h = 315€ 101 5 Compromiso social 5.1 Entorno legal Sólo se tiene en cuenta en el aspecto legal el proyecto COMPOSE, que es sobre el que se han hecho modificaciones. El resto de aplicaciones y servicios tienen sus propios términos en cuanto a licencias, pero todas son de libre distribución y no han sido modificadas en ningún sentido. El proyecto COMPOSE cumple las siguientes directrices europeas, los detalles de cada una se incluyen en los enlaces provistos. : •
Directiva 95/46/CE del Parlamento Europeo y del Consejo, de 24 de octubre de 1995, sobre la protección de las personas físicas en lo que respecta al tratamiento de datos personales ya la libre circulación de estos datos (http://eurlex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:31995L0046:en:
HTML). •
Artículo 29 DATA PROTECTION WORKING PARTY (http://ec.europa.eu/justice/policies/privacy/docs/wpdocs/2009/wp163_en.pd
f). •
El artículo 8 de la Carta de los Derechos Fundamentales de la UE (http://www.europarl.europa.eu/charter/pdf/text_en.pdf). 102 5.2 Entorno medioambiental El proyecto se ha realizado íntegramente en un entorno local, por lo que la repercusión medioambiental que tendrá es la suma de horas de electricidad usada por los equipos usados para desarrollar el proyecto, así como lo que haya costado en términos de medio ambiente fabricar el equipo que lo ha hecho posible. Con los datos disponibles, ya que no hay un acceso claro a las políticas medioambientales de cada empresa (no son transparentes en otros países en los que operan, o no se conoce el coste de sus instalaciones y manufacturación) no se puede hacer una estimación exacta y numérica del coste medioambiental del proyecto. 103 6 Conclusiones El objeto de estudio en este proyecto han sido las tecnologías que permiten un procesado de streams en tiempo real. El rápido crecimiento de internet, y con ello llegar a un punto en el que no sólo es importante recibir la información, sino también cobra importancia el cuando se recibe, ha sido lo que ha generado la necesidad y proliferación de este tipo de tecnologías. El primer caso de estudio fue Blockmon, una plataforma de procesado de strems que ofrece un alto rendimiento. El objetivo era modificar la parte de comunicación de la aplicación para que pudiese usar la API de OFED, para correr sobre infiniband, y no sobre sockets. El mayor problema al enfrentar este caso ha sido la falta de documentación. El hecho de que sean tecnologías relativamente recientes, y que esta en concreto estaba siendo desarrollada por un proyecto pequeño y poco conocido ha generado mucho retraso en términos de tiempo y de esfuerzo, ya que los errores o la comprensión del código se dificultan con una escasez de material para poder entender qué está pasando. Lo que motivó pues la decisión de un cambio de rumbo en el proyecto fue la falta de documentación y el tiempo consumido por procesar numerosos errores que no se indicaba como solucionarlos, además de la confirmación de que Blockmon había dejado de desarrollarse en 2011, por lo que los fallos no se resolverían. La segunda parte ha consistido en el estudio y ejecución de la plataforma Apache S4, pero después de eso desde COMPOSE se decidió usar Storm. Esta etapa también la marcó la falta de documentación sobre este tipo de plataformas, aunque fue de utilidad la experiencia previa con blockmon. Apache S4 dejó de desarrollarse en junio de 2013. La parte final del proyecto ha consistido en el estudio de Storm, una tecnología algo más estable y aún en activo, para participar en el desarrollo de un producto para esta plataforma, un derivado de COMPOSE. Se descartó la primera línea de desarrollo del proyecto, ya que Storm es mucho más complicado que Blockmon, y en este punto del 104 proyecto ya se habían destinado mucho tiempo y recursos, por lo que se decidió desarrollar sobre la tecnología, y no modificarla. En esta parte se ha desarrollado la parte de data provenance y un tracking de popularidad para este producto, llamado servioTicy. En primera instancia han ayudado a una mejor comprensión del proyecto las fases anteriores, ya que globalmente la idea de procesado por etapas es la misma, pero adaptándose a la metodología de la tecnología en concreto. La rapidez y la inherente multitud de cambios a lo largo del proceso de desarrollo de internet, hace que, estas tecnologías, a pesar de ser relativamente recientes, vayan evolucionando de una manera rápida y en muchos casos sean igual de rápidamente descartadas a favor de otras. También hay que mencionar que la curva de aprendizaje para este tipo de tecnologías es al principio muy lenta, ya que son complejas. Personalmente, la mayor dificultad de este proyecto han sido los múltiples cambios de rumbo que se han producido durante el mismo, y el tiempo de reacción y decisión crítico de abandonar una línea de investigación cuando ha consumido más recursos de lo previsto. También ha sido un gran problema a lo largo de todo el proyecto la escasez de documentación y de resolución de problemas, cosa que ha realentizado el proceso de forma considerable. También ha tenido aspectos positivos, ya que el objetivo de llegar a conocer estas tecnologías se ha cumplido, y, aunque el objetivo inicial de modificar una de ellas no se ha podido implementar por las razones ya mencionadas, he acabado desarrollando código para Storm, he conocido el funcionamiento e interacción con los elementos adicionales que esta tecnología requiere, he aprendido a solventar errores muy específicos con muy poca documentación y ahora se valorar lo que es trabajar con algo estable y documentado: sin estas dos premisas, para el desarrollador se puede hacer una auténtica pesadilla adentrarse un una tecnología en concreto. 105 7 Referencias 1. A. di Pietro, F. Huici, N. Bonelli, B. Trammell, P. Kastovsky, T. Groleat, S. Vaton, and M. Dusi. Blockmon: Toward high-­‐speed composable network traffic measurement. In Proceedings of the IEEE Infocom Conference (mini-­‐
conference), 2013. 2. Kestrel Queues. https://github.com/robey/kestrel. 3. D. Eyers, T. Freudenreich, A. Margara, S. Frischbier, P. Pietzuch, and P. Eugster. Living in the present: on-­‐the-­‐fly information processing in scalable web architectures. In Proceedings of the ACM International Workshop on Cloud Computing Platforms, 2012. 4. J. Kreps, N. Narkhede, and J. Rao. Kafka: A distributed messaging system for log processing. In Proceedings of the International Workshop on Networking Meets Databases, 2011. 5. Cloud MapReduce. http://code.google.com/p/cloudmapreduce. 6. Y. Lee and Y. Lee. Toward scalable internet traffic measurement and analysis with hadoop. Comput. Commun. Rev., 43(1):5–13, Jan. 2013. 7. N.Bonelli, A. Di Pietro, S. Giordano, and G. Procissi. On multi–gigabit packet capturing with multi–core commodity hardware. In Proceedings of the Passive and Active Measurement Conference, 2012. 8. J. Dean and S. Ghemawat. Mapreduce: simplified data processing on large clusters. Commun. ACM, 51(1):107–113, 2008. 106 9. FP7 Demons Project. http://fp7-­‐demons.eu. 10. BlockMon. http://blockmon.github.com/blockmon. 11. The 0MQ Pro ject. http://www.zeromq.org. 12. The Nimbus Project. http://www.nimbusproject.org. 13. Apache Hadoop. http://hadoop.apache.org 14. Apache S4. http://incubator.apache.org/s4/doc/0.6.0/. 15. Storm. https://github.com/nathanmarz/storm/wiki/Setting-­‐up-­‐development-­‐
environment. 16. Getting started on Storm. https://github.com/nathanmarz/storm-­‐
starter#getting-­‐started. 17. Zookeeper. http://zookeeper.apache.org/doc/r3.3.3/zookeeperOver.html. 18. ServioTicy. http://www.servioticy.com/?page_id=27. 19. Couchbase. http://www.couchbase.com/. 20. QLogic. (2011). QLogic resources. Obtenido de http://www.qlogic.com/Resources/Documents/TechnologyBriefs/Adapters/Tec
h_Brief_Introduction_to_Ethernet_Latency.pdf 21. Infiniband Trade Association. (s.f.). Infiniband Trade Association. Obtenido de Infiniband Trade Association: http://www.infinibandta.org 22. Top 500. (2013). Top 500. Obtenido de Top 500: http://www.top500.org 107 23. Open Fabrics Alliance. (s.f.). Open Fabrics Alliance resources. Obtenido de OFED overview: https://www.openfabrics.org/resources/ofed-­‐for-­‐linux-­‐ofed-­‐for-­‐
windows/ofed-­‐overview.html 24. Open Fabrics Alliance. (2010). Open Fabrics Alliance resources. Obtenido de OpenFabrics Enterprise Distribution: Driving Performance and Efficiency in the TOP500: https://www.openfabrics.org/images/docs/PR/OFA_Top500.pdf 25. Simoncelli, D., Gringoli, F., Dusi, M., & Niccolini, S. (Abril de 2013). Sigcomm. Obtenido de Sigcomm: http://www.sigcomm.org/sites/default/files/ccr/papers/2013/April/2479957-­‐
2479962.pdf 26. Armbrust, M., Fox, A., Griffith, R., & Patterson, D. (Abril de 2010). A view of cloud computing. Communications of the ACM . 27. Stonebraker, M., & Zdonik, S. (2005). The 8 requirements of real-­‐time stream processing. ACM SIGMOD . 28. Odlyzko, A. (2001). Internet growth: Is there a „Moore’s Law” for data traffic. 29. Cherniack, M., Balakrishnan, H., Balazinska, M., Carney, D., Cetintemel, U., Xing, Y., & Zdonik, S. B. (2003, January). Scalable Distributed Stream Processing. In CIDR (Vol. 3, pp. 257-­‐268). 30. Stonebraker, M., Çetintemel, U., & Zdonik, S. (2005). The 8 requirements of real-­‐time stream processing. ACM SIGMOD Record, 34(4), 42-­‐47. 31. Jetty. http://download.eclipse.org/jetty/. 32. Elasticsearch http://www.elasticsearch.org/. 108 33. Kibana. https://github.com/elasticsearch/kibana 109 8 Anexo 8.1 Código de la aplicación WordCount 8.1.1 Clase Sentence public class Sentence {
private String string;
public Sentence(){
// default constructor
}
public Sentence(String string) {
this.string = string;
}
public String getString() {
return string;
}
public void setString(String message) {
this.string = message;
}
public String getSentenceId(){
// all sentences have the same key
return "1";
}
public void setSentenceId(String id){
// do nothing
}
@Override
public String toString() {
return "Sentence [string=" + string + "]";
110 }
}
8.1.2 Clase Word public class Word {
private String string;
public String getString() {
return string;
}
public void setString(String message) {
this.string = message;
}
@Override
public String toString() {
return "Word [string=" + string + "]";
}
} 8.1.3 clase WordReceiverPE import io.s4.dispatcher.Dispatcher;
import io.s4.processor.AbstractPE;
public class WordReceiverPE extends AbstractPE {
111 private StringBuilder builder = new StringBuilder();
private Dispatcher dispatcher;
public Dispatcher getDispatcher() {
return dispatcher;
}
public void setDispatcher(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public void processEvent(Word word) {
System.out.println("Received: " + word);
builder.append(' ').append(word.getString().trim());
if (builder.toString().endsWith(".")) {
System.err.print("End of sentence found");
dispatcher.dispatchEvent("Sentence", new
Sentence(builder.toString()));
builder.setLength(0);
}
}
public void processEvent(Sentence sentence) {
System.out.println("Received Sentence(WordReceiverPE) : "
+ sentence);
}
@Override
public void output() {
// TODO Auto-generated method stub
}
112 @Override
public String getId() {
return this.getClass().getName();
}
}
8.1.4 Clase SentenceReciever import io.s4.processor.AbstractPE;
public class SentenceReceiverPE extends AbstractPE {
public void processEvent(Sentence sentence){
System.out.println("Received Sentence: " + sentence);
}
@Override
public void output() {
// TODO Auto-generated method stub
}
@Override
public String getId() {
return this.getClass().getName();
}
}
113 8.1.5 WordCountconf xml <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans2.0.xsd">
<bean id="wordCatcher" class="test.s4.WordReceiverPE">
<property name="dispatcher" ref="dispatcher"/>
<property name="keys">
<list>
<value>RawWords *</value>
<value>Sentence *</value>
</list>
</property>
</bean>
<bean id="sentenceCatcher"
class="test.s4.SentenceReceiverPE">
<property name="keys">
<list>
<value>Sentence *</value>
</list>
</property>
</bean>
<bean id="dispatcher" class="io.s4.dispatcher.Dispatcher"
init-method="init">
<property name="partitioners">
<list>
<ref bean="sentenceIdPartitioner" />
114 </list>
</property>
<property name="eventEmitter" ref="commLayerEmitter" />
<property name="loggerName" value="s4" />
</bean>
<bean id="sentenceIdPartitioner"
class="io.s4.dispatcher.partitioner.DefaultPartitioner">
<property name="streamNames">
<list>
<value>Sentence</value>
</list>
</property>
<property name="hashKey">
<list>
<value>sentenceId</value>
</list>
</property>
<property name="hasher" ref="hasher" />
<property name="debug" value="true" />
</bean>
</beans> 115 8.1.6 TextMessageSender import io.s4.client.Driver;
import io.s4.client.Message;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class TestMessageSender {
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("No host name specified");
System.exit(1);
}
String hostName = args[0];
if (args.length < 2) {
System.err.println("No port specified");
System.exit(1);
}
int port = -1;
try {
port = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
System.err.println("Bad port number specified: " +
args[1]);
System.exit(1);
}
if (args.length < 3) {
System.err.println("No stream name specified");
System.exit(1);
}
String streamName = args[2];
116 if (args.length < 4) {
System.err.println("No class name specified");
System.exit(1);
}
String clazz = args[3];
Driver d = new Driver(hostName, port);
Reader inputReader = null;
BufferedReader br = null;
try {
if (!d.init()) {
System.err.println("Driver initialization
failed");
System.exit(1);
}
if (!d.connect()) {
System.err.println("Driver initialization
failed");
System.exit(1);
}
inputReader = new InputStreamReader(System.in);
br = new BufferedReader(inputReader);
for
(String inputLine = null; (inputLine =
br.readLine()) != null;) {
String string =
"{\"string\":\""+inputLine+"\"}";
System.out.println("sending " + string);
Message m = new Message(streamName, clazz,
string);
d.send(m);
}
} catch (IOException e) {
117 e.printStackTrace();
}
finally {
try { d.disconnect(); } catch (Exception e) {}
try { br.close(); } catch (Exception e) {}
try { inputReader.close(); } catch (Exception e) {}
}
}
} 118 8.2 Servioticy_queues.scala import com.twitter.conversions.storage._
import com.twitter.conversions.time._
import com.twitter.logging.config._
import com.twitter.ostrich.admin.config._
import net.lag.kestrel.config._
new KestrelConfig {
listenAddress = "0.0.0.0"
memcacheListenPort = 22133
textListenPort = 2222
thriftListenPort = 2229
queuePath = "/tmp/kestrel-queue"
clientTimeout = 30.seconds
expirationTimerFrequency = 1.second
maxOpenTransactions = 100
// default queue settings:
default.defaultJournalSize = 16.megabytes
default.maxMemorySize = 128.megabytes
default.maxJournalSize = 1.gigabyte
default.maxItems = 0
admin.httpPort = 2223
admin.statsNodes = new StatsConfig {
reporters = new TimeSeriesCollectorConfig
}
queues = new QueueBuilder {
119 name = "services"
// Timeout for the descriptors
maxAge = 120.seconds
maxItems = 1500000
keepJournal = false
expireToQueue = "discarded"
} :: new QueueBuilder {
name = "discarded"
syncJournal = 0.seconds
}
//
aliases = new AliasBuilder {
//
name = "wx_updates"
//
destinationQueues = List("weather_updates")
//
} :: new AliasBuilder {
//
name = "spam_all"
//
destinationQueues = List("spam", "spam0")
//
}
/*
loggers = new LoggerConfig {
level = Level.INFO
handlers = new FileHandlerConfig {
filename = "/var/log/kestrel.log"
roll = Policy.Never
}
}
*/
}
120 8.3 byUser.ddoc { “views”: { “byUser”: { “map”: ”function(doc, meta){ \nemit(doc.userId, meta.id);\n }“ } } } 121 
Descargar