Netramesh – solução leve de service mesh

À medida que passamos de uma aplicação monolítica para uma arquitetura de microsserviços, enfrentamos novos desafios.

Em uma aplicação monolítica, geralmente é bastante fácil determinar em qual parte do sistema ocorreu o erro. Muito provavelmente, o problema está no código do próprio monólito ou no banco de dados. Mas quando começamos a procurar um problema numa arquitetura de microsserviços, tudo já não é tão óbvio. Precisamos encontrar todo o caminho que a solicitação percorreu do início ao fim e selecioná-lo entre centenas de microsserviços. Além disso, muitos deles também possuem instalações de armazenamento próprias, o que também pode causar erros lógicos, bem como problemas de desempenho e tolerância a falhas.

Netramesh – solução leve de service mesh

Há muito tempo que procuro uma ferramenta que me ajude a lidar com esses problemas (escrevi sobre isso no Habré: 1, 2), mas no final criei minha própria solução de código aberto. Neste artigo falo sobre os benefícios da abordagem service mesh e compartilho uma nova ferramenta para sua implementação.

O rastreamento distribuído é uma solução comum para o problema de localização de erros em sistemas distribuídos. Mas e se esta abordagem de recolha de informação sobre interacções de rede ainda não tiver sido implementada no sistema, ou, pior, em parte do sistema já funciona correctamente, mas em parte não, uma vez que não foi adicionada a serviços antigos ? Para determinar a causa raiz exata de um problema, é necessário ter uma visão completa do que está acontecendo no sistema. É especialmente importante compreender quais microsserviços estão envolvidos nos principais caminhos críticos para os negócios.

Aqui a abordagem da malha de serviços pode vir em nosso auxílio, que lidará com todo o mecanismo de coleta de informações da rede em um nível inferior ao dos próprios serviços. Essa abordagem nos permite interceptar todo o tráfego e analisá-lo em tempo real. Além disso, os aplicativos nem precisam saber nada sobre isso.

Abordagem de malha de serviço

A ideia principal da abordagem service mesh é adicionar outra camada de infraestrutura à rede, o que nos permitirá fazer qualquer coisa com a interação entre serviços. A maioria das implementações funciona da seguinte maneira: um contêiner secundário adicional com um proxy transparente é adicionado a cada microsserviço, através do qual todo o tráfego de entrada e saída do serviço é passado. E é aqui que podemos fazer o balanceamento de clientes, aplicar políticas de segurança, impor restrições ao número de solicitações e coletar informações importantes sobre a interação dos serviços em produção.

Netramesh – solução leve de service mesh

Soluções

Já existem várias implementações desta abordagem: Istio и linkerd2. Eles fornecem muitos recursos prontos para uso. Mas, ao mesmo tempo, há uma grande sobrecarga de recursos. Além disso, quanto maior for o cluster em que tal sistema opera, mais recursos serão necessários para manter a nova infra-estrutura. Na Avito, operamos clusters Kubernetes que contêm milhares de instâncias de serviço (e seu número continua a crescer rapidamente). Em sua implementação atual, o Istio consome aproximadamente 300 MB de RAM por instância de serviço. Devido ao grande número de possibilidades, o balanceamento transparente também afeta o tempo geral de resposta dos serviços (até 10ms).

Como resultado, analisamos exatamente quais recursos precisávamos no momento e decidimos que o principal motivo pelo qual começamos a implementar essas soluções era a capacidade de coletar informações de rastreamento de todo o sistema de forma transparente. Queríamos também ter controle sobre a interação dos serviços e fazer diversas manipulações com os cabeçalhos que são transferidos entre os serviços.

Como resultado, chegamos à nossa decisão:  Netramesh.

Netramesh

Netramesh é uma solução de malha de serviço leve com capacidade de escalabilidade infinita, independentemente do número de serviços no sistema.

Os principais objetivos da nova solução eram baixa sobrecarga de recursos e alto desempenho. Entre os principais recursos, queríamos imediatamente poder enviar spans de rastreamento de forma transparente para nosso sistema Jaeger.

Hoje, a maioria das soluções em nuvem são implementadas em Golang. E, claro, existem razões para isso. Escrever aplicativos de rede em Golang que funcionam de forma assíncrona com E/S e escalam entre núcleos conforme necessário é conveniente e bastante simples. E, o que também é muito importante, o desempenho é suficiente para resolver este problema. É por isso que também escolhemos Golang.

Desempenho

