Implementación eficaz de imágenes de Docker en escenarios de conectividad con un ancho de banda bajo

Azure Container Registry
Azure Functions
Azure IoT Edge
Azure IoT Hub
Azure Pipelines

En este artículo se describe una solución para implementar módulos perimetrales de Internet de las cosas (IoT) en contenedores en conexiones de Internet intermitentes o de bajo ancho de banda.

El procesamiento perimetral es un patrón clave de Internet de las cosas (IoT) para proporcionar conectividad de baja latencia y conservar el ancho de banda, como en escenarios móviles. Los sistemas de IoT normalmente aprovisionan los dispositivos perimetrales mediante la implementación de imágenes de contenedor de software. Las implementaciones de contenedores interrumpidas a través de conexiones intermitentes de Internet de ancho de banda bajo pueden provocar errores en escenarios móviles. Los escenarios de IoT que tienen un ancho de banda limitado, intermitente o bajo necesitan capacidades de implementación confiable y resistente.

En este ejemplo, una gran empresa logística quería mejorar su seguimiento de envíos de productos en todo el mundo. La empresa envió mercancías con diversos métodos de transporte terrestre, aéreo y marítimo a muchos lugares, incluidas zonas con una conectividad a Internet intermitente y de bajo ancho de banda. Dependiendo del tipo de mercancía, los envíos de productos llevaban instalados diversos dispositivos de seguro, protección o seguimiento de IoT, con diferentes capacidades. Los dispositivos incluían rastreadores GPS, sensores de temperatura y herramientas de captura de datos.

La plataforma tenía problemas para actualizar sus dispositivos a través de la plataforma de Azure IoT Edge desarrollada recientemente. Los principales puntos débiles eran:

  • Consumo elevado de ancho de banda al implementar software actualizado en dispositivos.
  • Ausencia de implementación automatizada estandarizada en todos los dispositivos.
  • Flexibilidad limitada en la selección de tecnología.

Para solucionar estos problemas, el equipo de desarrollo creó una solución que:

  • Minimiza el tamaño de la implementación en cada dispositivo, lo que reduce el ancho de banda.
  • Aplica una implementación estandarizada de contenedores de Docker desde la plataforma IoT Edge a dispositivos IoT remotos heterogéneos.
  • Habilita la supervisión de implementación confiable.
  • Aprovecha varios servicios de Azure DevOps y en la nube, y utiliza las herramientas heredadas preferidas por el cliente.

La solución ha aumentado considerablemente la confiabilidad y la resistencia del proceso de aprovisionamiento de dispositivos en entornos con conectividad limitada. En este artículo se describen los detalles de la solución y el proceso de evaluación de las opciones de la solución.

Requisitos del cliente

El cliente tenía los siguientes requisitos:

  • La solución debe admitir una conectividad intermitente de bajo ancho de banda en la nube.
  • Las aplicaciones implementadas deben seguir ejecutándose localmente.
  • El personal local necesita utilizar la funcionalidad sin conexión o sin un retraso de ida y vuelta a la nube.
  • Cuando se conecta, la solución debe usar la conexión en la nube de forma eficaz.
  • La solución debe priorizar el envío de datos según las reglas de negocio definidas de forma coherente en todos los productos.

También se han descrito los siguientes requisitos detallados:

  • Los archivos de imagen se transfieren a través de una conexión satélite de conectividad intermitente de ancho de banda bajo.
  • La cantidad de datos que se transfieren debe minimizarse.
  • La transferencia de archivos a dispositivos usa la aplicación de terceros preferida del cliente.
  • Las cargas de trabajo de dispositivo usan imágenes de Docker en IoT Edge.
  • El tamaño de las imágenes oscila entre decenas de MB y varios GB.
  • Los módulos de IoT Edge se escriben en .NET Core 2.2.

Posibles casos de uso

Esta solución es adecuada para escenarios de IoT en los que los contenedores de software ofrecen soluciones a través de conexiones intermitentes y de ancho de banda bajo. Algunos ejemplos son los siguientes:

  • Supervisión remota de petróleo, gas y minería
  • Actualizaciones de automoción por vía inalámbrica
  • En cualquier lugar en el que no se garantice una conexión segura.

Arquitectura

