Capítulo 1. Malla de servicios 101

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Linkerd es la primera malla de servicios -de hecho, es el proyecto que acuñó el término "malla de servicios". Fue creado en 2015 por Buoyant, Inc, como veremos más en el Capítulo 2, y durante todo ese tiempo se ha centrado en facilitar la producción y el funcionamiento de un software nativo de la nube realmente excelente.

Pero, ¿qué es exactamente una malla de servicios? Podemos empezar con la definición del Glosario del CNCF:

En un mundo de microservicios, las aplicaciones se dividen en múltiples servicios más pequeños que se comunican a través de una red. Al igual que tu red wifi, las redes informáticas son intrínsecamente poco fiables, pirateables y, a menudo, lentas. Las mallas de servicios abordan este nuevo conjunto de retos gestionando el tráfico (es decir, la comunicación) entre servicios y añadiendo características de fiabilidad, observabilidad y seguridad de manera uniforme en todos los servicios.

El mundo nativo de la nube consiste en la computación a una gran variedad de escalas, desde minúsculos clústeres que se ejecutan en tu portátil para el desarrollo hasta el tipo de infraestructura masiva que manejan Google y Amazon. Esto funciona mejor cuando las aplicaciones utilizan la arquitectura de microservicios, pero la arquitectura de microservicios es inherentemente más frágil que una arquitectura monolítica.

Fundamentalmente, las mallas de servicios consisten en ocultar esa fragilidad al desarrollador de aplicaciones -y, de hecho, a la propia aplicación-. Lo hacen tomando varias funciones que son críticas a la hora de crear aplicaciones robustas y trasladándolas de la aplicación a la infraestructura. Esto permite a los desarrolladores de aplicaciones centrarse en lo que hace únicas a sus aplicaciones, en lugar de tener que dedicar todo su tiempo a preocuparse por cómo proporcionar las funciones críticas que deberían ser las mismas en todas las aplicaciones.

En este capítulo daremos un vistazo general a lo que hacen las mallas de servicio, cómo funcionan y por qué son importantes. En el proceso, te proporcionaremos los antecedentes que necesitas para nuestros debates más detallados sobre Linkerd en el resto del libro.

Funcionalidad básica de la malla

Las funciones críticas que proporcionan las mallas de servicios de se dividen en tres grandes categorías: seguridad, fiabilidad y observabilidad. Al examinar estas tres categorías, compararemos la forma en que se desarrollan en un monolito típico y en una aplicación de microservicios.

Por supuesto, "monolito" puede significar varias cosas diferentes. La Figura 1-1 muestra un diagrama de la "típica" aplicación monolítica que vamos a considerar.

luar 0101
Figura 1-1. Una aplicación monolítica

El monolito es un proceso único dentro del sistema operativo, lo que significa que puede aprovechar todos los mecanismos de protección que ofrece el sistema operativo; otros procesos no pueden ver nada dentro del monolito, y definitivamente no pueden modificar nada dentro de él. Las comunicaciones entre las distintas partes del monolito suelen ser llamadas a funciones dentro del único espacio de memoria del monolito, por lo que, de nuevo, no hay oportunidad de que ningún otro proceso vea o altere estas comunicaciones. Es cierto que un área del monolito puede alterar la memoria que utilizan otras partes -de hecho, ¡ésta es una enorme fuente de fallos!-, pero en general se trata sólo de errores, no de ataques.

Procesos múltiples frente a máquinas múltiples

"¡Pero espera!", te oímos gritar. "¡Cualquier sistema operativo que se precie puede proporcionar protecciones que abarquen más de un proceso! ¿Qué pasa con los archivos mapeados en memoria o los segmentos de memoria compartida del Sistema V? ¿Qué pasa con la interfaz loopback y los sockets de dominio Unix?

Tienes razón: estos mecanismos pueden permitir que varios procesos cooperen y compartan información sin dejar de estar protegidos por el sistema operativo. Sin embargo, deben codificarse explícitamente en la aplicación, y sólo funcionan en una única máquina. Parte de la potencia de los sistemas de orquestación nativos de la nube, como Kubernetes, es que se les permite programar Pods en cualquier máquina de tu clúster, y no sabrás qué máquina con antelación. Esto es tremendamente flexible, pero también significa que los mecanismos que asumen que todo está en una sola máquina simplemente no funcionarán en el mundo nativo de la nube.

En cambio, la Figura 1-2 muestra la aplicación de microservicios correspondiente.

luar 0102
Figura 1-2. Una aplicación de microservicios

