Netramesh: solución de malla de servicio liviana

A medida que pasamos de una aplicación monolítica a una arquitectura de microservicios, enfrentamos nuevos desafíos.

En una aplicación monolítica, suele ser bastante fácil determinar en qué parte del sistema se produjo el error. Lo más probable es que el problema esté en el código del propio monolito o en la base de datos. Pero cuando empezamos a buscar un problema en una arquitectura de microservicios, ya no todo es tan obvio. Necesitamos encontrar la ruta completa que tomó la solicitud de principio a fin y seleccionarla entre cientos de microservicios. Además, muchos de ellos también tienen sus propias instalaciones de almacenamiento, lo que también puede causar errores lógicos, así como problemas de rendimiento y tolerancia a fallos.

Netramesh: solución de malla de servicio liviana

He estado buscando durante mucho tiempo una herramienta que me ayudara a afrontar este tipo de problemas (escribí sobre ello en Habré: 1, 2), pero al final creé mi propia solución de código abierto. En este artículo hablo sobre los beneficios del enfoque de malla de servicios y comparto una nueva herramienta para su implementación.

El rastreo distribuido es una solución común al problema de encontrar errores en sistemas distribuidos. Pero, ¿qué pasa si este enfoque para recopilar información sobre las interacciones de la red aún no se ha implementado en el sistema o, peor aún, en parte del sistema ya funciona correctamente, pero en parte no, ya que no se ha agregado a los servicios antiguos? ? Para determinar la causa raíz exacta de un problema, es necesario tener una imagen completa de lo que está sucediendo en el sistema. Es especialmente importante comprender qué microservicios están involucrados en rutas clave y críticas para el negocio.

Aquí puede venir en nuestra ayuda el enfoque de malla de servicios, que se ocupará de toda la maquinaria para recopilar información de la red a un nivel inferior al que operan los propios servicios. Este enfoque nos permite interceptar todo el tráfico y analizarlo sobre la marcha. Además, las aplicaciones ni siquiera tienen que saber nada al respecto.

Enfoque de malla de servicios

La idea principal del enfoque de malla de servicios es agregar otra capa de infraestructura a la red, lo que nos permitirá hacer cualquier cosa con la interacción entre servicios. La mayoría de las implementaciones funcionan de la siguiente manera: a cada microservicio se agrega un contenedor adicional con un proxy transparente, a través del cual pasa todo el tráfico entrante y saliente del servicio. Y este es precisamente el lugar donde podemos equilibrar los clientes, aplicar políticas de seguridad, imponer restricciones en la cantidad de solicitudes y recopilar información importante sobre la interacción de los servicios en producción.

Netramesh: solución de malla de servicio liviana

Решения

Ya existen varias implementaciones de este enfoque: Istio и enlazador2. Proporcionan muchas funciones listas para usar. Pero al mismo tiempo, se produce un gran gasto en recursos. Además, cuanto mayor sea el grupo en el que opera dicho sistema, más recursos se necesitarán para mantener la nueva infraestructura. En Avito, operamos clústeres de Kubernetes que contienen miles de instancias de servicio (y su número continúa creciendo rápidamente). En su implementación actual, Istio consume ~300Mb de RAM por instancia de servicio. Debido a la gran cantidad de posibilidades, el equilibrio transparente también afecta el tiempo de respuesta general de los servicios (hasta 10 ms).

Como resultado, analizamos exactamente qué capacidades necesitábamos en este momento y decidimos que la razón principal por la que comenzamos a implementar dichas soluciones era la capacidad de recopilar información de seguimiento de todo el sistema de forma transparente. También queríamos tener control sobre la interacción de los servicios y realizar diversas manipulaciones con los encabezados que se transfieren entre servicios.

Como resultado, llegamos a nuestra decisión:  netramesh.

netramesh

netramesh es una solución de malla de servicios liviana con la capacidad de escalar infinitamente, independientemente de la cantidad de servicios en el sistema.

Los principales objetivos de la nueva solución eran una baja sobrecarga de recursos y un alto rendimiento. Entre las características principales, inmediatamente quisimos poder enviar de forma transparente tramos de seguimiento a nuestro sistema Jaeger.

Hoy en día, la mayoría de las soluciones en la nube se implementan en Golang. Y, por supuesto, hay razones para ello. Escribir aplicaciones de red en Golang que funcionen de forma asincrónica con E/S y se escalen entre núcleos según sea necesario es conveniente y bastante simple. Y, lo que también es muy importante, el rendimiento es suficiente para solucionar este problema. Por eso también elegimos Golang.

