Aqueles de nós que vivem imersos no mundo da tecnologia estão habituados à onda contínua de novos termos e tecnologias, que muitas vezes são apenas ruído e marketing. Felizmente, de vez em quando, as ondas atiram algum tesouro. Neste caso, deixaram-nos a Service Mesh.
As arquiteturas monolíticas há muito que são reconhecidas como um problema em termos de melhoria da manutenção, escalabilidade e estabilidade das aplicações, bem como de facilitação das tarefas das equipas de desenvolvimento. Nos últimos 20 anos, tem havido um movimento no sentido de uma maior dissociação e autonomia dos componentes de uma aplicação, desde arquitecturas SOA, passando por bus de mensagens, até aos micro-serviços.
Nos últimos anos, o aparecimento das tecnologias de contentores acelerou esta evolução, fornecendo ao ecossistema de desenvolvimento e operação infraestruturas e ferramentas que facilitam e de alguma forma impulsionam a adoção de micro-serviços como blocos de construção para aplicações. O que antes era um bloco tornou-se agora em dezenas de pequenos componentes autónomos (micro-serviços), com funcionalidades muito específicas. Para utilizar uma analogia simples, este padrão de conceção é análogo ao utilizado pelo sistema operacional UNIX desde o seu início: em vez de fornecer programas complexos e pesados com dezenas de funcionalidades, o ambiente é fornecido com pequenos utilitários altamente especializados no desempenho de uma tarefa muito específica e simples, e uma série de mecanismos de interligação que permitem a sua combinação para resolver problemas mais complexos. Este padrão de conceção de sistemas operacionais tem sido bem sucedido nos últimos 50 anos, pelo que não parece ser um mau exemplo a considerar para orientar a arquitetura da aplicação.
A proliferação de plataformas de contentores, com a Kubernetes na vanguarda, acumulou centros de dados com milhares de contentores. As aplicações consistem agora em dezenas ou centenas de contentores, cujo padrão de comunicação entre si constitui o seu sistema nervoso, tal como num organismo vivo. Esta comunicação tem lugar através da rede existente, na maioria dos casos a rede de um cluster Kubernetes. É aqui que surgem as primeiras dificuldades.
Se na operação tradicional de uma aplicação clássica simples, com as suas camadas frontend e backend típicas, os fluxos de tráfego são bem definidos e facilmente rastreáveis, imagine o que acontece numa aplicação baseada em dezenas de micro-serviços, espalhados por vários sistemas físicos, ou mesmo numa infraestrutura híbrida, onde parte dela se encontra num centro de dados proprietário e o resto numa nuvem pública. O número de fluxos de comunicação explode geometricamente à medida que o tamanho da aplicação cresce, e o acompanhamento dos mesmos torna-se uma tarefa impossível de gerir. A ideia de monitorizar esses fluxos, ou de resolver um problema funcional ou de desempenho é enervante.

