Elevando o nível de microsserviços com service meshes
Desde que o Docker foi lançado – e até mesmo antes disso – pessoas desenvolvedoras se preocupam em como vão desacoplar suas aplicações das suas configurações e também da sua infraestrutura, de forma que elas possam ser facilmente migradas e possam facilmente se comunicar umas com as outras.
O advento do Kubernetes facilitou muito a implementação de melhores camadas de serviços, principalmente quando falamos de serviços distribuídos utilizando a arquitetura de microsserviços.
O grande problema é que, mesmo com todas essas facilidades, ainda enfrentamos muitas dificuldades quando o assunto é migrar uma aplicação ou até mesmo fazer com que ela seja mais independente. Principalmente por conta do modelo de comunicação que utilizamos.
O problema dos microsserviços
Quando estudamos microsserviços, vemos que o ideal é que tenhamos aplicações individuais que se comunicam com seus próprios bancos de dados e são independentes o suficiente a ponto de não precisarem de nenhuma outra aplicação externa para funcionarem.
Além disso sempre estamos ouvindo frases como esta:
Um microsserviço é uma aplicação independente, que é auto-contida e pode utilizar seu próprio banco de dados para não depender de nenhuma outra parte do sistema.
O grande problema é que nem sempre conseguimos criar uma aplicação neste modelo, isso pode estar relacionado a diversos problemas mais impeditivos:
- Grande aumento de complexidade
- Custo para manter todos os bancos de dados
- Dispersão dos dados
- Duplicação de dados
- Segurança
E outro grande problema que muitas empresas enfrentam quando utilizam microsserviços são os clássicos problemas da observabilidade (que vamos falar em outro artigo) e do Service Discovery. Apesar deste último ter sido resolvido em grande parte pelo uso de ferramentas de orquestração como o Kubernetes.
Então entramos em um assunto bastante interessante, o Service Mesh.
Service Mesh
Em palavras bastante simples, service mesh pode ser considerada uma nova "camada" que abstrai os problemas de rede quando estamos falando de comunicação entre serviços.
Porém, quando falamos de abstrações, não estamos falando apenas de algo passivo. Uma camada de service mesh também proporciona uma série de benefícios, entre eles os principais são:
- Telemetria
- Canary Deployments
- Testes A/B
- Roteamento de tráfego
- Descoberta de rede (Service Discovery)
- Monitoramento
- Tracing
Estes pontos são os principais problemas que temos em arquiteturas distribuídas, perguntas do tipo "como posso monitorar todos os meus serviços de forma completa?", "como posso saber o número de requisições que estou recebendo?" geralmente exigem a existência de um recurso compartilhado, o API Gateway, que é um recurso muito comum em arquiteturas distribuídas como um ponto de entrada e controle para toda a malha de serviços por trás.
Em geral, chamar Service Mesh de camada está um pouco errado, porque ela não é uma camada no topo dos demais serviços, mas sim uma rede embutida diretamente na estrutura das aplicações.
Uma das principais vantagens de se utilizar um modelo de mesh é, por exemplo, não termos este recurso compartilhado, como teríamos com um API Gateway se tivermos que rotear as nossas requisições através de um único ponto de entrada, pois a implementação do roteamento já está dentro da aplicação em si.
Além disso, o uso de Service Mesh permite a implementação de um padrão chamado Circuit Breaker, que isola instâncias quebradas de uma aplicação até gradualmente trazê-las de volta à vida.
As implementações mais famosas de Service Mesh hoje são o Istio e o Linkerd.
Definições
Por se tratar de um novo padrão que permeia todo o ecossistema de serviços, algumas nomenclaturas se fazem necessárias.
Instâncias e serviços
Por padrão, todas as aplicações dentro de um service mesh são chamadas de serviços, cada uma delas é uma instância de um serviço.
Basicamente todo serviço é uma cópia em execução de algum microsserviço, algumas vezes essa instância é um único container, outras vezes pode ser uma aplicação constituída de mais de um container, que é o conceito de Pod que temos no Kubernetes.
Sidecar
O grande trunfo do Service mesh é o uso de sidecars. Sidecars são proxies em containers que vivem lado a lado com os serviços que estão suportando. Quando estamos falando em um Pod no Kubernetes, um sidecar é outro container que vive dentro do mesmo pod.
O sidecar é o responsável por receber o tráfego de rede e rotear este tráfego para sua aplicação irmã. O sidecar se comunica com os demais sidecars em outros microsserviços e são gerenciados pelo orquestrador de containers em uso. Além disso, o sidecar é um dos maiores responsáveis por obter métricas de uso de rede da aplicação.
Este é o grande trunfo porque um sidecar é basicamente uma implementação distribuída de um API Gateway. Ao invés de termos um único ponto de comunicação, temos vários pontos que permitem à rede saber como chegar do serviço A ao serviço B.
Data Plane
O Data Plane é o responsável pelo tráfego de rede entre as instâncias, isso é chamado de "Tráfego Leste-Oeste", como podemos ver na imagem anterior, este tipo de tráfego é aquele que está se movendo horizontalmente dentro da mesma rede. Ou seja, o data plane é o próprio sidecar.
O tráfego Norte-Sul, como mostrado na imagem acima, é a comunicação entre diferentes redes, ou seja, a comunicação interna para uma rede externa é um tipo de tráfego N-S, enquanto um serviço se comunicando com outro dentro do mesmo cluster é um exemplo de tráfego L-O.
Control Plane
O plano de controle, ou Control Plane, é somente utilizado pelas pessoas operando o service mesh, ou seja, é o plano que contém as ferramentas de controle para a malha de serviços.
Esta camada inclui, na maioria dos casos, uma API de comunicação com o Service Mesh e também pode incluir um CLI e/ou uma interface visual para controle da aplicação, com podemos ver na imagem abaixo.
Como um service mesh pode melhorar a comunicação de serviços
É difícil entender como uma simples malha de serviço pode melhorar a comunicação de uma rede inteira. Pois bem, vamos ver uma situação prática onde podemos tirar proveito das métricas e da visibilidade do service mesh para melhorar nossas aplicações.
Imagine que você possua um serviço que falhe constantemente, isso já é conhecido mas não se sabe uma possível solução, então sua aplicação implementa um sistema de tentativas que faz um retry da chamada a cada 5, 8, 10 e 15 segundos respectivamente a cada tentativa. Porém isso está causando um gargalo de requisições visto que, em horários de pico, sua aplicação está segurando o usuário por até 15 segundos.
Por conta da natureza inerentemente transparente das métricas do service mesh, toda a comunicação é documentada, incluindo as métricas de tráfego entre serviços. Com estas métricas em mãos, podemos ver o tempo médio que nosso serviço está falhando – ou até mesmo se é este serviço que está falhando e não um outro – e ajustar o nosso tempo de retry para ser equivalente.
Vamos supor que o tempo médio de falha é de 6 segundos, então nosso retry não está sendo efetivo, pois temos que fazer duas tentativas antes de poder ter uma terceira que está válida. Podemos fixar nosso tempo de retry para 6 segundos então, evitando uma carga desnecessária de requisições do serviço.
Conclusão
Nos próximos artigos vamos explorar ainda mais como podemos criar e manipular service meshes e também seus conceitos! Por isso se inscreva na newsletter para receber notícias semanais de todos os artigos e também outras notícias do mundo da tecnologia!
Até mais!