Algunos conceptos en arquitrcturas de (¿micro?)servicios que se suelen confundir

Bueno, dejémoslo en arquitecturas distribuídas, que a veces hablamos muy (demasiado) alegremente de microservicios

La idea es hablar sobre algunos conceptos, patrones que se prestan a confusión porque muchas veces «se tocan» a nivel funcional y no queda claro si estamos uno u otro. Vamos a hablar de proxiesAPI GatewayService MeshBackend For Frontend.

Transparent (o Forward) Proxy, Reverse Proxy

Empecemos por el más sencillo: El transparent (o forward) proxy se limita a reenviar las peticiones que realiza un cliente hacia un servidor determinado. Se suele usar para «agrupar» las conexiones de varios clientes, es decir, para el servidor que sirve el recurso es como si la petición la realizara el transparent proxy y desconoce cuantos (y cuales) clientes hay al «otro lado». Uso típico: un proxy que agrupa la salida a Internet de toda una oficina.

El reverse proxy por otro lado lo que hace es «aislar» los servidores del cliente. Si con un proxy normal son los servidores que desconocen qué clientes hay, con un reverse proxy son los clientes que desconocen a los servidores.

Si expones varios servicios al exterior, tener que ofrecer una IP y/o puerto distinto para cada uno de ellos no suele ser algo deseable. Precisamente esta es la función del reverse proxy: enrutar las peticiones que vienen del exterior permitiendo ofrecer así un único punto de entrada (IP+puerto). De este modo si tienes dos servicios, uno es localizable usando http://my-server/service1 y el otro usando http://my-server/service2. Sin reverse proxy, o bien ambos servicios tienen su propia IP o bien (si están ambos en my-server) tienen puertos distintos.

Para tener proxies se suele confiar en productos de terceros que están especializados en ello como NGINX, Linkerd o Envoy por citar tres ejemplos.

Load balancer

Un balanceador de carga es un producto que se dedica, como su nombre indica, a balancear la carga entre distintos servidores que ofrecen el mismo servicio. De este modo para el cliente hay un único punto de entrada y redistribuyen las peticiones basándose en determinadas reglas.

Hay que distinguir entre balanceadores de nivel 7 (L7) o de nivel 4 (L4). Esto define a que nivel del modelo OSI trabajan. Así un balanceador L4 se limita a reenviar paquetes de transporte (p. ej. TCP) a un servidor en concreto basándose en las características de esos paquetes en el nivel 4. Es decir poco más que la IP y el puerto de destino. Por otro lado un balanceador L7 funciona a nivel de aplicación (p. ej. HTTP) lo que indica que pueden analizar paquetes a este nivel y tomar decisiones en base a elementos del protocolo de aplicación. P. ej. una decisión que podrían tomar es decidir a que subconjunto de servidores reenviar basándose en la URL de la petición.  Observa que eso último hace que nuestro balanceador de carga pueda actuar como reverse proxy.

De todos modos conviene aclarar que por lo general un reverse proxy va un poco más allá que simplemente balancear la carga, ya que puede interaccionar con el protocolo de aplicación y así ofrecer servicios adicionales tales como compresión, modificación de cabeceras, etc…

Sobre si es mejor usar un balanceador de carga L4 o L7 hay opiniones para todos los gustos y yo creo que, como todo en este mundo, depende de tus casos concretos. Una opinión muy generalizada en contra de usar L7 es que se suele usar para tener sticky sessions (eso es, garantizar que un mismo cliente es enrutado siempre al mismo servidor). En efecto sticky sessions es una práctica a evitar porque limita el escalado de tu aplicación y la hace más sensible a fallos. Pero sticky sessions no es el único uso posible para un balanceador L7.

Por si os pica la curiosidad, Envoy es L7 y NGINX puede funcionar como L4 o L7.

API Gateway

Aquí abrimos ya la caja de pandora. Porque si definimos «API Gateway» a grosso modo podríamos decir que es «un servicio que habla con servicios internos y ofrece funcionalidades adicionales». Pero es que esta misma definición nos sirve exactamente así para definir un reverse proxy.