Rendimiento

Hemos centrado nuestros esfuerzos en conseguir la máxima productividad. Para una solución que se implementa junto a cada instancia del servicio, se requiere un pequeño consumo de RAM y tiempo de CPU. Y, por supuesto, el retraso en la respuesta también debería ser pequeño.

Veamos qué resultados obtuvimos.

RAM

Netramesh consume ~10Mb sin tráfico y 50Mb como máximo con una carga de hasta 10000 RPS por instancia.

El proxy enviado de Istio siempre consume ~300 Mb en nuestros clústeres con miles de instancias. Esto no permite escalarlo a todo el clúster.

Netramesh: solución de malla de servicio liviana

Netramesh: solución de malla de servicio liviana

Con Netramesh obtuvimos una reducción de aproximadamente 10 veces en el consumo de memoria.

CPU

El uso de la CPU es relativamente igual bajo carga. Depende del número de solicitudes por unidad de tiempo al sidecar. Valores a 3000 solicitudes por segundo en pico:

Netramesh: solución de malla de servicio liviana

Netramesh: solución de malla de servicio liviana

Hay un punto más importante: Netramesh: una solución sin plano de control y sin carga no consume tiempo de CPU. Con Istio, los sidecars siempre actualizan los puntos finales del servicio. Como resultado, podemos ver esta imagen sin carga:

Netramesh: solución de malla de servicio liviana

Usamos HTTP/1 para la comunicación entre servicios. El aumento en el tiempo de respuesta de Istio cuando se realiza proxy a través de Envoy fue de hasta 5 a 10 ms, lo cual es bastante para servicios que están listos para responder en un milisegundo. Con Netramesh este tiempo se ha reducido a 0.5-2 ms.

Escalabilidad

La pequeña cantidad de recursos que consume cada proxy permite colocarlo al lado de cada servicio. Netramesh fue creado intencionalmente sin un componente de plano de control para simplemente mantener cada sidecar liviano. A menudo, en las soluciones de malla de servicios, el plano de control distribuye información de descubrimiento de servicios a cada sidecar. Junto con esto viene información sobre tiempos de espera y configuraciones de equilibrio. Todo esto te permite hacer muchas cosas útiles, pero, desafortunadamente, aumenta el tamaño de los sidecars.

Descubrimiento de servicio

Netramesh: solución de malla de servicio liviana

Netramesh no agrega ningún mecanismo adicional para el descubrimiento de servicios. Todo el tráfico se envía de forma transparente a través de netra sidecar.

Netramesh admite el protocolo de aplicación HTTP/1. Para definirlo se utiliza una lista configurable de puertos. Normalmente, el sistema tiene varios puertos a través de los cuales se produce la comunicación HTTP. Por ejemplo, usamos 80, 8890, 8080 para la interacción entre servicios y solicitudes externas. En este caso, se pueden configurar usando una variable de entorno. NETRA_HTTP_PORTS.

Si utiliza Kubernetes como orquestador y su mecanismo de entidad de servicio para la comunicación dentro del clúster entre servicios, entonces el mecanismo sigue siendo exactamente el mismo. Primero, el microservicio obtiene una dirección IP de servicio utilizando kube-dns y abre una nueva conexión. Esta conexión se establece primero con el netra-sidecar local y todos los paquetes TCP llegan inicialmente a netra. A continuación, netra-sidecar establece una conexión con el destino original. NAT en la IP del pod en el nodo sigue siendo exactamente igual que sin netra.

Seguimiento distribuido y reenvío de contexto

Netramesh proporciona la funcionalidad necesaria para enviar tramos de seguimiento sobre interacciones HTTP. Netra-sidecar analiza el protocolo HTTP, mide los retrasos en las solicitudes y extrae la información necesaria de los encabezados HTTP. En última instancia, obtenemos todas las trazas en un único sistema Jaeger. Para una configuración detallada, también puede utilizar las variables de entorno proporcionadas por la biblioteca oficial. biblioteca jaeger go.

Netramesh: solución de malla de servicio liviana

Netramesh: solución de malla de servicio liviana

