Entendendo orquestradores parte 1: Service Discovery

Clayton Cavaleiro
Training Center
Published in
8 min readMar 12, 2019

--

Uma visão geral sobre orquestradores, um componente essencial de muitas soluções para gerenciamento de contêineres e novas arquiteturas de microserviços.

A maestria de vários componentes em sintonia diante de um maestro (Darth Vader).

Estamos em uma fase muito bacana no universo de microserviços, uma verdadeira batalha (muitas com vencedores já consolidados) no universo de conteinerização, onde cada vez mais aparecem ferramentas que podem ajudar ou piorar o seu ambiente, muitos com curva de aprendizados simples, outras complexas, que nos confundem a ponto de perguntarmos: Qual caminho escolho?

Os caminhos de especialização e tecnologias de um DevOps

Conforme podemos ver ao lado, existem vários caminhos para seguir no ramo de DevOps (caminho o qual tenho dedicado os últimos anos, e o qual este artigo se destina), entre eles iremos focar no item "Cluster Managers" o qual os orquestradores se enquadram.

Ao que irei apresentar aqui, peço que perdoem comparações que não forem corretas, mas acredito que seja didaticamente correto apresentar dessa forma, assim quando seu conhecimento for consolidado, a forma que apresentei irá fazer menos sentido, mas teve seu propósito de quebrar a barreira inicial.

Revisitando velhos conceitos: Arquitetura MVC

Arquitetura típica de uma aplicação MVC

Recorri ao modelo da Arquitetura MVC, devido seu modelo já estar bem consolidado e bem aplicado no mercado, se tornou um requisito obrigatório no caminho de desenvolvimento web para muitas tecnologias modernas.

Usando esse modelo vimos os seguintes papéis sendo exercidos:

Routing: Porta de entrada de processamento, interpretador que irá definir qual ação deve ser realizada pelo controlador a partir de um regex que será executado no path da url da aplicação, como exemplo sabemos que ele irá acionar controlador de usuários se eu mandar um caminho "/user/1" na url da aplicação.

Controller: Brinco que sua função é mais como "Pombo Correio", pois ele não pode conter regras de negócios em sua camada, parte dele a ação de normalizar a entrada de informações e levar até a camada de negócios a demanda solicitada que partiu por muitas vezes da rota, mas pode partir de outros pontos, como agendas e eventos.

Model: Camada totalmente moldada pelo modelo de negócios que a aplicação foi projetada, normalmente ele que leva a maior adaptação e mudanças ao longo do ciclo de vida de uma software. Sua responsabilidade é responder por todas as solicitações que partem do controlador já com as entradas tratadas, nesse ponto o que vale agora é a regra de negócio.

View: Camada que é responsável pela apresentação da saída, quando a arquitetura necessita de um renderizador a nível de servidor, hoje em dia é mais comum ser delegado para uma aplicação separada de front-end onde, a apresentação é moldada pelos dados que o compõe. Podemos resumir nesse artigo que é a camada que processa o output do serviço em resposta a solicitação feita ao Routing.

Vamos a partir de então, conhecendo o modelo acima, Reescrever da seguinte forma o papel de um orquestrador:

Routing: Continua o mesmo citado acima.

Controller: Ele terá um papel maior no orquestrador, ele será responsável por manter os serviços (Onde estarão os modelos de negócios dos clientes), e irá garantir que sempre terá alguém escalado para responder a rota solicitada.

Model: Os serviços definidos por terceiros (elasticsearch, postgresql, entre outros serviços), ou serviços desenvolvidos para atender a camada de negócios da solução implantada (serviços conteinerizados de várias linguagens), nele ficará a responsabilidade de processar o que foi solicitado e deve responder ao routing (note que não estamos tendo o controller como responsável por intermediar comunicação, somente por manter os modelos funcionando).

View: Iremos ignorar essa camada.

Entendendo a camada de Routing apresentando um poderoso aliado: Service Discovery

Exemplo das camadas de trabalho do Service Discovery

Quando passamos a utilizar docker e docker-compose, vimos a magia da interligação entre vários contêineres acontecendo de forma lógica e simples:

docker compose classico de um wordpress

Note que a referência acontece pelos apelidos dos serviços (db, wordpress) e nosso docker local interpreta de forma a resolver esses nomes e criar links entre os serviços e montar a rede para que eles se "encontrem" de forma fácil.

Aí vem a pergunta, tá na mesma máquina é fácil pois tenho um serviço de docker só que sabe onde está todo mundo e só vai.. e quando tenho mais de um host?

Temos um problema que vai um pouco além do docker tradicional, onde precisamos de ambos os lados de confiar em alguém que nos diga:

  • Quem é o db? ele está ativo?
  • Quem é o wordpress? ele está ativo?

Se colocarmos tudo em um lugar só pra responder com nosso velho e bom papo "cliente-servidor" estamos postergando o problema e deixando a vulnerabilidade do sistema em cima de quem vai nos informar. Não acham que ele deve ser distribuído por padrão?

Nesse nicho começa a entrar o papel do service discovery, semelhante a nossa camada de router do padrão MVC, ele remete ao responsável de cada pesquisa que fizer a ele, seja por rota, alias, dns, ou o que o service discovery escolhido permitir.

Usaremos a partir de daqui o consul como exemplo de service discovery:

Como o consul fica distribuído na sua infraestrutura.

