Conecta con nosotros

Opinión

Service Mesh is the New Black

Publicado el

Los que vivimos inmersos en el mundo de la tecnología estamos acostumbrados al continuo oleaje de nuevos términos y tecnologías, que en muchas ocasiones son sólo ruido y marketing. Afortunadamente, de vez en cuando el oleaje arroja algún tesoro. En este caso, nos ha dejado en la orilla el Service Mesh.

Hace tiempo que se constató que las arquitecturas monolíticas eran un problema de cara a mejorar la mantenibilidad, escalabilidad y estabilidad de las aplicaciones, así como a facilitar las tareas de los equipos de desarrollo. Desde hace 20 años se ha evolucionado en la dirección de un mayor desacoplamiento y autonomía de los componentes de una aplicación, desde las arquitecturas SOA, pasando por los buses de mensajes, a los microservicios.

En los últimos años, la aparición de las tecnologías de contenedores ha acelerado esta evolución, dotando al ecosistema de desarrollo y explotación de infraestructuras y herramientas que facilitan y de alguna manera empujan la adopción de los microservicios como piezas de construcción de las aplicaciones.

Lo que antes era un bloque, ahora se ha convertido en decenas de pequeños componentes autónomos (los microservicios), con funcionalidades muy específicas. Utilizando una analogía simple, este patrón de diseño es análogo al empleado por el sistema operativo UNIX desde sus comienzos: en lugar de proporcionar complejos y pesados programas con decenas de funcionalidades, se dota al entorno de pequeñas utilidades altamente especializadas en el desempeño de una tarea muy concreta y simple, y de una serie de mecanismos de interconexión que permitan combinarlas para solucionar problemas más complejos.

Este patrón de diseño de sistemas operativos ha sido un éxito durante los últimos 50 años, por lo que no parece un mal ejemplo a tener en cuenta para guiar la arquitectura de las aplicaciones. La proliferación de las plataformas de contenedores, con Kubernetes a la cabeza, ha sembrado los centros de datos de miles de contenedores.

Ahora las aplicaciones constan de decenas o centenares de contenedores, cuyo patrón de comunicación entre ellos constituye su sistema nervioso, al igual que en un organismo vivo. Esta comunicación se realiza sobre la red existente, en la mayoría de los casos la red de un clúster Kubernetes. Y es aquí donde aparecen las primeras dificultades.

Si en el funcionamiento tradicional de una simple aplicación clásica, con sus típicas capas de frontend y backend, los flujos de tráfico están bien definidos y son fácilmente trazables, imaginemos lo que ocurre en una aplicación basada en decenas de microservicios, repartida entre varios sistemas físicos, o incluso en una infraestructura híbrida, en la que parte se encuentra en un centro de datos propietario y el resto en una nube pública.

El número de flujos de comunicación se dispara de forma geométrica a medida que crece el tamaño de la aplicación y hacer el seguimiento resulta una tarea inabarcable. Pensar en monitorizar esos flujos, o en resolver un problema funcional o de rendimiento pone los pelos de punta.

Además de hacer frente a este desafío, pronto se hizo patente que el modelo adolecía de una serie de funcionalidades que facilitarían enormemente el ciclo de vida de las aplicaciones, como:

  • Balanceo de carga: capacidad de repartir el tráfico entre varias instancias del mismo microservicio.
  • Encaminamiento inteligente: tomar decisiones de encaminamiento en base a políticas, basadas en periodos de tiempo, en el estado de otros servicios, en el tipo de tráfico o en el contenido de este, por ejemplo. Esta funcionalidad es básica para adoptar modelos de despliegue de tipo A/B, blue/green o canary.
  • Descubrimiento de servicios: teniendo en cuenta la complejidad que puede llegar a alcanzar una aplicación basada en microservicios, es muy conveniente disponer de un mecanismo de descubrimiento de servicios, de forma que un microservicio que tenga que comunicar con otro sepa dónde encontrarlo.
  • Resiliencia: facilidad de reencaminar el tráfico a un servicio de respaldo en caso de fallo en el principal.
  • Observabilidad: en el mundo de las aplicaciones monolíticas, las interacciones entre sus componentes se pueden rastrear utilizando herramientas de depuración y profiling. En el mundo de los microservicios, estas interacciones son flujos de comunicaciones a nivel de red, dinámicos, de elevada complejidad. Es deseable contar con la capacidad de monitorizar y analizar estas interacciones para, por ejemplo, realizar diagnóstico de problemas, optimizar rendimientos o hacer previsión de capacidad. 
  • Seguridad: Los datos entre los distintos microservicios deberían viajar cifrados, y ambos extremos validarse mutuamente mediante certificados digitales, ya que no se tiene control (desde la capa de aplicación) de las redes por la que los datos transitan. Además, sería conveniente poder gestionar los permisos, de forma que se impidan todos los flujos de comunicación no autorizados, mejorando la seguridad de la aplicación de forma considerable.

