Pontos Principais
- Service meshes expõem ferramentas para o controle de tráfego entre serviços;
- As ferramentas expostas podem ser utilizadas por workflows especializados para reduzir os riscos nos releases das aplicações e melhorar o mean time to recovery (MTTR);
- Para extrair o real valor de um service mesh, extensões precisam ser desenvolvidas para maximizar suas funcionalidades;
- No momento, as implementações de service mesh variam tanto em API quanto em tecnologia, e essa tendência não mostra sinais de estar diminuindo;
- Desenvolver em cima de APIs voláteis pode ser arriscado;
- Há a necessidade de uma API simplificada e que seja amigável aos workflows para proteger a base de código das organizações dos detalhes de implementação dos service meshes.
As organizações estão tentando modernizar seus stacks de desenvolvimento com tecnologias cloud-native e uma infraestrutura que pode melhorar muito a habilidade de entregar software de qualidade rapidamente. À medida que as aplicações se tornam mais descentralizadas, e a infraestrutura mais voltada para o cloud, manter disponibilidade, qualidade e segurança fica cada vez mais difícil com a quantidade de releases feitas por dia. O Kubernetes e containers ajudaram muito na padronização dos modelos de empacotamento e implementação com base em uma infraestrutura imutável, mas uma vez que estas aplicações estão no ar, elas necessitam se comunicar umas com as outras através da rede. Esta situação apresenta inúmeros desafios ao desenvolvimento e à operação. Os service meshes fornecem um framework que serve de fundação para a construção e operação de serviços, e auxiliam a resolver alguns dos desafios de sistemas distribuídos e de entregas seguras e progressivas.
A operação e os desenvolvedores necessitam resolver os desafios de comunicação entre serviços, como o service discovery, o client-side load balancing, a configuração de segurança na camada de transporte, provisionamento de identidade, roteamento de tráfego, resiliência na comunicação com circuit breaking, observabilidade nas requisições de rede, e muitos outros. Entre várias linguagens e frameworks, esse exercício pode ser muito tedioso e propenso a erros. Ao construir soluções para uma linguagem, como é possível essa solução ser suportada em outras linguagens? Se a aplicação é desenvolvida para uma plataforma cloud, a mesma será extensível a outras? Os service meshes resolvem esses problemas injetando um proxy próximo a cada serviço, ao passo que cada serviço interage com esse proxy para se comunicar com outros serviços através da rede, isto permite que o proxy resolva de forma transparente e consistente os problemas de comunicação.
Um proxy de serviço é tipicamente dedicado a uma instância particular do serviço e não é compartilhado com outras instâncias do cluster. O conjunto desses proxies é conhecido como "plano de dados". Todas requisições para os serviços em um service mesh são interceptados para passar por esses proxies. Essas requisições são então enriquecidas por comportamentos poderosos como retries, timeouts, circuit breaking, service discovery, load balancing e muito mais. O Envoy Proxy é um projeto open source popular de proxy de serviço. Como todas requisições de/para uma instância do serviço passam pelo seu proxy, os proxies podem capturar métricas sobre as requisições e afetar como a comunicação entre os serviços é feita. Os operadores do service mesh interagem com o plano de controle, que é um conjunto de componentes que vivem à parte das requisições que suportam o plano de dados.
Os service meshes seguem a regra dos 80/20. É possível obter 80% da solução dos problemas de comunicação entre serviços de forma agnóstica à linguagem, framework e de provedor cloud. Os últimos 20% envolvem escrever um código para direcionar e operar o service mesh de modo a "orquestrá-lo" para entregar serviços de alto valor. O service mesh expõe um conjunto de habilidades individuais poderosas, mas para a organização conseguir utilizar todo seu potencial, é necessário integrá-los e combiná-los. Exemplos de serviços possibilitados pelo service mesh:
- Smart auto-scaling/back-pressure;
- Entrega progressiva;
- Detecção de padrões de comunicação;
- Análise e injeção de caos;
- Hierarquia e chain-of-custody;
- Zero-trust networking practices;
- Experimentos A/B (versão mais madura da entrega progressiva).
Um service mesh tem a habilidade de entregar 80% das necessidades de comunicação em sistemas distribuídos, mas para realmente aproveitar essas habilidades, é necessário estender a rede com 20% de código próprio.
Desenvolvendo através de um service mesh para reduzir os riscos
Vamos analisar um exemplo simples que combina muitas funcionalidades de um service mesh para ajudar a alcançar nossos objetivos e reduzir os riscos ao implementar mudanças no sistema.
A entrega progressiva é um termo cunhado em 2018 por Sam Guckenheimer, product owner na Azure Devops, e James Governor, analista na RedMonk, para descrever a abordagem em expor progressivamente uma nova funcionalidade de maneira que, em caso de erro, o dano seja minimizado. Utilizando a combinação de traffic control, feature flagging, request routing, e metrics collection, é possível construir uma pipeline poderosa de CI/CD seguindo as técnicas de entrega progressiva, o que pode reduzir os riscos em novas entregas ou em alterações de funcionalidades existentes.
Sem alterar nenhuma linha de código, poderíamos utilizar o service mesh como um framework de entrega progressiva, orquestrando habilidades como o traffic control, request routing, e metrics collection. Se, por exemplo, uma alteração no serviço de recomendação for necessária, poderíamos implementar uma nova versão e ter um ajuste fino no controle de quais usuários podem acessar a nova versão. Inicialmente, por exemplo, é possível expô-la somente aos funcionários. Após observar as métricas e logs da nova versão, seria possível saber quais limites utilizar para aumentar a nossa entrega. Se a nova versão se comportar de forma não desejada, como maior número de falhas ou uma maior latência no processamento das requisições, poderíamos voltar à versão antiga (ou reduzir o tráfego), diminuindo então o impacto da nova versão. Se tudo estiver correndo bem, podemos abrir a versão para mais usuários (por exemplo, 1% dos usuários). Podemos seguir essa abordagem e de forma devagar entregar a nova versão a novos grupos de usuários, observando os indicadores através de métricas e logs, e continuar ou interromper a entrega da versão.
Um service mesh é implementado em uma arquitetura de plano de controle e plano de dados. O plano de dados consiste dos proxies de cada serviço, através do qual métricas das requisições são coletadas e o controle de roteamento de requisições pode ser aplicado. O plano de controle é um conjunto de componentes, à parte do plano de dados, que os operadores e usuários podem interagir para coletar métricas, estabelecer políticas e aplicar mudanças de configuração no plano de dados.
Um plano de controle tem um conjunto de APIs ou alguma abordagem específica para a configuração do service mesh. Na maioria das implementações open source de service meshes, há uma forma de configuração declarativa ou conjunto de APIs com uma "intenção declarativa" sendo o service mesh responsável por "conciliar esta intenção". Por exemplo, no Istio a configuração é declarada em arquivos YAML que descrevem a intenção do comportamento da rede dos serviços, e o plano de controle do Istio sintetiza essa intenção em configurações para os proxies dos serviços. Para um serviço de recomendação, podemos querer controlar o tráfego a um release canário, separando uma porcentagem do tráfego que vai para cada versão. No Istio, podemos utilizar um VirtualService para fazer esse trabalho:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: recommendation-vs
spec:
hosts:
- recommendation.prod.svc.cluster.local
http:
- route:
- destination:
host: recommendation.prod.svc.cluster.local
subset: v1
weight: 90
- destination:
host: recommendation.prod.svc.cluster.local
subset: v2
weight: 10
O Istio também coleta métricas dos proxies dos serviços utilizando StatsD, Prometheus, e/ou Jaeger para o tracing distribuído. Essas métricas podem ser publicadas para os serviços de analytics do backend. Elas podem também ser utilizadas para tomar decisões importantes sobre a continuação, ou não, no processo de entrega/release. Utilizando esses sinais, podemos instruir o service mesh para enviar 15% do tráfego para a release canário se tudo estiver correndo bem, ou podemos diminuir (talvez até zerar) a porcentagem se os sinais mostrarem que a nova versão está se comportando de maneira inesperada.
Uma outra implementação de service mesh, o AWS App Mesh, utiliza uma API declarativa similar para descrever as regras de roteamento de tráfego. Recursos JSON alimentam o plano de controle, que sintetiza as intenções em configurações Envoy (o Envoy é utilizado como o plano de dados do App Mesh):
{
"routeName": "recommendation-route",
"spec": {
"httpRoute": {
"action": {
"weightedTargets": [
{
"virtualNode": "recommendation-v1-vn",
"weight": 9
},
{
"virtualNode": "recommendation-v2-vn",
"weight": 1
}
]
},
"match": {
"prefix": "/"
}
}
},
"virtualRouterName": "recommnedation-vr"
}
O AWS App Mesh pode enviar métricas e telemetria das requisições ao CloudWatch, X-Ray, e outros, a partir do qual um engine de entrega progressiva pode analisar se deve continuar a aumentar o fluxo de tráfego para novas versões.
Outras implementações de service mesh como o Linkerd e o Consul Connect estão trabalhando em suas APIs de controle de tráfego mas provavelmente seguirão um padrão similar.
Ao observar a implementação de uma entrega progressiva, baseada em um service mesh, é possível começar a perceber quais partes estão nos 80% e quais estão nos 20%. Embora um service mesh possa coletar métricas sobre o que está acontecendo no nível de serviço/requisições, e isso possa ajudar a deslocar/restringir/rotear tráfego para os serviços, ele não sabe do processo de promoção de serviços ou do processo de rollback, incluindo limites utilizados como gatilhos para disponibilizar o serviço a mais ou menos usuários. Esta lógica de controle, que pode variar muito entre organizações, serão os 20% de código que uma empresa necessitará desenvolver em torno do service mesh. Para conseguir isto, a organização precisará utilizar as APIs do plano de controle específicas do service mesh escolhido.
A necessidade de uma API aberta
À medida que uma organização começa a desenvolver suas habilidades em entrega progressiva, ou qualquer outra habilidade, baseadas em service meshes, ela irá necessariamente desenvolver em torno de uma API específica do service mesh escolhido. No momento, em todas implementações de service mesh as APIs estão mudando e evoluindo. Organizações inteligentes começaram a construir suas próprias APIs de configuração que expressam mais claramente suas intenções, se encaixam melhor nos valores adicionados pelos serviços construídos, e possibilita abstrair as constantes mudanças das APIs. Depois, uma camada de tradução converte essas configurações para as APIs específicas de uma implementação de service mesh. Ao abstrair os detalhes de uma API específica, uma organização pode limitar o impacto em uma alteração de uma implementação de API, especialmente ao atualizar e encontrar uma mudança não compatível com a versão anterior.
Outra vantagem em abstrair APIs específicas de um service mesh é a habilidade de mudar de tecnologia de service mesh em um momento futuro, sem necessariamente quebrar os valiosos 20% de código desenvolvidos e a funcionalidade que foi desenvolvida em cima do service mesh específico. De fato, à medida que os service meshes continuam a evoluir, veremos que cada service mesh focará ou se especializará em uma área específica (por exemplo, segurança ou observabilidade), enquanto outros encontrarão suas principais qualidades em outras áreas (como controle de tráfego, por exemplo). Com uma API separada e agnóstica ao service mesh, as empresas poderão unificar ou plugar certas funcionalidades.
Por último, à medida que organizações tentam unificar seus processos e deploys on premises com as que rodam no cloud, uma API única para abstrair a rede se torna de valor inestimável. As ferramentas que foram escritas para estender o Istio para trabalhos on premises podem ainda ser utilizadas nos deploys em nuvem. Por exemplo, o App Mesh da AWS é um service mesh nativo da AWS e, se você utilizar a AWS, faz sentido aproveitar as vantagens do service mesh nativo. Com uma API unificada para service meshes e com os serviços construídos em cima dessa API, não é necessário recriar a roda ao adotar novas plataformas.
Por um lado, as principais organizações têm construído esta "API informal e agnóstica" pelas razões citadas antes, por outro lado, é um pouco de trabalho perdido cada organização fazer isto. E se pudéssemos colaborar em um projeto open source para prover uma API estável, que nos dêem a flexibilidade de adotar as soluções que queremos/precisamos, adicionar/remover o que queremos, e também, confiantemente, escrever os 20% de código em cima da infraestrutura do service mesh escolhido?
Algumas abordagens para uma API aberta de service mesh
O SuperGloo é um projeto open source originalmente criado para se construir uma API aberta, estável e agnóstica de service mesh. Com uma API simplificada, que pode ser utilizada para abstrair qualquer service mesh, podemos alcançar os seguintes objetivos:
- Criar uma experiência consistente e simplificada ao utilizar qualquer service mesh;
- Construir extensões (através dos 20% de código) e se proteger de qualquer mudança no âmbito de service meshes (escolhas de adoção, mudanças de versão, etc);
- Descoberta de recursos e implementações, e gerenciamento dos mesmos;
- Gerenciar várias implementações diferentes sob um único painel;
- Gerenciar múltiplos cluster de uma ou mais implementações de service mesh.
No KubeCon EU 2019, a criadora do SuperGloo, a Solo.io, e outros parceiros, Microsoft, HashiCorp, e a Buoyant anunciaram o SMI, Service Mesh Interface, especificação para atuar como a API de abstração de service meshes para coisas como traffic routing, metrics collection e policy enforcement. O SuperGloo atua tanto como uma implementação de referência do SMI, com a habilidade de converter objetos SMI para qualquer service mesh, mas também atua como uma maneira de gerenciar instalações de service meshes, agrupá-las, e conectá-las em uma rede para prover federação e permitir que organizações as utilizem.
O SuperGloo tem APIs para instalação e descoberta de funcionalidades de um service mesh através dos objetos de configuração `Install` e `Mesh`, respectivamente. Por exemplo, a instalação do Linkerd pode ser feita da seguinte maneira:
- apiVersion: supergloo.solo.io/v1
kind: Install
metadata:
creationTimestamp: "2019-05-01T14:12:58Z"
generation: 1
name: linkerd
namespace: supergloo-system
resourceVersion: "5571565"
selfLink: /apis/supergloo.solo.io/v1/namespaces/supergloo-system/installs/linkerd
uid: 389b5e09-6c1b-11e9-92c9-42010a8000c0
spec:
installationNamespace: linkerd
mesh:
linkerd:
enableAutoInject: true
enableMtls: true
version: stable-2.3.0
As funcionalidades de um mesh se encontram na configuração `Mesh`:
- apiVersion: supergloo.solo.io/v1
kind: Mesh
metadata:
creationTimestamp: "2019-05-01T14:13:09Z"
generation: 1
labels:
created_by: mesh-discovery
discovered_by: linkerd-mesh-discovery
name: linkerd
namespace: supergloo-system
resourceVersion: "5571484"
selfLink: /apis/supergloo.solo.io/v1/namespaces/supergloo-system/meshes/linkerd
uid: 3f70f63e-6c1b-11e9-92c9-42010a8000c0
spec:
discoveryMetadata:
enableAutoInject: true
injectedNamespaceLabel: linkerd.io/inject
installationNamespace: linkerd
meshVersion: stable-2.3.0
mtlsConfig:
mtlsEnabled: true
linkerd:
installationNamespace: linkerd
version: 2.3.0
mtlsConfig:
mtlsEnabled: true
A configuração equivalente para o Istio e o AWS App Mesh seriam similares.
As coisas começam a ficar interessantes quando são definidas regras de tráfego. Por exemplo, podemos assinalar regras de tráfego para um mesh com a API de `TrafficSplit`. Para especificar uma regra de roteamento para direcionar a quantidade de tráfego entre as versões 1 e 2 de um serviço de `review`, é possível utilizar a seguinte configuração:
apiVersion: split.smi-spec.io/v1alpha1
kind: TrafficSplit
metadata:
name: example-routing
spec:
application.
service: reviews.default
backends:
- service: reviews-v1.default
weight: 1
- service: reviews-v3.default
weight: 100m
Observe que o SMI faz o roteamento para serviços utilizando o FQDN. Estes serviços são definidos utilizando seus labels associados e metadados de agrupamento que podem selecionar versões específicas de uma implementação.
O SMI também suporta APIs de telemetria através do TrafficMetrics, e suporta a aplicação de políticas utilizando recursos de TrafficTarget. Estes objetos podem ser aplicados em qualquer implementação de service mesh. Para ver a API por completo, basta acessar o documento de especificação. No momento, o SuperGloo suporta o Istio, o Linkerd e o AWS App Mesh via SMI. E o suporte a mais implementações estão a caminho.
O SuperGloo pode ser utilizado para gerenciar qualquer implementação de service mesh consistentemente, incluindo cenários mais complicados. Sendo possível utilizar múltiplos service meshes e múltiplos clusters (mesmo que seja um múltiplo cluster de apenas um service mesh). Todo os 20% de código que escrevemos pode ser desenvolvido agnóstico ao service mesh e prover extensões valiosas. Um grande exemplo é o projeto Flagger da Weaveworks, que fornece funcionalidade de entrega progressiva e pode utilizar as vantagens do SuperGloo e do SMI para permanecer agnóstico ao mesh.
Considerações Finais
À medida que as empresas continuam a adotar a tecnologia de service mesh e desenvolver em cima dela, elas se cansarão de amarrar seus valiosos 20% de código a uma implementação qualquer, até surgir um vencedor ou um padrão claro. Mesmo em uma única implementação, as coisas mudam rápido, as APIs mudam, e esta turbulência pode causar estragos a qualquer organização que não pode esperar para implementar os tipos de soluções que um service mesh provê. Com o SuperGloo, é possível se defender contra esta volatilidade, proteger os 20% do código, e até tratar casos de uso mais avançados como a federação de múltiplos meshes e múltiplos clusters.
Christian Posta (@christianposta) é o CTO global da Solo.io, antigo Chief Architect na Red Hat, e bem conhecido na comunidade como autor (Istio in Action, da Manning, Microservices for Java Developers, da O´Reilly 2016), blogger, palestrante, entusiasta de open source e committer de vários projetos open source, incluindo o Istio, Kubernetes, e muitos outros. Christian trabalhou tanto em empresas corporativas como em empresas com alta necessidade de escalabilidade web, e agora auxilia organizações a criar e fazer a implementação de arquiteturas distribuídas de larga escala, resilientes e cloud native. Ele curte mentorias, treinamentos e liderar times a serem bem sucedidos através dos conceitos de sistemas distribuídos, microservices, devops, e design de aplicações cloud native.