Con los microservicios, las cosas son diferentes. Cada microservicio es un proceso independiente, y los microservicios sólo se comunican a través de la red, pero los mecanismos de protección que proporciona el sistema operativo sólo funcionan dentro deun proceso. Estos mecanismos no son suficientes en un mundo en el que cualquier información compartida entre microservicios tiene que viajar por la red.

Esta dependencia de las comunicaciones a través de, una red poco fiable e insegura, planteamuchas dudas a la hora de desarrollar aplicaciones de microservicios.

Seguridad

Empecemos por el hecho de que la red es intrínsecamente insegura. Esto da lugar a una serie de posibles problemas, algunos de los cuales se muestran enla Figura 1-3.

luar 0103
Figura 1-3. La comunicación es un negocio arriesgado

Algunos de los problemas de seguridad más importantes son las escuchas, la manipulación, lausurpación de identidad y la extralimitación:

Espionaje

Los malhechores pueden ser capaces de interceptar las comunicaciones de entre dos microservicios, leyendo comunicaciones no destinadas a ellos. Dependiendo de lo que averigüe exactamente un malhechor, esto podría ser una pequeña molestia o un gran desastre.

La protección típica contra las escuchas de es la encriptación, que codifica los datos de modo que sólo el destinatario previsto pueda entenderlos.

Manipulación

Un malhechor también podría modificar los datos en tránsito por la red. En su forma más simple, el ataque de manipulación simplemente corrompería los datos en tránsito; en su forma más sutil, modificaría los datos para que resultaran ventajosos para el atacante.

Es muy importante comprender que la encriptación por sí sola no protege contra la manipulación. La protección adecuada es utilizar comprobaciones de integridad como las sumas de comprobación; todos los criptosistemas bien diseñados incluyen comprobaciones de integridad como parte de sus protocolos.

Robo de identidad

Cuando entregas los datos de la tarjeta de crédito a tu microservicio de pago, ¿cómo sabes con certeza que estás hablando realmente con tu microservicio de pago? Si un malhechor puede hacerse pasar con éxito por uno de tus microservicios, eso abre la puerta a todo tipo de posibilidades problemáticas.

La autenticación fuerte es fundamental para protegerse contra este tipo de ataque. Es la única forma de estar seguro de que el microservicio con el que hablas es realmente el que crees quees.

Extralimítate

En la otra cara de la moneda de la suplantación de identidad, un malhechor puede aprovecharse de un lugar donde se permite a un microservicio hacer cosas que sencillamente no debería permitírsele. Imagina, por ejemplo, que un malhechor descubre que el microservicio de pago está perfectamente dispuesto a aceptar solicitudes del microservicio que debería limitarse a listar cosas en venta.

Una atención cuidadosa a la autorización es la clave aquí. En un mundo perfecto, cada microservicio podrá hacer exactamente lo que necesita, y nada más (el principio del menor privilegio).

Fiabilidad

La fiabilidad en el mundo de los monolitos suele ser se refiere a lo bien que funciona el monolito: cuando las distintas partes del monolito se comunican con llamadas a funciones, normalmente no tienes que preocuparte de que se pierda una llamada o de que una de tus funciones deje de responder de repente. Pero, como se muestra en la Figura 1-4, las comunicaciones poco fiables son en realidad la norma con los microservicios.

luar 0104
Figura 1-4. Las comunicaciones poco fiables son la norma

Hay bastantes formas en que los microservicios pueden ser poco fiables, entre ellas:

Fallo de solicitud

A veces, las peticiones realizadas a través de la red fallan. Puede haber cualquier número de razones posibles, desde un microservicio bloqueado hasta una sobrecarga o partición de la red. La aplicación o la infraestructura tienen que hacer algo para tratar la petición que ha fallado.

En el caso más sencillo, la malla puede gestionar simplemente los reintentos de la aplicación: si la llamada falla porque el servicio llamado muere o se agota el tiempo de espera, basta con reenviar la solicitud. Esto no siempre funcionará, por supuesto: no todas las solicitudes son seguras para reintentar, y no todos los fallos son transitorios. Pero en muchos casos, una simple lógica de reintento puede tener un gran efecto.

Fallo del servicio

Un caso especial de fallos en las peticiones se produce cuando no es una sola instancia de un microservicio la que se bloquea, sino todas las instancias. Puede que se haya implementado una versión incorrecta, o que se haya bloqueado todo un clúster. En estos casos, la malla puede ayudar conmutando a un clúster de reserva o a una implementación del servicio que se sepa que funciona bien.

De nuevo, esto no siempre puede ocurrir sin ayuda de la aplicación (la conmutación por error de los servicios con estado puede ser bastante compleja, por ejemplo). Pero los microservicios suelen estar diseñados para gestionarse sin estado, en cuyo caso la conmutación por error de malla puede ser de gran ayuda.