Aquí hay que distinguir entre «API Gateways genéricas» y «API Gateways personalizadas». Las primeras son productos tipo Kong o Azure API Management. Esos productos ofrecen servicios genéricos tales como monitorización, logging, autenticación centralizada, modificación de claims, cuotas de llamadas, cache, etc

Algunos de esos servicios (p. ej. cachelogging) se pueden ofrecer también desde reverse proxies tipo NGINX, de ahí que sea posible montar API Gateways genéricas usando un reverse proxy. Observa que conceptualmente tanto el reverse proxy como el API Gateway «hacen lo mismo»: reciben una petición del exterior y la mandan a un servicio interno y ofrecen servicios adicionales. Hay pues, una fina línea entre que consideramos reverse proxy y API Gateway genérica. En general una API Gateway ofrece servicios «más avanzados» o «más de aplicación». Por supuesto muchos productos que son API Gateways implementan también un reverse proxy, pero también puedes tenerlos explícitamente separados en dos productos distintos (p. ej. tener un Kong detrás de un NGINX). Eso ya dependerá de tus necesidades.

Otra cosa habitual en las API Gateways es que a partir de una petición de un cliente, se llame a más de un servicio distinto y se combine la respuesta. El objetivo es que tu puedes tener varios servicios internos, pero no quieres ofrecerlos todos a los clientes (por ejemplo no quieres que tu cliente dependa de tu topología lógica de servicios). Así puedes crear una (o varias) API Gateways que ofrezcan otra interfaz al cliente. De este modo el cliente interacciona con el API Gateway: hace una petición y el API Gateway realiza internamente N peticiones a distintos servicios y combina los resultados y los manda al cliente. Este otro tipo de API Gateways, que yo llamo «personalizadas», si que se distinguen de un reverse proxy ya que tienen inteligencia del negocio: deben saber qué servicios internos llamar y como combinar las respuestas.

Backend for Frontend (BFF)

BFF es un patrón que consiste en ofrecer API Gateways específicas por el tipo de cliente. La idea es que tenemos nuestros servicios internos que desconocen por completo sus posibles clientes. La topología de esos servicios internos depende de otros aspectos, pero no de qué clientes interaccionarán con ellos. Luego por cada cliente (frontend) se crea una API Gateway (Backend). Este Backend está creado ad-hoc por el cliente, por lo que ofrecerá una API pública alineada con las necesidades de éste. Luego, esta API Gateway será la encargada de hablar con los servicios internos y combinar los resultados para mandárselos al cliente.

Así podemos decir que BFF es un patrón para organizar nuestras API Gateways específicas.

Service Mesh

Un service mesh es infraestructura dedicada para realizar llamadas entre servicios. El objetivo es implementar protocolos que estas llamadas sean rápidas y fiables. Eso significa que el service mesh implementa, entre otros, patrones tipo retrycircuit breaker, desligando así a nuestro código de los servicios de hacerlo.

Cuando un servicio debe llamar a otro, esa llamada va a través del service mesh que ofrece estos servicios adicionales. A nivel de red podríamos decir que el service mesh funciona en los niveles 3 y 4 de OSI. Es como si tuviéramos un sustituto de TCP/IP que nos garantiza reintentos, circuit breakers y cache en determinados casos.

Un producto que puede funcionar como service mesh es Linkerd. P. ej. cuando se despliega Linkerd en Kubernetes, se termina desplegando un daemon en cada nodo, que intercepta las llamadas entre pods y les añade esos servicios adicionales. Para nuestro código esto es transparente. Es importante notar que el service mesh solo actúa entre las llamadas entre nuestros servicios, no entre los servicios y los clientes externos.

Si empiezas a dibujar cajitas es fácil confundir el service mesh con una API Gateway: al final ambos realizan llamadas entre servicios internos. Pero recuerda que el API Gateway es funcional, mientras que el service mesh es pura infraestructura. Eso significa que, cuando una API Gateway llama a varios servicios para agregar sus resultados, esas llamadas pasan por el service mesh.

Bueno, lo dejamos aquí. Espero que esto os ayude a clarificar un poco los distintos conceptos. Y como siempre, si tenéis cualquier comentario o ideas distintas de lo que son cada uno de esos conceptos, ¡no dudéis en dejar un comentario!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *