Una de los últimos avances en el mundo de la virtualización son los unikernels. En este artículo voy a introducir brevemente el concepto de unikernel para luego enfocarme en Toro, que es un unikernel dedicado a microservicios (estilo arquitectónico donde la aplicación se descompone en un conjunto de pequeños servicios que se ejecutan de forma autónoma y se comunican vía llamadas a sus respectivas APIs).

Ejemplo de aplicación eCommerce desarrollada siguiendo la arquitectura de microservicios

Ejemplo de aplicación eCommerce desarrollada siguiendo la arquitectura de microservicios – Fuente https://microservices.io/patterns/microservices.html

Un unikernel es una aplicación que se compila junto a la parte del kernel del Sistema Operativo que necesita y que tiene como resultado un binario que incluye ambos. Este binario luego puede ser ejecutado tanto en baremetal o en una máquina virtual sin la necesidad de un sistema operativo intermediario (es decir, sin tener que ejecutar Linux, Windows o lo que sea por debajo), con los beneficios de rendimiento y seguridad que esto implica. Por ejemplo, se podria “unikernealizar” el servidor web Apache y asi tendriamos un host o máquina virtual dedicada a ejecutar un servidor web.

Comparación del stack tecnológico en una aplicación "tradicional" vs una aplicación como unikernel

Comparación del stack tecnológico en una aplicación “tradicional” vs una aplicación como unikernel.  Fuente

En general el proceso para generar un unikernel es muy costoso. Además, muchas veces, este desarrollo en general es limitado a una aplicación, e.g., Apache, con lo cual es difícil reutilizar código. Eso sí, una vez creado, las ventajas de los unikernels son muchas y variadas como apuntábamos antes:

  • Mejor seguridad: menos código qué atacar, aislación completa de otras aplicaciones (ya que no comparten ni las llamadas al sistema operativo).
  • Mejor rendimiento: el unikernel está optimizado para su función específica en lugar de tener que proporcionar servicios genéricos. No hay overhead en llamadas al sistema o en cambios de contexto o niveles de privilegio.
  • Limitación de recursos. El tamaño de un unikernel es órdenes de magnitud más pequeño que un sistema operativo normal. Por esta misma razón se arranca (boot) mucho más rápidamente.

La principal competencia de unikernels son los containers. Los containers son, a groso modo, virtualización por software. Los containers tienen varias ventajas frente a los unikernels como mejores tiempos de desarrollo, menos consumo de memoria y menos consumo de CPU. Claro, que estas ventajas son ciertas si miramos al container en sí. El container sigue dependiendo de un sistema operativo “normal” por debajo con lo que esta eficiencia dependerá mucho de como esté desplegado. Y debido a esta misma dependencia, su principal desventaja es que la aislación no es completa. En otras palabras, el “attack surface” del container es más grande que el de unikernels. Para toda aplicación que sea crítica (tanto a nivel de seguridad como de rendimiento) mejor optar por un unikernel. 

Hay bastantes proyectos que facilitan la creación de unikernels. Uno de ellos es el proyecto que yo lidero: Toro, un unikernel orientado a microservicios.

Toro presenta una simple interfaz para la aplicación de usuario donde en la definición del microservicio se indican qué librerias del sistema operativo proporcionadas por Toro se necesitan. Con esa información, el código del microservicio se compila con Toro y resulta en un binario que puede ejecutarse tanto en baremetal como en una máquina virtual. Esta imagen o binario es inmutable, esto quiere decir que la misma imagen puede usarse en diferentes hypervisors, simplificando el trabajo de IT para hacer despliegues de esas aplicaciones.

Diagrama de una Arqutiectura de unikernels para microservicios con Toro

Arqutiectura de unikernels para microservicios con Toro

Para especificar microservicios, Toro propone dos tipos de sockets: bloqueantes y no bloqueantes. Los bloqueantes son para microservicios que realizan IO intensivo. El modelo es similar al implementado en Apache, en el que por cada conexión crea un Thread. Los sockets no bloqueantes son para microservicios que pueden responder solicitudes sin bloquearse. El modelo implementado es el “Single Thread Event Loop” el cual es similar al de Nginx. En este modelo, cada microservicio se implementa como un único thread el cual hace el tratamiento de cada socket de manera secuencial. La principal ventaja de este modelo es que escala muy bien con el número de conexiones soportando varios miles de conexiones concurrentes.

Toro deja al programador elegir el modelo dependiendo de los requerimientos del microservicio a desarrollar. Una vez definida la aplicación esta se compila con el kernel y se genera un binario que puede ser utilizado para lanzar una máquina virtual . Por ejemplo, un microservicio que simplemente responde “Hello World!” pesa alrededor de 130kb y tarda en inicializar alrededor de 150ms.

Como resumen, en este artículo presenta muy brevemente el concepto de unikernel. En particular, me enfoqué en Toro el cual es un unikernel dedicado para miroservicios. A diferencia de otros unikernels, Toro provee una interfaz de usuario simplificada para el desarrollo de microservicios. Un vez desarrollada la aplicación, se obtiene un microservicio que se ejecuta en una máquina virtual sin sistema operativo intermediario. Para más documentación sobre Toro visite https://github.com/torokernel/.