#SISTEMAS DISTRIBUIDOS
##Práctica no guiada: Sockets, Streaming de Eventos, Colas y modularidad.
#Art With Drones
Joan Cerveto Serrano
5 de noviembre de 2023
Tercer año de Ingeniería Informáitca.
Universitat d'Alacant
Escuela Politécnica Superior
Descargar repositorio:
git clone https://github.com/jcerveto/art_with_drones.git
cd art_with_drones1. Introducción 2. Tecnologías
El objetivo de la práctica a desarrollar es un sistema distribuido que implemente una simulación de una solución para la creación de figuras mediante dispositivos autónomos (drones) manejados en tiempo real.
Se podrán lanzar drones que formen figuras específicas. Cada dron se podrá desplegar en máquinas diferentes dentro de una misma red local.
El uso de docker, Apache Kafka y sockets ha sido crucial para el desarrollo de esta práctica.
Se requiere, como mínimo, la implementación de los siguientes módulos:
- AD_Registry.
- AD_Engine.
- AD_Weather.
- AD_Drone.
Además de estos cuatro módulos, en la presente práctica se ha desarrollado dos servicios más:
- AD_Database para desplegar y manejar adecuadamente la base de datos.
- AD_Frontend para visualizar de una manera más amigable el movimiento de los drones.
En el proyecto adjuntado con la memoria, además de estos módulos se ha adjuntado una carpeta docs/ para almacenar toda información relevante del proyecto. También otras enunciado/ y pruebas/.
La práctica ha sido desarrollada con el control de versión Git y GitHub. En el proyecto se podrá consultar el historial de commits.
Querría aclarar que, como TypeScript se transpila a JavaScript, a lo largo de esta memoria se puede utilizar indistintamente las palabras TypeScript como JavaScript para referirnos al mismo código. Incluso también Node.
A la hora de levantar cada docker-compose se ha utilizado el parámetro --build para forzar a que se reconstruya la imagen, por si hubiera cambios en el código.
Durante el desarrollo de esta práctica se han utilizado diferentes tecnologías para conseguir el mejor desempeño posible.
Para facilitar el despliegue de los diferentes servicios en varias máquinas para tener un sistema realmente distribuido se ha hecho uso de docker. Se ha utilizado tanto imágenes y contenedores creados por separados como docker-compose para automatizar estos procesos. Se han expuesto los puertos y se han montado diferentes volúmenes para conseguir una persistencia adecuada en todas las aplicaciones.
La práctica tendrá el siguiente esquema de puertos:
| Servicio | Puerto | Observación |
|---|---|---|
| AD_Engine | 8888 | Servidor HTTP |
| AD_Engine | 8080 | Servidor de Sockets |
| AD_Drone | Sin puerto | Actúa como cliente. |
| AD_Database | Sin puerto | Se usa una BBDD sqlite. Funciona con archivos, no puertos. |
| AD_Registry | 6000 | Servidor de Sockets |
| AD_Weather | 5000 | Servidor de Sockets |
| AD_Frontend | 3000 | Servidor de HTTP |
| Zookeeper | 2181 | - |
| Kafka | 29092 | - |
Los mensajes enviados entre servicios, tendrán el siguiente formato:
A lo largo de esta práctica de ha utilizado TypeScript, Python y JavaScript. Además de algunos scripts en Bash.
Más del 45% está escrito en TypeScript. Únicamente se ha utilizado este lenguaje para el desarrollo de AD_Engine. Para elegir qué tecnología utilizar para este servicio, lo primero que me planteé fue buscar una que tuviera una gran facilidad en la gestión de hilos concurrentes. El entorno de ejecución de Node permite esto sin ningún tipo de dudas. Una vez decidido que quería ejecutar el servidor en código de JavaScript tendría que decidir entre hacerlo con código nativo de JavaScript o si quería transpilar mi código de TypeScript a JavaScript. Como quería tener una aplicación bien modularizada y escalable, elegí hacerlo con TypeScript.
El 33% del código de está escrito en Python. Elegí este lenguaje por la cantidad de documentación que tiene a lo largo de todo Internet. También, sin duda, por la facilidad de escritura y ejecución del mismo.
Para hacer el servicio AD_Frontend se ha utilizado el framework React.js. Además de HTML y CSS.
El resto del código del proyecto es JavaScript o Shell. Se han utilizado para tareas menores, de pruebas o de automatización de ejecución de procesos.
Para Node se ha utilizado la imagen: node:18-alpine. Para python se ha utilizado la imagen: 3.9-alpine.
\newpage
En cuanto a la persistencia, se ha utilizado una base de datos sqlite para conectar los drones registrados entre AD_Registry y AD_Engine.
Dentro de los cuatro servicios también se han utilizado archivos para mantener una lógica de persistencia entre estos.
En AD_Drone se ha utilizado un archivo csv para almacenar los @id, @alias y @token de los drones registrados en AD_Registry.
En AD_Weather se ha guardado un archivo csv para que el servidor pueda leer la temperatura de las ciudades.
En AD_Engine se ha creado una tabla para almacenar la información relacionada con la figura actual presentada, junto a los drones que están actualmente creándola.
La base de datos se compone de dos tablas. Donde una de ellas tiene una clave ajena apuntando a la otra. La estructura de las tablas es la siguiente:
| Nombre | Tipo | Constraints | Descripción |
|---|---|---|---|
| pk_registry_id | INTEGER | Primary key | Identificador único de la tabla. |
| alias | TEXT | Not null | Nombre del usuario. |
| token | TEXT | Not null | Token de autenticación. |
| Nombre | Tipo | Constraints | Descripción |
|---|---|---|---|
| pk_fk_map_registry_id | INTEGER | Foreign key references Registry | Identificador único de la tabla. |
| uk_map_figura | INTEGER | Unique | ID en la figura del mapa. |
| row | INTEGER | Not null | Fila objetivo del dron. |
| column | INTEGER | Not null | Columna objetivo del dron. |
Todos los archivos utilizados, tienen disponibles con variables de entorno sus rutas dentro de volúmenes montados en docker o como rutas como archivos locales físicos, la manera habitual.
\newpage
El uso docker-compose ha supuesto una mejora enorme en la automatización. Sin embargo, no se entendería utilizar docker-compose sin variables de entorno. Las variables de entorno son ficheros text.env. De estos ficheros hay que extraer su contenido usando las librerías correspondientes de cada lenguaje de programación. En python dotenv-python o en Node dotenv.
Un ejemplo de la obtención de estos datos es la siguiente: En un archivo .env:
# SECURITY
ENCODING=utf-8
MAX_CONTENT_LENGTH=1024
# KAFKA
KAFKA_HOST=192.168.0.235
KAFKA_PORT=29092En un archivo python:
import dotenv
import os
dotenv.load_dotenv()
def getBrokerHost() -> str:
return os.getenv('KAFKA_HOST')
def getBrokerPort() -> str:
return os.getenv('KAFKA_PORT')Además, para levantar varios drones se han creado archivos Bash para porder crear muchos drones concurrentemente.
A continuación se muestra un script.
#!/bin/bash
# PARAMETROS:
NUM_INSTANCES=$1
FIRST_ID=$2
LAST_ID=$(($FIRST_ID + $NUM_INSTANCES - 1))
# Construir la imagen de docker
docker-compose build ad_drone
echo "Ejecutando $NUM_INSTANCES instancias de engine desde ID=$FIRST_ID hasta ID-$LAST_ID..."
# Bucle para crear y ejecutar las instancias en paralelo
for i in $(seq $FIRST_ID $LAST_ID); do
echo "id: $i"
CONTAINER_NAME="ad_drone__registry_id_$i"
docker-compose run --name $CONTAINER_NAME --rm ad_drone python app/src/runnerRegistry.py create $i drone-$i &
if [ $? -ne 0 ]; then
echo "Error en la ejecucion de la instancia $i. Parando el script..."
exit 1
fi
done
echo "Esperando a que terminen las instancias..."
exit 0 Se ejecuta tal que:
./run-engine-instances.sh <NUM_INSTANCES> <FIRST_DRONE_ID>Con la siguiente instrucción se ejecutarán 20 instancias. Desde el dron con id 400 al 419:
./run-engine-instances.sh 20 400
Este script solo es válido en ordenadores Unix, pero no Windows.
Para mantener, desarrollar y publicar el código fuente de este proyecto, se ha utilizado el sistema de control de versiones \textit{Git} junto con \textit{Github}. El repositorio está publicado, aunque de manera privada, en el siguiente repositorio.
A continuación se presenta cómo se levantan todos los servicios utilizados, junto con capturas que demuestran que cada módulo funciona.
Se recomienda la ejecución de toda la aplicación en el orden que se presenta a continuación, aunque como es un sistema distribuido, funcionará igualmente sin importar el orden de ejecución.
Desde la carpeta docker-kafka/AD_Kafka se ha utilizado un docker-compose para levantar Apache Kafa.
docker-compose up --buildSe para Kafka y Zookeeper o bien, parando individualmente sus contenedores de docker (docker stop id) o bien ejecutando "Ctrl + C" desde su terminal, la opción más sencilla.
Desde la carpeta AD_Weather se ha utilizado un docker-compose para levantar AD_Weather.
docker-compose up --buildDesde la carpeta AD_Registry se ha utilizado un docker-compose para levantar AD_Registry.
docker-compose up --buildEn este módulo se podrá consultar el estado de la base de datos.
Desde la carpeta AD_Database se ejecuta:
Este módulo no está dockerizado.
npm installConsultar todos los scripts:
npm run startMostrar base de datos:
node src/display-data.jsEl nombre del script es suficientemente descriptivo para saber utilizarlo.
Se mostrarán las casillas vacías, en verde o en rojo, según su estado. Se conectará mediante conexión HTTP, usando GET para conectarse con el puerto de \textitAD_Engine disponible.
Desde la carpeta \textit /AD_Frontend se ha utilizado un docker-compose para levantar AD_Frontend.
docker-compose up --buildA continuación en el puerto 3000 http:localhost:3000 se podrá visualizar la página web creada con React.js.
Cabe recalcar que realizar dos \textitexpose de este módulo. Uno para montar el servidor por sockets y otro para levantar el puerto HTTP con express.
Se marcan con caracteres ASCII es estado de la casilla, según los drones de la misma estén en una posición correcta, o no.
Se pueden editar múltiples opciones en el archivo de entorno .env. Ya sea las IP de los otros servicios o flags del propio AD_Engine.
docker-compouse up --buildDesde la carpeta AD_Drone se utilizan diferentes comandos.
- Registrar drones, si no están registrados. Crea 20 drones nuevos en la base de datos. Guarda en un fichero todas las credenciales.
./run-registry-instances 20 400 &- Levantar múltiples instancias (recomendado).
Levanta 20 drones desde el id 400 hasta el id 419.
./run-engine-instances 20 400 &- Matar múltiples instancias. Para N instancias según su id.
./run-engine-remove-instances 20 400 &- Construir y ejecutar individualmente los drones.
docker-compose build ad_droneDespués usaremos docker run usando el README.md del módulo AD_Drone.
Para ser una práctica desarrollada en tan solo seis semanas ha supuesto un gran aprendizaje en la automatización de procesos, uso de docker, de Apache Kafka. Incluso de python, JavaScript, TypeScript y Bash. Aunque también de aspectos concretos de redes de computadores.
Por su puesto docker es una herramienta para cualquier ingeniero especificado en los sistemas distribuidos. Sin embargo, también cabe recalcar que lenguajes modernos como JavaScript o Python tienen gestores de paquete que simplifican la gestión de las versiones. Ya sea npm para Node o pip y los entornos virtuales (.vev) para python.