Pero hay un problema. Hasta que los servicios generen y envíen un encabezado uber especial, no veremos tramos de seguimiento conectados en el sistema. Y esto es lo que necesitamos para encontrar rápidamente la causa de los problemas. Aquí nuevamente Netramesh tiene una solución. Los servidores proxy leen los encabezados HTTP y, si no contienen el ID de seguimiento de Uber, generan uno. Netramesh también almacena información sobre solicitudes entrantes y salientes en un sidecar y las compara enriqueciéndolas con los encabezados de solicitudes salientes necesarios. Todo lo que necesitas hacer en los servicios es enviar solo un encabezado. X-Request-Id, que se puede configurar mediante una variable de entorno NETRA_HTTP_REQUEST_ID_HEADER_NAME. Para controlar el tamaño del contexto en Netramesh, puede configurar las siguientes variables de entorno: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (el tiempo durante el cual se almacenará el contexto) y NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (frecuencia de limpieza de contexto).

También es posible combinar varias rutas en su sistema marcándolas con un token de sesión especial. Netra te permite instalar HTTP_HEADER_TAG_MAP para convertir los encabezados HTTP en etiquetas de intervalo de seguimiento correspondientes. Esto puede resultar especialmente útil para realizar pruebas. Luego de pasar la prueba funcional, se puede ver qué parte del sistema fue afectada filtrando por la clave de sesión correspondiente.

Determinar el origen de la solicitud

Para determinar de dónde proviene la solicitud, puede utilizar la función de agregar automáticamente un encabezado con la fuente. Usando una variable de entorno NETRA_HTTP_X_SOURCE_HEADER_NAME Puede especificar un nombre de encabezado que se instalará automáticamente. Mediante el uso NETRA_HTTP_X_SOURCE_VALUE puede establecer el valor en el que se establecerá el encabezado X-Source para todas las solicitudes salientes.

Esto permite que la distribución de este útil encabezado se distribuya de manera uniforme por toda la red. Luego puedes usarlo en servicios y agregarlo a registros y métricas.

Enrutamiento de tráfico y elementos internos de Netramesh

Netramesh consta de dos componentes principales. El primero, netra-init, establece reglas de red para interceptar el tráfico. Él usa reglas de redireccionamiento de iptables interceptar todo o parte del tráfico en sidecar, que es el segundo componente principal de Netramesh. Puede configurar qué puertos deben interceptarse para las sesiones TCP entrantes y salientes: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.

La herramienta también tiene una característica interesante: el enrutamiento probabilístico. Si utiliza Netramesh exclusivamente para recopilar tramos de seguimiento, en un entorno de producción puede ahorrar recursos y habilitar el enrutamiento probabilístico utilizando variables. NETRA_INBOUND_PROBABILITY и NETRA_OUTBOUND_PROBABILITY (de 0 a 1). El valor predeterminado es 1 (se intercepta todo el tráfico).

Después de una intercepción exitosa, netra sidecar acepta la nueva conexión y utiliza SO_ORIGINAL_DST opción de socket para obtener el destino original. Luego, Netra abre una nueva conexión a la dirección IP original y establece una comunicación TCP bidireccional entre las partes, escuchando todo el tráfico que pasa. Si el puerto está definido como HTTP, Netra intenta analizarlo y rastrearlo. Si el análisis de HTTP falla, Netra recurre a TCP y transfiere los bytes de forma transparente.

Construyendo un gráfico de dependencia

Después de recibir una gran cantidad de información de seguimiento en Jaeger, quiero obtener un gráfico completo de las interacciones en el sistema. Pero si su sistema está bastante cargado y se acumulan miles de millones de tramos de seguimiento por día, agregarlos no se convierte en una tarea tan fácil. Hay una forma oficial de hacer esto: dependencias de chispa. Sin embargo, llevará horas crear un gráfico completo y le obligará a descargar todo el conjunto de datos de Jaeger durante las últimas XNUMX horas.

Si está utilizando Elasticsearch para almacenar intervalos de seguimiento, puede utilizar una sencilla utilidad de Golang, que creará el mismo gráfico en minutos utilizando las características y capacidades de Elasticsearch.

Netramesh: solución de malla de servicio liviana

Cómo utilizar Netramesh

Netra se puede agregar fácilmente a cualquier servicio que ejecute cualquier orquestador. Puedes ver un ejemplo. aquí.

Por el momento, Netra no tiene la capacidad de implementar sidecars automáticamente en los servicios, pero hay planes para su implementación.

El futuro de Netramesh

Propósito principal netramesh es lograr costos mínimos de recursos y un alto rendimiento, proporcionando capacidades básicas para la observabilidad y el control de la comunicación entre servicios.

En el futuro, Netramesh admitirá otros protocolos de capa de aplicación además de HTTP. La ruta L7 estará disponible en un futuro próximo.

Utilice Netramesh si encuentra problemas similares y escríbanos con preguntas y sugerencias.

Fuente: habr.com

Añadir un comentario