Sobrecarga del servicio

Otro caso especial: a veces el fallo se produce porque se acumulan demasiadas peticiones en el mismo servicio. En estos casos, la desconexión puede ayudar a evitar un fallo en cascada: si la malla falla algunas peticiones rápidamente, antes de que los servicios dependientes se vean implicados y causen más problemas, puede ayudar a limitar los daños. Es una técnica un poco drástica, pero este tipo de desconexión forzada de la carga puede aumentar drásticamente la fiabilidad general de la aplicación en su conjunto.

Observabilidad

Es difícil ver lo que ocurre en cualquier aplicación informática: ¡incluso una máquina lenta, hoy en día, funciona en escalas de tiempo mil millones de veces más rápidas que la que vivimos los humanos! Dentro de un monolito, la observabilidad suele gestionarse mediante registros internos o cuadros de mando que recogen métricas globales de muchas áreas diferentes del monolito. Esto es mucho menos factible con una arquitectura de microservicios, como vemos en la Figura 1-5, eincluso si fuera factible, no contaría toda la historia.

luar 0105
Figura 1-5. Es difícil trabajar en la oscuridad

En el mundo de los microservicios, la "observabilidad" tiende a centrarse más en el gráfico de llamadas y las métricas doradas:

El gráfico de llamadas

Cuando se observa una aplicación de microservicios, lo primero que resulta crítico suele ser saber qué servicios son llamados por qué otros servicios. Éste es el gráfico de llamadas, que se muestra en la Figura 1-6, y algo crítico que puede hacer una malla de servicios es proporcionar métricas sobre cuánto tráfico pasa por cada perímetro del gráfico, cuánto tiene éxito, cuánto falla, etc.

luar 0106
Figura 1-6. El gráfico de llamadas de una aplicación

El gráfico de llamadas es un punto de partida crítico, porque los problemas que el usuario ve desde fuera del clúster pueden estar causados en realidad por problemas con un único servicio enterrado en lo más profundo del gráfico. Es muy importante tener visibilidad de todo el gráfico para poder resolver los problemas.

También hay que tener en cuenta que, en situaciones concretas, serán relevantes determinados caminos a través del gráfico, como se muestra en la Figura 1-7. Por ejemplo, distintas peticiones del usuario pueden utilizar distintos caminos en el gráfico, ejercitando distintos aspectos de las cargas de trabajo.

luar 0107
Figura 1-7. Diferentes caminos a través del gráfico de llamadas
La métrica de oro

Hay muchísimas métricas que podríamos recopilar para cada microservicio. Con el tiempo, tres de ellas han demostrado repetidamente ser especialmente útiles en una gran variedad de situaciones, hasta el punto de que ahora nos referimos a ellas como las "métricas de oro" (como se muestra en la Figura 1-8):

Latencia

¿Cuánto tardan en completarse las solicitudes? Normalmente se indica el tiempo que tarda en completarse un determinado porcentaje de solicitudes. Por ejemplo, la latencia P95 indica el tiempo en que se completan el 95% de las solicitudes, por lo que puedes interpretar "5 ms P95" como que el 95% de las solicitudes se completan en 5 ms o menos.

Tráfico

¿Cuántas peticiones gestiona un servicio determinado? Se suele indicar como peticiones por segundo, o RPS.

Tasa de éxito

¿Cuántas solicitudes tienen éxito? (También se puede indicar como su inverso, la tasa de error.) Se suele indicar como porcentaje del total de solicitudes, y la "tasa de éxito" se suele abreviar como TS.

luar 0108
Figura 1-8. Las tres métricas de oro

Las "Señales Doradas" originales

Estas se describieron originalmente en el post de Google "Monitorización de sistemas distribuidos " como las cuatro "señales de oro": latencia, tasa de solicitudes, tasa de errores y saturación. Preferimos "métricas de oro" porque las métricas son cosas que puedes medir directamente; las señales (como la "saturación") se derivan de las métricas.

Hablaremos de ellas con mucho más detalle en el Capítulo 10, pero vale la pena señalar en este punto que estas métricas han demostrado ser tan útiles que muchas mallas dedican un esfuerzo considerable a registrarlas, y que la malla de servicio es un lugar ideal para realizar su seguimiento.

¿Cómo funcionan realmente las mallas?

Por último, echemos un vistazo rápido a cómo funcionan realmente las mallas de servicio.