En escenarios de ancho de banda alto, Azure IoT Edge extrae imágenes directamente de un registro de Docker accesible desde Internet, ya sea un centro de Docker o uno privado como Azure Container Registry. Esta funcionalidad es la misma que la de ejecutar el comando docker pull <image_name>.

Con un acceso de red potencialmente intermitente, como una conexión a Internet satélite, el método Docker pull deja de ser confiable. El progreso no se almacena en caché si la conexión a Internet se interrumpe mientras Docker extrae la imagen. Cuando se reanuda la conexión a Internet, Docker debe empezar a extraer la imagen de nuevo desde el principio.

La solución usa un mecanismo de implementación alternativo, la aplicación de revisiones binarias de archivos de imagen de Docker, para reducir el ancho de banda y compensar la conectividad intermitente.

Diagrama que muestra la arquitectura de soluciones de alto nivel de Azure y Azure DevOps.

Flujo de datos

  1. Los desarrolladores interactúan con el código fuente del módulo perimetral en un repositorio de código fuente.
  2. Container Registry almacena las imágenes de Docker de cada módulo.
  3. El repositorio de manifiestos contiene los manifiestos de implementación para todas las series de tareas.
  4. Cada módulo tiene una canalización de compilación de Azure Pipelines que usa una compilación genérica de Docker para crear y registrar módulos automáticamente.
  5. La canalización de imagen a dispositivo implementa las imágenes de Docker en los dispositivos de destino, tal y como se define en el archivo de manifiesto.
  6. La canalización de manifiesto a dispositivo inserta el manifiesto de implementación en la instancia adecuada de Azure IoT Hub para el dispositivo que se está actualizando.
  7. Una solución de transferencia rápida de archivos de terceros transfiere los archivos de una cuenta de Azure Storage al dispositivo.
  8. El módulo de reconstrucción de imágenes de IoT Edge aplica las revisiones recibidas en los dispositivos.
  9. IoT Hub recibe mensajes de estado del módulo de reconstrucción de imágenes y establece el manifiesto de implementación del dispositivo. El resto del flujo de canalización usa este manifiesto de implementación.
  10. Azure Functions supervisa el flujo de mensajes de IoT Hub, actualiza la base de datos SQL y notifica al usuario si se ha realizado correctamente o no.
  11. Azure SQL Database realiza un seguimiento de las incidencias en los dispositivos de destino y en los servicios basados en Azure, durante y después de la implementación.

Componentes

  • Azure IoT Edge ejecuta cargas de trabajo en contenedores en los dispositivos, proporcionando una conectividad de baja latencia y conservando el ancho de banda.
  • Azure IoT Hub es un servicio administrado hospedado en la nube que actúa como centro de mensajes entre aplicaciones de IoT y los dispositivos IoT que controlan.
  • Azure Container Registry es un servicio de registro privado basado en la nube para almacenar y administrar imágenes de contenedor de Docker privadas y artefactos relacionados.
  • Azure Pipelines combina la integración continua (CI) y la entrega continua (CD) para probar y compilar automáticamente el código y enviarlo a cualquier destino.
  • Azure Functions es una plataforma de proceso sin servidor que permite ejecutar código desencadenado por eventos sin tener que aprovisionar ni administrar la infraestructura.
  • Azure Storage proporciona almacenamiento altamente escalable, seguro, eficaz y rentable para todos los tipos de datos, objetos y archivos empresariales.
  • Azure SQL Database es un servicio de base de datos relacional multimodelo y totalmente administrado creado para la nube.
  • Docker es una plataforma abierta para desarrollar, enviar y ejecutar aplicaciones en contenedores.

Alternativas

El equipo de desarrollo evaluó varias opciones antes de decidir sobre la solución completa de transferencia diferencial de imágenes de Docker. En las secciones siguientes se describen las alternativas y los resultados de la evaluación.

El equipo consideró los siguientes criterios de evaluación para cada opción:

  • Si la solución cumple o no los requisitos.
  • Si es necesario implementar una lógica baja, media o alta en los dispositivos.
  • Si es necesario implementar una lógica baja, media o alta en Azure.
  • Eficiencia del ancho de banda, o proporción de los datos transferidos al tamaño total de la imagen, para transferir una imagen de contenedor.