Concentramos nossos esforços em alcançar a máxima produtividade. Para uma solução implantada próxima a cada instância do serviço, é necessário um pequeno consumo de RAM e tempo de CPU. E, claro, o atraso na resposta também deve ser curto.

Vamos ver quais resultados obtivemos.

RAM

Netramesh consome aproximadamente 10 MB sem tráfego e no máximo 50 MB com carga de até 10000 RPS por instância.

O proxy enviado do Istio sempre consome aproximadamente 300 MB em nossos clusters com milhares de instâncias. Isso não permite que ele seja dimensionado para todo o cluster.

Netramesh – solução leve de service mesh

Netramesh – solução leve de service mesh

Com Netramesh obtivemos uma redução de aproximadamente 10x no consumo de memória.

CPU

O uso da CPU é relativamente igual sob carga. Depende do número de solicitações por unidade de tempo para o sidecar. Valores em 3000 solicitações por segundo no pico:

Netramesh – solução leve de service mesh

Netramesh – solução leve de service mesh

Há mais um ponto importante: Netramesh - uma solução sem plano de controle e sem carga não consome tempo de CPU. Com o Istio, os sidecars sempre atualizam os endpoints de serviço. Como resultado, podemos ver esta imagem sem carga:

Netramesh – solução leve de service mesh

Usamos HTTP/1 para comunicação entre serviços. O aumento no tempo de resposta do Istio ao fazer proxy por meio do envoy foi de 5 a 10 ms, o que é bastante para serviços que estão prontos para responder em um milissegundo. Com Netramesh, esse tempo diminuiu para 0.5-2ms.

Escalabilidade

A pequena quantidade de recursos consumidos por cada proxy permite colocá-lo próximo a cada serviço. Netramesh foi criado intencionalmente sem um componente de plano de controle para simplesmente manter cada sidecar leve. Freqüentemente, em soluções de malha de serviço, o plano de controle distribui informações de descoberta de serviço para cada sidecar. Junto com ele vêm informações sobre tempos limite e configurações de balanceamento. Tudo isso permite que você faça muitas coisas úteis, mas, infelizmente, aumenta o tamanho dos carros laterais.

Descoberta de serviço

Netramesh – solução leve de service mesh

Netramesh não adiciona nenhum mecanismo adicional para descoberta de serviço. Todo o tráfego é proxy de forma transparente por meio do netra sidecar.

Netramesh suporta protocolo de aplicativo HTTP/1. Para defini-lo, é utilizada uma lista configurável de portas. Normalmente, o sistema possui várias portas através das quais ocorre a comunicação HTTP. Por exemplo, usamos 80, 8890, 8080 para interação entre serviços e solicitações externas. Neste caso, eles podem ser definidos usando uma variável de ambiente NETRA_HTTP_PORTS.

Se você usar o Kubernetes como orquestrador e seu mecanismo de entidade de serviço para comunicação intracluster entre serviços, o mecanismo permanecerá exatamente o mesmo. Primeiro, o microsserviço obtém um endereço IP de serviço usando kube-dns e abre uma nova conexão com ele. Esta conexão é estabelecida primeiro com o netra-sidecar local e todos os pacotes TCP chegam inicialmente ao netra. Em seguida, o netra-sidecar estabelece uma conexão com o destino original. O NAT no pod IP no nó permanece exatamente o mesmo que sem netra.

Rastreamento distribuído e encaminhamento de contexto

Netramesh fornece a funcionalidade necessária para enviar intervalos de rastreamento sobre interações HTTP. Netra-sidecar analisa o protocolo HTTP, mede atrasos nas solicitações e extrai as informações necessárias dos cabeçalhos HTTP. Em última análise, obtemos todos os rastros em um único sistema Jaeger. Para uma configuração refinada, você também pode usar as variáveis ​​de ambiente fornecidas pela biblioteca oficial Biblioteca Jaeger Go.

Netramesh – solução leve de service mesh

Netramesh – solução leve de service mesh

Mas há um problema. Até que os serviços gerem e enviem um cabeçalho uber especial, não veremos intervalos de rastreamento conectados no sistema. E é disso que precisamos para encontrar rapidamente a causa dos problemas. Aqui, novamente, Netramesh tem uma solução. Os proxies leem cabeçalhos HTTP e, se não contiverem o ID do uber trace, geram um. Netramesh também armazena informações sobre solicitações de entrada e saída em um arquivo secundário e as combina, enriquecendo-as com os cabeçalhos de solicitação de saída necessários. Tudo que você precisa fazer nos serviços é enviar apenas um cabeçalho X-Request-Id, que pode ser configurado usando uma variável de ambiente NETRA_HTTP_REQUEST_ID_HEADER_NAME. Para controlar o tamanho do contexto no Netramesh, você pode definir as seguintes variáveis ​​de ambiente: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (o tempo durante o qual o contexto será armazenado) e NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (frequência de limpeza de contexto).