A alto nivel, todas las mallas hacen fundamentalmente el mismo trabajo: se insertan en la pila de red del sistema operativo, se hacen cargo de la red de bajo nivel que utiliza la aplicación y median en todo lo que la aplicación hace en la red. Ésta es la única forma práctica de permitir que la malla proporcione toda la funcionalidad para la que está diseñada sin requerir cambios en la propia aplicación.

La mayoría de las mallas -incluida Linkerd- utilizan el modelo sidecar de inyectar un contenedor proxy junto a cada contenedor de aplicación (ver Figura 1-9).1 Una vez en funcionamiento, el proxy reconfigura las reglas de enrutamiento de la red del host para que todo el tráfico que entra y sale del contenedor de aplicaciones pase por el proxy. Esto permite al proxy controlar todo lo necesario para la funcionalidad de la malla.

luar 0109
Figura 1-9. Linkerd y el modelo sidecar

Hay otros modelos, pero el modelo de sidecar tiene enormes ventajas en cuanto a sencillez operativa y seguridad:

  • Desde la perspectiva de básicamente todo lo demás en el sistema, el sidecar actúa como si fuera parte de la aplicación. En concreto, esto significa que todas las cosas que hace el sistema operativo para garantizar la seguridad de la aplicación también funcionan para el sidecar. Ésta es una característica muy, muy importante: limitar el sidecar a exactamente un contexto de seguridad limita drásticamente la superficie de ataque del sidecar y hace que sea mucho más fácil razonar sobre si las cosas que hace el sidecar son seguras.

  • Del mismo modo, gestionar el sidecar es exactamente igual que gestionar cualquier otra aplicación o servicio. Por ejemplo, kubectl rollout restart sólo servirá para reiniciar un Pod de aplicación y su sidecar como una unidad.

También hay desventajas, por supuesto. La mayor es que cada Pod de aplicación necesita un contenedor sidecar, aunque tu aplicación tenga miles de Pods. Otra preocupación común gira en torno a la latencia: el sidecar, por definición, requiere cierto tiempo para procesar el tráfico de red. De nuevo, hablaremos más sobre esto más adelante, pero vale la pena señalar por adelantado que Linkerd se toma muchas molestias para minimizar el impacto del sidecar, y en la práctica Linkerd es muy rápido y muy ligero.

Entonces, ¿por qué necesitamos esto?

Dicho sin rodeos, la funcionalidad que proporciona la malla no es opcional. Nunca oirás al equipo de ingeniería decir "oh, no necesitamos seguridad" o "oh, la fiabilidad no es importante" (aunque puede que tengas que convencer a la gente de la necesidad de la observabilidad -¡esperemos que este libro te ayude!)

En otras palabras, la elección no es entre tener estas tres características o no: es entre tenerlas proporcionadas por la malla o necesitar proporcionarlas en la aplicación.

Proporcionarlos en la aplicación es costoso. Tus desarrolladores podrían escribirlos a mano, pero esto significa un montón de código de aplicación engorroso replicado en cada microservicio, que es muy fácil que salga mal (sobre todo porque la tentación siempre será que los desarrolladores senior se centren en las joyas de la corona de la lógica específica de tu negocio, en lugar del trabajo monótono y menos visible, pero igualmente crítico, de hacer bien los reintentos). También puedes encontrarte con incompatibilidades entre partes de la aplicación, especialmente a medida que ésta crece.

Como alternativa, puedes encontrar bibliotecas que implementen la funcionalidad por ti, lo que sin duda ahorra tiempo de desarrollo. Por otro lado, sigues teniendo que hacer que todos y cada uno de tus desarrolladores aprendan a utilizar esas bibliotecas, estás limitado a los lenguajes y tiempos de ejecución para los que puedes encontrar las bibliotecas, y las incompatibilidades siguen siendo un grave problema (supongamos que un microservicio actualiza la biblioteca antes que otro).

Con el tiempo, nos ha quedado bastante claro que introducir toda esta funcionalidad en la malla, donde los desarrolladores de aplicaciones ni siquiera tienen por qué saber que existe, es la forma más inteligente de proporcionarla, y creemos que Linkerd es la mejor de las mallas que existen. Si al final del libro no te hemos convencido a ti también, ¡dinos en qué nos hemos quedado cortos!

Resumen

En resumen, las mallas de servicios son infraestructuras a nivel de plataforma que proporcionan seguridad, fiabilidad y observabilidad de manera uniforme en toda una aplicación, sin requerir cambios en la propia aplicación. Linkerd fue la primera malla de servicios, y creemos que sigue siendo la que tiene el mejor equilibrio entre potencia, velocidad y simplicidad operativa.

1 El nombre procede de la analogía de atornillar un sidecar a una motocicleta.

Get Linkerd: En marcha now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.