La eficacia del ancho de banda incluía escenarios en los que:

  • No existía ninguna imagen en el dispositivo.
  • Existía una imagen con la misma base en el dispositivo.
  • Existía una imagen de una versión de aplicación anterior en el dispositivo.
  • Existía una imagen para la aplicación compilada en una imagen base anterior en el dispositivo.

El equipo usó los siguientes escenarios para evaluar la eficacia del ancho de banda:

Escenario Descripción
Transferencia de la imagen con la capa base ya en el dispositivo Transferir una nueva imagen cuando otra imagen que ya está en el dispositivo comparte la imagen base. Este escenario representa la implementación de una nueva aplicación por primera vez, mientras que existe otra aplicación en el mismo sistema operativo y marco.
Actualización de la capa de la aplicación Cambiar solo el código de la imagen de una aplicación existente. Este escenario representa un cambio típico cuando un usuario confirma una nueva característica.
Actualización de la imagen base Cambiar la versión de la imagen base en la que se basa la aplicación.

Opción de transferencia de capas de Docker

Una imagen de contenedor de Docker es un montaje UnionFS de diferencias en el sistema de archivos de solo lectura, con otra capa grabable para los cambios realizados mientras se ejecuta el contenedor. Los sistemas de archivos se denominan capas, que son básicamente carpetas y archivos. Las capas se apilan para formar la base del sistema de archivos raíz de un contenedor. Puesto que las capas son de solo lectura, varias imágenes pueden compartir la misma capa si la tienen en común.

La opción de transferencia de capas de Docker reutiliza las capas entre imágenes y transfiere solo nuevas capas al dispositivo. Esta opción sería más útil para las imágenes que comparten la misma capa base, normalmente el sistema operativo, o para actualizar las versiones de las imágenes existentes.

Los inconvenientes de este método son:

  • El orquestador debe mantener información sobre qué capas existen en qué dispositivos.
  • Los cambios en la capa base provocarán cambios en todos los códigos hash de las capas posteriores.
  • La comparación requiere hashes de capa coherentes.
  • Podría haber dependencias en Docker save y Docker load.

Opción de modificación del cliente de Docker

Esta opción se centra en modificar o ajustar el cliente de Docker para que reanude la descarga de capas después de una interrupción. De forma predeterminada, Docker pull reanuda una descarga si la conexión a Internet se restaura en un plazo de 30 minutos a partir de la interrupción. De lo contrario, el cliente se cierra y se pierde todo el progreso de la descarga.

Este método es viable, pero tiene complicaciones, entre las que se incluyen:

  • Todas las imágenes del dispositivo tienen que registrarse con el demonio de Docker que extrae las imágenes para maximizar la eficacia del ancho de banda.
  • El proyecto de código abierto de Docker debería modificarse para admitir esta funcionalidad, lo que presenta un riesgo de rechazo por los responsables de mantener el código abierto.
  • La transferencia de datos a través de HTTP en lugar de mediante la solución de transferencia rápida de archivos elegida por el cliente requeriría el desarrollo de lógica de reintento personalizada.
  • Todas las capas deben retransmitirse al cambiar una imagen base.

Opción de compilación en el dispositivo perimetral

Este enfoque mueve el entorno de compilación de imagen a los dispositivos. Se envían los datos siguientes al dispositivo:

  • Código fuente de la aplicación que se está compilando
  • Copia de todos los paquetes NuGet de los que depende el código
  • Las imágenes base de Docker para el entorno de compilación y el entorno de ejecución de .NET Core
  • Metadatos acerca de la imagen final

A continuación, un agente de compilación en el dispositivo compila la imagen y la registra con el administrador de dispositivos de Docker.

Esta solución se rechazó porque:

  • Seguiría siendo necesaria una manera de mover imágenes de Docker de gran tamaño al dispositivo. Las imágenes para compilar aplicaciones .NET son más grandes que las propias imágenes de la aplicación.
  • Este método solo funciona para las aplicaciones en las que el equipo tiene el código fuente, por lo que no puede usar imágenes de terceros.
  • La opción requiere empaquetar paquetes NuGet y realizar el seguimiento de su movimiento a los dispositivos.
  • Si una imagen no se compila en el dispositivo, el equipo tendría que depurar de forma remota el entorno de compilación y la imagen creada. La depuración remota requeriría un uso elevado de la conexión a Internet potencialmente limitada.

