IoT Edge: Desplegando cargas de trabajo «on-prem» desde IoT Hub
Hace unos días vine para hablaros sobre la calidad del código y como poder medir y cuantificarla con SonarCloud. Si bien es cierto que hacer las cosas lo mejor posible es uno de los puntos que más me gusta de trabajar en Plain Concepts, poder jugar con diferentes tecnologías y escenarios es el otro punto fuerte de mi trabajo.
Cada proyecto es un mundo y cada cliente tiene sus necesidades y requisitos, precisamente de uno de esos proyectos es del que vengo ha hablar hoy:
Uno de nuestros clientes está usando Smart Concepts, nuestra solución de IoT basada en Azure. Esta solución ingesta datos desde diferentes dispositivos dentro de una planta de procesado y utiliza para alimentar unos paneles de monitorización y unos sistemas de predicción basados en Inteligencia Artificial. Hasta aquí podríamos estar hablando de un escenario IoT bastante común hoy en día:
Una vez entrenados los modelos de predicción, estos se utilizaban para poder detectar cierto tipo de alarmas críticas para el negocio.
El problema
Con este sistema, el cliente es capaz de solucionar la gran mayoría de los escenarios simplemente adaptando un poco en caso de ser necesario el procesado de los datos ingeridos para hacer las transformaciones necesarias y entrenando sus nuevos modelos de predicción. El tema aquí (y sino no sería un problema), es qué una vez validado el sistema, el cliente ha querido ampliarlo a otras áreas de su negocio. Los datos que manejan estas otras áreas son de naturaleza más sensible a los originalmente gestionados por la plataforma. No es lo mismo hacer predicciones sobre cuándo se va a romper una máquina que sobre historiales médicos, por ejemplo.
Precisamente de lo delicado de los datos que se estaban procesando, viene la necesidad de que los datos se procesen directamente en la propia infraestructura física del cliente y no salgan a internet.
Esto deja la puerta abierta a diferentes soluciones con sus correspondientes retos. Desplegar algo «on-prem» obliga a plantearse ciertas cosas, como modelos de hospedaje (servidores existentes o nuevos), modelos de distribución (instaladores, scripts, docker), o simplemente métodos de actualización. Por ejemplo, esto último, la actualización, puede parecer un tema totalmente banal hasta que te das cuenta de que los modelos de predicción son iterativos y pueden requerir de reentrenarse con cierta frecuencia.
IoT Edge al rescate
Para quién no lo conozca , IoT Edge es un servicio que nos permite desplegar desde IoT Hub diferentes cargas de trabajo en los diferentes dispositivos conectados. Hablando en términos más mundanos, vamos a poder desplegar contenedores Docker en diferentes máquinas que previamente hemos configurado con el runtime de IoT Edge. Aunque este modelo no elimina el problema de hospedaje, elimina de un plumazo el de distribución y el de actualización, distribuiremos imágenes Docker y el runtime se encargará de aplicar las actualizaciones que hagamos sobre IoT Hub.
El runtime IoT Edge soporta tanto Windows como Linux , por lo que la cantidad de dispositivos donde se puede instalar es enorme y raro será el caso donde no se pueda emplear este sistema.
Con este nuevo planteamiento, hemos conseguido que nuestros modelos de predicción se encuentren dentro de la propia red del cliente, de modo que sus datos no viajan por internet ni salen de la misma, pero seguimos pudiendo controlar de manera remota y centralizada los modelos que se están utilizando.
Una vez más, talk is cheap, show me the code!
Instalando el runtime de IoT Edge
Lo primero que vamos a necesitar es cumplir con una serie de prerrequisitos simples:
- Tener un IoT Hub desplegado (como desplegar IoT Hub desde el portal o desde azure cli)
- Tener la extensión de IoT para Azure Cli (basta con ejecutar
az extension add --name azure-iot
)
Lo primero que vamos a hacer, es desde la PowerShell ejecutar el comando
. {Invoke-WebRequest -useb aka.ms/iotedge-win} | Invoke-Expression; ` Initialize-IoTEdge -ContainerOs Linux
Este comando va a instalar el runtime para contenedores Linux en un equipo Windows, si quisiéramos contenedores Windows, deberíamos cambiar el parámetro -ContainerOs
Una vez el proceso termine, nos va a pedir reiniciar. Basta con decirle que si para que el proceso continúe. Una vez reiniciado el equipo, desde una PowerShell nuevamente, vamos a ejecutar el comando:
. {Invoke-WebRequest -useb aka.ms/iotedge-win} | Invoke-Expression; Initialize-IoTEdge -ContainerOs Linux
Esto va a iniciar un proceso interactivo donde vamos a configurar el equipo como un dispositivo IoT Edge. Este proceso nos va a pedir la cadena de conexión del dispositivo. Para ello primero vamos a tener que crear un dispositivo IoT en IoT Hub y obtener su cadena de conexión. Esto lo vamos a poder conseguir ejecutando:
az iot hub device-identity create --device-id myEdgeDevice --edge-enabled --hub-name myIoTHub
az iot hub device-identity show-connection-string --device-id myEdgeDevice --hub-name myIoTHub
Con estos dos comandos, primero crearemos un dispositivo llamado myEdgeDevice en el IoT Hub myIoTHub con el flag de IoT Edge activado, y después simplemente obtenemos su cadena de conexión.
Una vez el proceso de inicialización finalice, ya tendremos nuestro dispositivo IoT Edge configurado. Si todo ha ido bien, deberíamos poder ejecutar:
iotedge list
y obtener un resultado como este
NAME STATUS DESCRIPTION CONFIG
edgeAgent running Up 45 seconds mcr.microsoft.com/azureiotedge-agent:1.0
Desplegando cargas de trabajo sobre IoT Edge
IoT Hub permite desplegar varias cargas de trabajo (módulos) que puedes descargar desde el marketplace, pero en este caso lo que queríamos nosotros era desplegar nuestros propios modelos que ya estaban en nuestro propio Azure Conteiner Registry (acr). Aunque esto se puede hacer también desde la interfaz gráfica, para este ejemplo voy a utilizar az cli ya que es lo que vemos a poder automatizar como parte de nuestros procesos de CI/CD.
Esto se hace un poco más tedioso ya que vamos a tener que generar un fichero json que con todo el contenido a desplegar y hacer los reemplazos de tokens que correspondan para adecuarnos a las versiones que queramos desplegar. De todos modos, para este ejemplo, vamos a hacer un despliegue sencillo de un simple nginx.
{
"modulesContent": {
"$edgeAgent": {
"properties.desired": {
"modules": {
"nginx": {
"settings": {
"image": "nginx",
"createOptions": "{\"ExposedPorts\":{\"80/tcp\":{}},\"HostConfig\":{\"PortBindings\":{\"80/tcp\":[{\"HostPort\":\"80\"}]}}}"
},
"env": {
"MyVar1": {
"value": "value1"
}
},
"type": "docker",
"version": "1.0",
"status": "running",
"restartPolicy": "always"
}
},
"runtime": {
"settings": {
"minDockerVersion": "v1.25",
"registryCredentials": {
"MyACR": {
"address": "MyACRUrl",
"password": "MyACRPassword",
"username": "MyACRUser"
}
}
},
"type": "docker"
},
"schemaVersion": "1.0",
"systemModules": {
"edgeAgent": {
"settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.0",
"createOptions": ""
},
"type": "docker"
},
"edgeHub": {
"settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.0",
"createOptions": "{\"HostConfig\":{\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
},
"type": "docker",
"status": "running",
"restartPolicy": "always"
}
}
}
},
"$edgeHub": {
"properties.desired": {
"routes": {
"route": "FROM /messages/* INTO $upstream"
},
"schemaVersion": "1.0",
"storeAndForwardConfiguration": {
"timeToLiveSecs": 7200
}
}
},
"nginx": {
"properties.desired": {}
}
}
}
Dentro de los módulos del agente, vamos a añadir una sección por cada imagen que queremos desplegar. Vamos a analizar en detalle el módulo:
"nginx": {
"settings": {
"image": "nginx",
"createOptions": "{\"ExposedPorts\":{\"80/tcp\":{}},\"HostConfig\":{\"PortBindings\":{\"80/tcp\":[{\"HostPort\":\"80\"}]}}}"
},
"env": {
"MyVar1": {
"value": "value1"
}
},
"type": "docker",
"version": "1.0",
"status": "running",
"restartPolicy": "always"
}
En la sección de settings, le estamos indicando la imagen y las opciones de creación, como bindings de puertos, volúmenes, o todo lo que necesitemos que se envíe a la API de Docker, a continuación, configuramos las diferentes variables de entorno que nos interese utilizar y por último, indicamos la política de reinicios que queremos que tenga (además de otros parámetros como el tipo o versión del módulo).
Para este ejemplo he utilizado la imagen de nginx de DockerHub, pero normalmente utilizamos un acr privado, por lo que en la sección runtime, vamos a configurar todos los registros de imágenes que necesitemos:
"runtime": {
"settings": {
"minDockerVersion": "v1.25",
"registryCredentials": {
"MyACR": {
"address": "MyACRUrl",
"password": "MyACRPassword",
"username": "MyACRUser"
}
}
},
"type": "docker"
}
Una vez que tenemos el json listo, basta con ejecutar:
az iot edge set-modules --device-id myEdgeDevice --hub-name MyIoTHubPlain --content deployment.json
indicandole el nombre del dispositivo IoT, el nombre de IoT Hub, y la ruta al json.
Si todo ha ido bien, deberíamos hacer iotedge list
y obtener algo como esto:
NAME STATUS DESCRIPTION CONFIG
edgeAgent running Up 38 minutes mcr.microsoft.com/azureiotedge-agent:1.0
edgeHub running Up 29 minutes mcr.microsoft.com/azureiotedge-hub:1.0
nginx running Up 1 second nginx:latest
Además, al haber desplegado un nginx, si abrimos en un navegador la dirección http://localhost, deberíamos encontrarnos con la página de bienvenida de nginx.
Conclusión
Los escenarios de IoT se han convertido en mucho más que el recoger métricas y enviarlas a algún sitio. Cada vez más queremos que nuestros dispositivos sean más y más inteligentes, funcionen mejor, se actualicen más frecuentemente, o simplemente que protejan mejor nuestros datos y nuestra privacidad.
IoT Edge nos permite cumplir con todos estos objetivos sin perder de vista el ciclo de vida de los productos y los métodos de despliegue iterativo, dándonos lo mejor de los dos mundos al poder controlar todo desde un punto cloud central, pero desplegar y ejecutar en puntos «on-prem» distribuidos.