O consul é um service discovery poderoso, de baixo consumo de recurso, que te fornece meio de encontrar os serviços por:

  • rpc
  • dns (inclusive ele implementa uma resposta adicional chamada SRV, que informa não somente os ip's de um serviço, mas também suas portas de conexão)
  • rest api

Ele tem um ecossistema forte que permite que você tenha mais facilidade para montar clusters de:

E isso pode extender a qualquer arquitetura de cluster distribuído que suportar tcp/ip para montar seu conjunto.

Uma das grandes eficácias do consul como sistema distribuído, é seu protocolo de gossip que pode ser comparado no gráfico abaixo demonstrando a diferença com os outros modelos tradicionais:

Gossip tem uma eficácia por tentar obter menor caminho da rede para obter a resposta do requerente.

Isso significa que você não precisa bater no elemento master do cluster de consul para obter a resposta, escolha qualquer um deles, inclusive um cliente de um cluster, que ele irá te dar a resposta.

Voltando ao resumo e sendo prático:

  • Tenho um cluster de 3 máquinas usando consul e criei um datacenter chamado "demo"
  • os ip's são 10.0.1, 10.0.0.2 e 10.0.3
  • como chamei de demo, o domínio criado pelo consul irá ser <nome-do-serviço>.demo.service.consul ou <nome-do-serviço>.service.consul (se eu tiver de dentro do datacenter demo).
  • o consul elege como líder o 10.0.0.2 (usando consensus protocol), mas não estou nem aí pra isso em um cluster, sempre uso essa referencia para simbolizar que o cluster inteiro opera como um serviço só:
O filme foi uma porcaria, mas esse trecho foi massa!
  • se eu usar o 10.0.0.1 na porta 8500 e procurar pelo serviço ele irá me responder!
  • se eu usar o 10.0.0.2 na porta 8500 e procurar pelo serviço ele irá me responder!
  • se eu usar o 10.0.0.3 na porta 8500 e procurar pelo serviço ele irá me responder!
  • se eu criar um cliente do serviço e apontar para qualquer um desses ip's entro no cluster e inclusive posso fazer consultas pelo localhost (127.0.0.1)

Tá legal o consul, aonde entra a lógica do service discovery???

Mais na frente iremos falar da camada que iremos comparar com controllers do MVC que são os schedulers, mas nesse caso irei falar bem rápido só para chegar no meu objetivo de mostrar o raciocínio de um service discovery.

Um outro produto da hashicorp é o nomad, que opera em cima da arquitetura do consul para controlar serviços de docker em instancias de um datacenter limitado pelo consul.

Vamos ao fluxo:

  • no nomad você fala que quer colocar um serviço chamado db e que deve servir na porta 3306 de um host e deve ser somente 1 serviço
  • o nomad irá prover o serviço pegando a imagem de docker do banco de dados especificado pelo serviço, e irá rodar em uma máquina delimitada pela sua declaração de serviço (podemos aprofundar em schedulers) .
  • o nomad para que possa controlar o estado do serviço ele registra no consul o serviço db.service.consul
  • se eu de dentro do cluster fizer o comando "dig db.service.consul", irei obter o ip de onde está o serviço provisionado
  • na hora de prover o wordpress, eu aponto como endereço do banco de dados ao invés de um ip fixo, mando o endereço "db.service.consul" e crio o serviço wordpress.service.consul.
  • Voilá temos um service discovery funcional, se por ventura tiver que migrar o banco para outra máquina (desconsidere os dados voláteis do banco nesse primeiro momento) a referencia irá trocar de ip e a consulta do service discovery irá se manter mas trocando o ip de resposta.
  • Se você tiver mais de um wordpress rodando e fizer consulta, e um estiver "ruim ou com problemas", o service discovery sozinho cuida de tirar ele da resposta da consulta, e se você tiver os dois saudáveis, ele irá fazer round-robin, tentando distribuir a carga.
  • O consul por exemplo tem como opções integrar com proxy reverso e você poderá colocar na frente para internet e permitir que recursos fora do cluster consultem serviços que estão rodando do seu datacenter do consul (se quiser entender mais a fundo clique aqui).

Conclusão

Conforme tentei construir sobre todo o artigo, demonstrei as similaridades entre um service discovery e um router em uma arquitetura MVC, mas os motivos devem ser didáticos, ficando fácil de raciocinar em como ele está operando em minha arquitetura, então vamos aos pontos principais:

  • ele serve como orientador, tanto interno como externo de onde os serviços estão localizados.
  • ele é um serviço distribuído por padrão, pois senão seria considerado um ponto único de falha, e isso bate contra o seu princípio de criação que foi justamente evitar os pontos únicos de falha.
  • o nosso exemplo consul, tem o protocolo de comunicação gossip que otimiza o menor caminho da rede para obter as respostas de suas questões
  • ele só perde seus dados somente se perdermos todos os três nós do consul no exemplo dado e não persistirmos os volumes voláteis deles (veremos posteriormente que é mais prático tratar como stateless e acreditar na disponibilidade alta do que persistir seus volume de dados)
  • Um líder no discovery, não tem aplicação nenhum para quem consulta o cluster de discovery.
  • Ele é uma extensão multi-host do modelo que conhecemos já do docker-compose em relação a referência entre os serviços.

Espero que tenham gostado. Iremos no próximo falar dos schedulers.

--

--

Clayton Cavaleiro
Training Center

Brazilian developer in pursuit to design a perfect reliable system