Opción de transferencia diferencial de imagen completa

El enfoque elegido trata una imagen de Docker como un único archivo binario. El comando de Docker save exporta la imagen como un archivo .tar. La solución exporta las imágenes existentes y nuevas de Docker y calcula la diferencia binaria que, cuando se aplica, transforma la imagen existente en la nueva.

La solución realiza un seguimiento de las imágenes de Docker existentes en los dispositivos y compila revisiones diferenciales binarias para transformar las imágenes existentes en las nuevas imágenes. El sistema transfiere solo las revisiones diferenciales a través de la conexión a Internet de ancho de banda bajo. Esta solución requería cierta lógica personalizada para compilar los parches binarios, pero enviaba la menor cantidad de datos a los dispositivos.

Evaluation results

En la tabla siguiente se muestran cada una de las soluciones anteriores medidas con respecto a los criterios de evaluación.

Cumple los requisitos Lógica del dispositivo Lógica de Azure Transporte Primera imagen Base en el dispositivo Actualización de la capa de aplicación Actualización de la capa base
Transferencia de capas de Docker Bajo Media FileCatalyst 100% 10,5 % 22,4 % 100%
Modificación del cliente de Docker Media Bajo HTTP 100% 10,5 % 22,4 % 100%
Compilación en el dispositivo perimetral No Alto Media FileCatalyst N/D N/D N/D N/D
Transferencia diferencial de imagen completa Bajo Alto FileCatalyst 100% 3,2 % 0,01 % 16,1 %

Consideraciones

Estas consideraciones constituyen los pilares del Marco de buena arquitectura de Azure, un conjunto de principios rectores que puede usar para mejorar la calidad de una carga de trabajo. Para más información, consulte Marco de buena arquitectura de Microsoft Azure.

Eficiencia del rendimiento

Esta solución redujo drásticamente el ancho de banda consumido por las actualizaciones de los dispositivos IoT. En las tablas siguientes se muestra un desglose de las diferencias en la eficiencia de la transferencia.

Reconstrucción de imágenes como origen:

Nombre de la imagen Tamaño de las imágenes Tamaño de la revisión Reducción de datos
Visualización de datos 228 MB 79.6 MB 65,1 %
WCD simulado 188 MB 1,5 MB 99,2 %
Proxy 258 MB 29,9 MB 88,4 %

Versión anterior como origen:

Nombre de la imagen Tamaño de las imágenes Tamaño de la revisión Reducción de datos
Visualización de datos 228 MB 0,01 MB 99,9%
WCD simulado 188 MB 0,5 MB 99,7 %
Proxy 258 MB 0,04 MB 99,9%

Excelencia operativa

En las siguientes secciones se ofrece un recorrido detallado de la solución.

Repositorio de código fuente

Los desarrolladores interactúan con el código fuente del módulo perimetral en un repositorio de código fuente. El repositorio consta de carpetas que contienen el código de cada módulo, como se muestra a continuación:


\- repository root
    - modulea
    - modulea.csproj
    - module.json
    - Program.cs
    - Dockerfile

\- moduleb
    - moduleb.csproj
    - module.json
    - Program.cs
    - Dockerfile

El número recomendado de repositorios de código fuente es:

  • Un repositorio para todos los módulos desarrollados en todas las series de tareas.
  • Un repositorio de código fuente para cada serie de tareas.

Instancias de Container Registry

Container Registry almacena las imágenes de Docker de cada módulo. Hay dos configuraciones posibles para Container Registry:

  • Una única instancia de Container Registry que almacena todas las imágenes.
  • Dos instancias de Container Registry: una para almacenar las imágenes de desarrollo, pruebas y depuración, y otra que contenga solo imágenes marcadas como listas para producción.

Repositorio de manifiestos

El repositorio de manifiestos contiene los manifiestos de implementación para todas las series de tareas. Las plantillas están en carpetas basadas en su serie de tareas. En este ejemplo, las dos series de tareas son la infraestructura compartida y la aplicación contenedorizada.


\- repository root
     - Workstream1
         - deployment.template.json
     - Workstream2
         - deployment.template.json

Canalización de compilación de imágenes de Docker