Também é possível combinar vários caminhos no seu sistema marcando-os com um token de sessão especial. Netra permite que você instale HTTP_HEADER_TAG_MAP para transformar cabeçalhos HTTP em tags de span de rastreamento correspondentes. Isso pode ser especialmente útil para testes. Depois de passar no teste funcional, você poderá ver qual parte do sistema foi afetada pela filtragem pela chave de sessão correspondente.

Determinando a origem da solicitação

Para determinar de onde veio a solicitação, você pode usar a funcionalidade de adicionar automaticamente um cabeçalho com a origem. Usando uma variável de ambiente NETRA_HTTP_X_SOURCE_HEADER_NAME Você pode especificar um nome de cabeçalho que será instalado automaticamente. Usando NETRA_HTTP_X_SOURCE_VALUE você pode definir o valor para o qual o cabeçalho X-Source será definido para todas as solicitações de saída.

Isto permite que a distribuição deste cabeçalho útil seja distribuída uniformemente por toda a rede. Depois, você pode usá-lo em serviços e adicioná-lo a logs e métricas.

Roteamento de tráfego e componentes internos do Netramesh

Netramesh consiste em dois componentes principais. O primeiro, netra-init, define regras de rede para interceptar o tráfego. Ele usa regras de redirecionamento do iptables para interceptar todo ou parte do tráfego no sidecar, que é o segundo componente principal do Netramesh. Você pode configurar quais portas precisam ser interceptadas para sessões TCP de entrada e saída: INBOUND_INTERCEPT_PORTS, OUTBOUND_INTERCEPT_PORTS.

A ferramenta também possui um recurso interessante - roteamento probabilístico. Se você usar o Netramesh exclusivamente para coletar intervalos de rastreamento, em um ambiente de produção você poderá economizar recursos e ativar o roteamento probabilístico usando variáveis NETRA_INBOUND_PROBABILITY и NETRA_OUTBOUND_PROBABILITY (de 0 a 1). O valor padrão é 1 (todo o tráfego é interceptado).

Após a interceptação bem-sucedida, o netra sidecar aceita a nova conexão e usa SO_ORIGINAL_DST opção de soquete para obter o destino original. O Netra então abre uma nova conexão com o endereço IP original e estabelece comunicação TCP bidirecional entre as partes, ouvindo todo o tráfego que passa. Se a porta for definida como HTTP, o Netra tentará analisá-la e rastreá-la. Se a análise HTTP falhar, o Netra recorre ao TCP e faz proxy transparente dos bytes.

Construindo um gráfico de dependência

Depois de receber uma grande quantidade de informações de rastreamento no Jaeger, desejo obter um gráfico completo das interações no sistema. Mas se o seu sistema estiver bastante carregado e bilhões de intervalos de rastreamento se acumularem por dia, agregá-los não será uma tarefa tão fácil. Existe uma maneira oficial de fazer isso: dependências de faísca. No entanto, levará horas para construir um gráfico completo e forçará você a baixar todo o conjunto de dados do Jaeger nas últimas XNUMX horas.

Se você estiver usando o Elasticsearch para armazenar intervalos de rastreamento, poderá usar um utilitário Golang simples, que construirá o mesmo gráfico em minutos usando os recursos e capacidades do Elasticsearch.

Netramesh – solução leve de service mesh

Como usar o Netramesh

O Netra pode ser facilmente adicionado a qualquer serviço executando qualquer orquestrador. Você pode ver um exemplo aqui.

No momento, a Netra não tem a capacidade de implementar automaticamente sidecars nos serviços, mas há planos para implementação.

O futuro do Netramesh

Objetivo principal Netramesh é atingir custos mínimos de recursos e alto desempenho, fornecendo capacidades básicas para observabilidade e controle da comunicação entre serviços.

No futuro, o Netramesh oferecerá suporte a outros protocolos da camada de aplicação além do HTTP. O roteamento L7 estará disponível em um futuro próximo.

Use o Netramesh se encontrar problemas semelhantes e escreva-nos com perguntas e sugestões.

Fonte: habr.com

Adicionar um comentário