Figura 1- Arquitetura de micro-serviços de Uber em 2018. Fonte: Uber.
Além de responder a este desafio, depressa se tornou evidente que o modelo carecia de uma série de funcionalidades que facilitariam grandemente o ciclo de vida das aplicações, tais como:
- Balanceamento de carga: a capacidade de dividir o tráfego entre múltiplas instâncias do mesmo micro-serviço;
- Encaminhamento inteligente: tomar decisões de encaminhamento baseadas em políticas, com base em períodos de tempo, o estado de outros serviços, tipo ou conteúdo de tráfego, por exemplo. Esta funcionalidade é essencial para a adoção de modelos de implantação, tais como A/B, blue/green ou canary;
- Descoberta de serviços: Dada a complexidade de uma aplicação baseada em micro-serviços é altamente desejável ter um mecanismo de descoberta de serviços, para que um micro-serviço que precisa de comunicar com outro micro-serviço saiba onde o encontrar;
- Resiliência: facilidade de reencaminhar o trâfego a um serviço de backup em caso de falha no principal;
- Observabilidade: No mundo das aplicações monolíticas, as interações entre os seus componentes podem ser rastreadas utilizando ferramentas de depuração e de profiling. No mundo dos micro-serviços, estas interações são fluxos de comunicação altamente complexos, dinâmicos e a nível de rede. É desejável poder monitorizar e analisar estas interações para, por exemplo, diagnosticar problemas, otimizar o desempenho ou prever a capacidade;
- Segurança: Os dados entre os diferentes micro-serviços devem viajar encriptados e ambas as extremidades devem validar-se mutuamente usando certificados digitais, uma vez que não há controlo (a partir da camada de aplicação) das redes sobre as quais os dados viajam. Além disso, seria desejável poder gerir as permissões, de modo a evitar todos os fluxos de comunicação não autorizados, melhorando consideravelmente a segurança da aplicação.
Service Mesh – Arquitetura de microsserviço
Não parece razoável pedir às equipas de desenvolvimento que implementem estas funcionalidades nos próprios micro-serviços, principalmente devido ao considerável aumento de tempo e custo. O que parece fazer mais sentido é criar bibliotecas que implementem estas funcionalidades, para que elas possam ser incorporadas em aplicações. Esta foi a primeira abordagem (Stubby do Google, Hysterix da Netflix ou Finagle do Twitter), embora logo se tenha percebido que a manutenção destas bibliotecas era muito complexa e dispendiosa. Por exemplo, uma motivação para a utilização de micro-serviços é que cada micro-serviço possa utilizar a linguagem que a equipa de desenvolvimento responsável considere mais apropriada, independentemente do resto dos micro-serviços. Esta diversidade de ambientes de desenvolvimento tem de ser transferida para estas bibliotecas, forçando os seus criadores a portar as mesmas funcionalidades para dezenas de línguas. Por outro lado, quando uma vulnerabilidade é corrigida, ou um problema é resolvido, é necessário reconstruir todos os micro-serviços, possivelmente numa nova versão, e uma nova implantação da aplicação.
Fazia sentido, portanto, separar estas funcionalidades dos próprios micro-serviços, que deveriam ser agnósticos aos detalhes de implementação dos micro-serviços. Isto é conseguido através da utilização de um proxy local para cada micro-serviço, que gere as comunicações de entrada e de saída de e para o micro-serviço. Do ponto de vista do micro-serviço, a sua única interface para o mundo é este proxy, quer tenha de aceitar ligações ou necessite de comunicar com outro componente da aplicação. É este proxy que trata das tarefas de equilíbrio, gestão de tráfego, segurança, etc., de forma transparente para a aplicação. Utilizando a tecnologia de contentores, a implementação destes proxies é independente da tecnologia utilizada no seu micro-serviço associado.
Esta rede de proxies é, de facto, o plano de dados da aplicação, que gere a comunicação entre todos os seus componentes. A configuração e monitorização deste plano de dados é tratada pelo plano de controlo correspondente. Tanto os planos de dados como os planos de controlo permitem o estabelecimento de uma malha de comunicações, a que chamamos service mesh. Exemplos de implementações são Linkerd, Istio ou Consul Connect.
Conceptualmente, chega-se a uma rede overlay sobre a infraestrutura de rede existente. Este tipo de redes nasce como solução para satisfazer as funcionalidades que carece a rede sobre a qual se apoia (underlay). Alguns exemplos deste tipo de redes são, por exemplo:
- A rede Tor, que nasce para garantir o anonimato dos usuários, algo que a rede Internet não pode fazer de forma nativa;
- As redes VPN, que se desenvolvem para proporcionar segurança em forma de encriptação de comunicações e autenticação de pares;
- A rede CNI de Kubernetes, que proporciona uma rede plana entre contentores, independentemente dos servidores físicos que compõem um cluster, como por exemplo, Weave, Flannel ou Calico.
Geralmente, o aparecimento de uma overlay é de grande preocupação para os gestores de comunicações e segurança nas organizações, uma vez que está fora do seu controlo. Por exemplo, uma rede overlay poderia interligar serviços que, na underlay, seriam isolados por políticas de segurança. É também comum que, ao longo do tempo, algumas das funcionalidades que motivaram a criação da overlay acabem por ser implementadas, de uma forma muito mais eficiente, no underlay. Isto é, por exemplo, o que aconteceu com as overlays de Kubernetes e SDN´s como o Cisco ACI.
A questão que muitas organizações se colocam neste cenário é: devo incorporar uma malha de serviço no meu ambiente e adaptar os meus desenvolvimentos para fazer uso dela? A resposta não é fácil. Os benefícios são óbvios, mas há alguns inconvenientes a considerar ao tomar a decisão:
- Imaturidade: a tecnologia para implementar service mesh é relativamente recente e algumas implementações ainda têm pouco tempo;
- Preparação das equipas: a curva de aprendizagem, tanto para os perfis de desenvolvimento como de operações, é bastante inclinada.
Na maioria dos casos, a melhor abordagem será um ambiente híbrido, no qual coexistem aplicações que podem tirar partido da service mesh e aplicações mais tradicionais que não valem a pena migrar para o novo esquema. Com o tempo, a proporção de aplicações para a service mesh aumentará gradualmente.
Durante os próximos anos, veremos todas estas desvantagens serem deixadas para trás e a service mesh tornar-se-á um elemento essencial na arquitetura das aplicações.
Se quiser mais informações sobre a Satec, não hesite em entrar em contato aquí 👈