Cada módulo tiene una canalización de compilación de Azure Pipelines. La canalización usa una compilación genérica de Docker para crear y registrar módulos. La canalización es responsable de:

  • Examen de seguridad del código fuente.
  • Examen de seguridad de la imagen base para compilar la imagen de Docker.
  • Ejecución de pruebas unitarias para el módulo.
  • Compilación del origen en una imagen de Docker. La etiqueta de imagen contiene BUILD_BUILDID para que la imagen siempre se pueda vincular al código fuente que la creó.
  • Inserción de la imagen en una instancia de Container Registry.
  • Creación del archivo delta.
  • Creación de un archivo de firma para la imagen y almacenamiento de dicho archivo en una cuenta de Azure Storage.

Todas las instancias de la canalización se basan en una única definición de canalización de YAML. La canalización puede actuar en los módulos mediante variables de entorno. Los filtros desencadenan cada canalización solo cuando se confirman cambios en una carpeta determinada. Este filtro evita la compilación de todos los módulos cuando solo se actualiza uno de ellos.

Canalización de imagen a dispositivo

La canalización de imagen a dispositivo implementa las imágenes de Docker en los dispositivos de destino, tal y como se define en un archivo de manifiesto. Al desencadenar la canalización, se inicia manualmente la implementación.

La definición de canalización especifica la ejecución de estas implementaciones en un contenedor. Las canalizaciones admiten la entrada de variable para las imágenes en las que se basen los contenedores. Una sola variable puede controlar las implementaciones de todas las canalizaciones.

Esta imagen contiene el código que determina qué revisiones se compilan, compila esas revisiones y las distribuye al lado de Azure de la herramienta de transferencia de archivos.

La herramienta de distribución de imágenes necesita la información siguiente:

  • Imágenes que se implementarán (proporcionadas por el manifiesto en el repositorio).
  • Dispositivos en los que realizará la implementación (proporcionados por el usuario que desencadena la canalización).
  • Imágenes que ya están en los dispositivos de destino (proporcionadas por una base de datos SQL en Azure).

Las salidas de la canalización son:

  • Agrupaciones de revisiones que se envían al lado de Azure de la herramienta de transferencia de archivos para distribuirse a los dispositivos.
  • Entradas de la base de datos SQL que marcan qué imágenes han empezado a transferirse a cada dispositivo.
  • Entradas de la base de datos SQL para los nuevos conjuntos de implementación. Estas entradas incluyen el nombre y la dirección de correo electrónico del usuario que ordenó la implementación.

La canalización realiza los pasos siguientes:

  1. Determina las imágenes necesarias, en función del manifiesto de implementación.
  2. Consulta SQL para ver qué imágenes ya están en los dispositivos. Si todas las imágenes ya están presentes, la canalización finaliza correctamente.
  3. Determina qué agrupaciones de revisiones se van a crear. El algoritmo que determina la imagen inicial que generará la agrupación de revisiones más pequeña.
    • Entradas: archivo .tar que contiene la nueva imagen para implementar y archivos de firma para las imágenes existentes en los dispositivos.
    • Salida: clasificación de las imágenes existentes para determinar la revisión más pequeña que se creará.
  4. Crea los conjuntos de revisiones necesarios para cada dispositivo. Compila revisiones similares una vez y las copia en todos los dispositivos que las necesitan.
  5. Distribuye las revisiones a la cuenta de almacenamiento de la herramienta de transferencia de archivos para la implementación.
  6. Actualiza SQL para marcar las nuevas imágenes como in transit en cada uno de los dispositivos de destino.
  7. Agrega la información del conjunto de implementación a SQL, con el correo electrónico de contacto de la persona que implementa la imagen.

Diagrama que muestra el archivo original para cambiar el archivo al flujo de trabajo de datos resultante.

Canalización de manifiesto a dispositivo

La canalización de manifiesto a dispositivo inserta el manifiesto de implementación en la conexión adecuada de IoT Hub para el dispositivo que se está actualizando. Un usuario desencadena la canalización manualmente y especifica una variable de entorno para la instancia de IoT Hub de destino.

La canalización:

  • Determina qué imágenes son necesarias para la implementación.
  • Consulta SQL para asegurarse de que todas las imágenes necesarias están en los dispositivos de destino. Si no es así, la canalización termina con un estado failed.
  • Inserta el nuevo manifiesto de implementación en el centro de IoT Hub adecuado.

Solución de transferencia rápida de archivos