No parece razonable pedir a los equipos de desarrollo que implementen estas funcionalidades en los propios microservicios, fundamentalmente por el incremento considerable de tiempos y costes. Lo que parece tener más sentido es crear librerías que implementen estas funcionalidades, de forma que puedan ser incorporadas en las aplicaciones. Esta fue la primera aproximación (Stubby de Google, Hysterix de Netflix o Finagle de Twitter), aunque pronto se constató que el mantenimiento de estas librerías era algo muy complejo y costoso.

Por ejemplo, una motivación para el uso de microservicios es que cada uno de ellos puede emplear el lenguaje que el equipo de desarrollo encargado considere más adecuado, de forma independiente del resto de microservicios. Esta diversidad de entornos de desarrollo hay que trasladarla a estas librerías, obligando a los desarrolladores de estas a portar las mismas funcionalidades a decenas de lenguajes. Por otro lado, cuando se corrige una vulnerabilidad, o se soluciona un problema, es necesario reconstruir todos los microservicios, posiblemente en una nueva versión, y un nuevo despliegue de la aplicación.

Era razonable, por tanto, separar estas funcionalidades de los propios microservicios, que habrían de ser agnósticos a los detalles de implementación de los mismos. Esto se consigue mediante el empleo de un proxy local a cada microservicio, que gestiona las comunicaciones entrantes y salientes de este.

Desde el punto de vista del microservicio, su único interfaz con el mundo es este proxy, tanto si tiene que aceptar conexiones o si necesita comunicar con otro componente de la aplicación. Es este proxy el que se encarga de las tareas de balanceo, gestión de tráfico, seguridad, etc., de forma transparente para la aplicación. Empleando tecnología de contenedores, la implementación de estos proxies es independiente de la tecnología empleada en su microservicio asociado.

Esta red de proxies es, de facto, el plano de datos de la aplicación, que gestiona la comunicación entre todos sus componentes. De la configuración y supervisión de este plano de datos se ocupa el correspondiente plano de control. Ambos planos, datos y control, permiten establecer una malla de comunicaciones, que denominamos service mesh.  Ejemplos de implementaciones son Linkerd, Istio o Consul Connect.

Conceptualmente, a lo que se llega es a una red overlay sobre la infraestructura de red existente. Este tipo de redes nace como solución para satisfacer funcionalidades que la red sobre la que se apoya (underlay) carece. Algunos ejemplos de este tipo de redes son, por ejemplo:

  • La red Tor, que nace para garantizar el anonimato de los usuarios, algo que la red Internet no puede hacer de forma nativa.
  • Las redes VPN, que se desarrollan para proporcionar seguridad en forma de cifrado de comunicaciones y autenticación de pares.
  • La red CNI de Kubernetes, que proporciona una red plana entre contenedores independientemente de los servidores físicos que componen un clúster, como por ejemplo,  Weave, Flannel, o Calico.

Generalmente la aparición de un overlay inquieta bastante a los responsables de comunicaciones y seguridad de las organizaciones, puesto que escapan su control. Por ejemplo, una red overlay podría interconectar servicios que en el underlay estarían aislados por políticas de seguridad. También es habitual que, con el paso del tiempo, parte de esas funcionalidades que han motivado la creación del overlay terminen implementándose, de forma mucho más eficiente, en el underlay. Esto es, por ejemplo, lo que ha ocurrido con los overlays de Kubernetes y las SDN, como Cisco ACI.

La pregunta que muchas organizaciones se hacen ante este escenario es: ¿debo incorporar un service mesh a mi entorno y adaptar mis desarrollos para hacer uso de él?. La respuesta no es fácil. Los beneficios son evidentes, pero no hay que olvidar algunos inconvenientes a la hora de tomar la decisión:

  • Inmadurez: la tecnología para implementar service mesh es relativamente reciente, y algunas implementaciones aún tienen pocas horas de vuelo.
  • Preparación de los equipos: la curva de aprendizaje, tanto para los perfiles de desarrollo como de operaciones, es bastante inclinada.

En la mayoría de los casos, la mejor aproximación será la de un entorno híbrido, en el que convivan aplicaciones que puedan aprovechar las ventajas del service mesh  y las aplicaciones más tradicionales que no merece la pena migrar al nuevo esquema.  Con el paso del tiempo, el ratio de aplicaciones sobre el service mesh irá aumentando paulatinamente.

En los próximos años veremos como todas estas desventajas se dejan atrás y el service mesh se convierte en un elemento esencial en la arquitectura de las aplicaciones.

Firmado: José Sánchez Seco, DevOps Senior Architect. Systems/Cloud Area Manager en SATEC

El equipo de profesionales de MCPRO se encarga de publicar diariamente la información que interesa al sector profesional TI.

Lo más leído