El cliente quería seguir usando su solución de transferencia rápida de archivos de terceros, denominada FileCatalyst, para proporcionar la conexión entre Azure y sus dispositivos IoT. Esta solución es una herramienta de transferencia de archivos con coherencia final, lo que significa que una transferencia puede tardar mucho tiempo, pero finalmente se completará sin perder ninguna información de archivo.

La solución usó una cuenta de Azure Storage en el lado de Azure de la conexión y la máquina virtual del host de transferencia de archivos existente del cliente para cada dispositivo que recibía imágenes. Los conjuntos de revisiones se transfieren a una máquina virtual Linux que ejecuta IoT Hub.

Módulo de reconstrucción de imágenes

El módulo de reconstrucción de imágenes de IoT Edge aplica las revisiones recibidas en los dispositivos. Cada dispositivo hospeda su propio registro de contenedor local mediante el registro de código abierto de Docker. El proceso de reconstrucción de imágenes se ejecuta en la máquina virtual host, que es la misma que la máquina virtual de transferencia de archivos.

El módulo:

  1. Recibe el paquete de revisiones en una carpeta montada en el contenedor.
  2. Descomprime el contenido de la revisión para leer el archivo de configuración.
  3. Extrae la imagen base del registro de contenedor local (por hash).
  4. Guarda la imagen base como un archivo .tar.
  5. Aplica la revisión a la imagen base.
  6. Carga el nuevo archivo .tar que contiene la nueva imagen en Docker.
  7. Inserta la nueva imagen en el registro de contenedor local, con un archivo de configuración que incluye un nombre descriptivo y una etiqueta.
  8. Envía un mensaje de operación finalizada correctamente a IoT Hub.

Si se produce un error en el proceso en cualquier momento, el módulo envía un mensaje de error a IoT Hub para que se pueda informar al usuario que ordenó la implementación.

IoT Hub

Varios de los procesos de implementación usan IoT Hub. Además de recibir mensajes de estado del módulo de reconstrucción de imágenes, IoT Hub establece el manifiesto de implementación para el dispositivo. El resto del flujo de canalización usa este manifiesto.

Diagrama que muestra la revisión del dispositivo IoT y del Centro de operaciones en el flujo de trabajo de Reconstrucción de imágenes.

Azure Functions

Azure Functions supervisa el flujo de mensajes procedente de IoT Hub y realiza acciones en la nube.

Para un mensaje de operación correcta:

  • La función actualiza el estado de la entrada de SQL de la imagen en el dispositivo de in transit a succeeded.
  • Si esta es la última imagen que llega a un conjunto de implementación:
    • La función notifica al usuario que la implementación se ha realizado correctamente.
    • La función actualiza la canalización de manifiesto a dispositivo para empezar a usar las nuevas imágenes.

Para un mensaje de operación no completada correctamente:

  • La función actualiza el estado de la entrada de SQL de la imagen en el dispositivo de in transit a failed.
  • La función notifica al usuario el error de transferencia de imágenes.

Flujo de trabajo de mensajes del reconstructor de imágenes de dispositivos IoT y de centro de operaciones

SQL Database

Una base de datos SQL realiza un seguimiento de las incidencias en los dispositivos de destino y en los servicios basados en Azure, durante y después de la implementación. Tanto Azure Functions como Azure Pipelines utilizan un paquete de NuGet privado creado para interactuar con la base de datos.

SQL Database almacena los datos siguientes:

  • Las imágenes que hay en cada dispositivo.
  • Las imágenes que se van a incluir en cada dispositivo.
  • Las imágenes que se implementan que pertenecen a un conjunto.
  • Usuario que ordenó las implementaciones.

El objetivo de este ejemplo era asegurarse de que el sistema generó los datos necesarios para futuros paneles de datos. La consulta a IoT Hub puede proporcionar los siguientes datos sobre la canalización de manifiesto a dispositivo:

  • El estado de una implementación.
  • Las imágenes de un dispositivo determinado.
  • Los dispositivos que tienen una imagen.
  • Los datos de serie temporal sobre transferencias correctas y con errores.
  • Las consultas de implementaciones basadas en el usuario.

Colaboradores

Microsoft mantiene este artículo. Originalmente lo escribieron los siguientes colaboradores.

Autor principal:

Pasos siguientes