Consul, parte da sua configuração

Uma abordagem mais intuitiva para quem deseja manter uma infraestrutura com servidor de configuração centralizado

Demis Gomes
Accenture Digital Product Dev
8 min readNov 6, 2019

--

Em uma arquitetura baseada em microsserviços, desacoplar os componentes de uma aplicação é fundamental para facilitar, por exemplo, a adição de novas funcionalidades e a escalabilidade da solução. Desse modo, ao contrário de estruturas monolíticas, os serviços são separados por funcionalidades, cada qual com um acesso à sua base de dados específica.

Um outro aspecto da aplicação, as configurações, pode ser migrado para um servidor centralizado. Esta alternativa traz dois benefícios: a responsabilidade da configuração em um serviço ao invés da aplicação e a possibilidade de gerenciar essas configurações de modo independente, oferecendo redundância quando necessário.

A partir desta análise, pensei em implementar uma arquitetura baseada nessa estratégia. Como um bom e velho javeiro, o framework escolhido a princípio foi o Spring (ou como diria um colega meu, o Springzaço). O Spring possui uma solução bem consolidada para um servidor de configurações centralizado chamada Spring Cloud Config.

A arquitetura do Spring Cloud Config requer três componentes: a aplicação, o servidor de configurações e um repositório Git, como mostra a figura.

Arquitetura de uma solução com o Spring Cloud Config (Fonte: Emmanuel Neri)

O repositório Git é usado para inserir as configurações do ambiente. O Config Server recupera essas informações e as mantém atualizadas para que a aplicação requisite-as. Abaixo, há um exemplo de uma aplicação em Java:

O client consegue obter as configurações quando inicia o serviço. Para modificar uma configuração existente, é necessário fazer um push no repositório Git com o novo valor. A aplicação consegue recuperar este novo valor quando recebe um POST com o endpoint /actuator/refresh , desde que esteja com a anotação RefreshScope.

Essa arquitetura levantou alguns questionamentos:

Existe uma forma de configurar essas variáveis diretamente no Config Server e não no Git?

Poderíamos configurar de modo mais intuitivo?

É possível recuperar os valores atualizados sem fazer um refresh forçado na aplicação?

Então, alguém me falou sobre o Consul…

E o que é esse tal de Consul?

Obviamente, fui pesquisar a respeito. Digitei “consul” no Google, e o que apareceu?

Sim, isso mesmo, geladeiras!

Eu sei que você já deve ter se ligado e ter feito piadas internamente, admito. Entretanto, isso dá um ensinamento: quando o nome da ferramenta remete a outra coisa com maior facilidade, complete a pesquisa com algo mais específico. Eu já tive problemas pesquisando sobre uma ferramenta chamada Juju, então fique ciente para não cometer os mesmos erros! Hehehe

Digitando “Consul microservices” apareceram coisas mais relevantes…

O Consul foca em prover uma estratégia de Service Mesh, ou seja, uma rede de serviços voltada para o funcionamento de uma aplicação, tais como configuração, Service Discovery, autenticação, entre outros. Neste post, vou focar na configuração (chamada no Consul de Key/Value store). Caso tenha interesse em saber como funciona os outros serviços, você pode acessar o site.

O Consul provê uma interface gráfica bem intuitiva, o que permite a configuração de variáveis de ambiente de modo simples. Além disso, pode ser acessado via interface REST. O gerenciamento da configuração não precisa de instalação de agentes ou algo semelhante na máquina da aplicação, ou seja, basta rodar o Consul na sua máquina de configuração e chamar o serviço.

A interface gráfica do Consul (Fonte: Hashicorp)

Para criar uma nova configuração, basta clicar em create e adicionar. Você pode adicionar pastas (adicionando um / no final) ou inserir um valor em JSON.

Quando requisitamos essa chave pelo Consul via API, ela retorna um metadado com algumas informações:

Uma requisição GET retorna metadados do conjunto chave-valor

O “value” é justamente o que estamos procurando, mas esse valor tá estranho… é porque está em base 64! O Consul busca padronizar o retorno e a inserção via API neste formato. Vale salientar que o valor máximo a ser armazenado pelo Consul é fixado em 512 KB. Então nada de salvar imagens pesadíssimas como configurações, hein? O Create e o Modify Index servem para identificar quando aquele registro foi criado e modificado, respectivamente. Este último será bem útil na funcionalidade que vamos discutir mais à frente.

Caso você não queira converter o valor em base 64 na mão, passe a opção raw na requisição GET.

O valor original é retornado quando passamos o raw no endpoint

Quando atualizamos o valor, ele tem o seu ModifyIndex alterado, como mostra a figura abaixo. Antes era 28, passou a ser 53.

O ModifyIndex se altera quando o valor é atualizado

Dado que o ModifyIndex se altera após a atualização do valor, é possível configurar um watch nele para, caso se altere, responda imediatamente. Essa é a chamada blocking query, uma das principais funcionalidades do Consul. Podemos configurar um tempo de espera máximo (por meio da variável wait) e o ModifyIndex previamente conhecido. Ao fazermos uma requisição GET, a resposta é enviada de dois modos: caso o ModifyIndex se altere antes do tempo de espera máximo ser concluído, a resposta é imediatamente enviada; por outro lado, se o valor não é alterado, a resposta é enviada ao final do tempo de espera.

Um exemplo de blocking query

No exemplo acima, a requisição é mantida até completar 60 segundos. Se o valor com index 53 não for alterado neste período, a resposta é recebida após os 60 segundos. Caso o valor seja alterado antes dos 60 segundos, a resposta é enviada imediatamente. A blocking query é uma alternativa ao pooling, que de tempos em tempos faz uma requisição ao servidor.

Outra funcionalidade interessante do Consul são as transactions. Elas podem ser executadas por meio de uma requisição PUT ao endpoint /txn . A vantagem das transactions consiste na execução de várias operações (no máximo 64) enviando apenas uma requisição. As operações a serem realizadas são definidas no corpo da requisição PUT. Um exemplo deste corpo é mostrado na figura a seguir:

Corpo da requisição PUT de uma transaction

O KV indica que a operação deve ser feita no Key/Value, ou seja, poderíamos passar outras operações para diferentes funcionalidades como o Service Discovery. O “verb” diz o que pretendemos fazer nessa chave. A primeira operação é um set, ou seja, vamos inserir ou substituir um valor na chave folder/key1. O valor é passado em base 64. A segunda operação tem o verb get-tree, que recupera todos os valores contidos na key folder (que na verdade é uma pasta). O retorno da operação é o seguinte:

Retorno da requisição feita anteriormente

O primeiro resultado é do set, enquanto os quatro resultados seguintes são do get-tree. Note que todas as keys abaixo de folder foram retornadas dado que o verb escolhido foi o get-tree. Existem vários outros verbs e, se tiver curiosidade, pode dar uma olhada neste link.

Probleminhas?

Embora o Consul tenha estratégias de logging, a falta de rastreabilidade das mudanças torna a adoção mais complicada por empresas. O Spring Cloud consegue fazer esse rastreio por meio dos commits, porém o Consul não provê quem, de fato, alterou uma configuração. Além disso, como a comunidade está bastante ativa, existe um grande número de issues abertas no github (quando escrevi esse post, em novembro de 2019, eram mais de 500), ou seja, é algo que ainda está passando por uma série de melhorias e refatorações. Por último, as operações realizadas com valores em base 64 podem causar um esforço leve, porém desnecessário para alguns desenvolvedores, já que a codificação e a decodificação poderiam ter sido evitadas por uma estratégia de autenticação.

Exemplos

O nosso querido Springzaço possui uma lib para utilizar o Consul na stack do Spring chamada Spring Cloud Starter Consul Config. Esta lib configura um arquivo bootstrap.yml que contém as informações da conexão, como mostrado abaixo:

Arquivo bootstrap.yml do Spring Consul Config

O application.properties contém, por exemplo, de quanto em quanto tempo o pooling será feito. Desse modo, o Spring não utiliza blocking queries para observar quando o valor é alterado, ou seja, requisita o valor de tempos em tempos. Caso seja utilizado o RefreshScope, a aplicação atualizará o valor obtido via Consul sem a necessidade de dar um refresh forçado! Dá uma olhada como fica a main:

Exemplo de uma aplicação usando o Spring Consul Config

O projeto está disponível no Github.

Caso você não queira utilizar o Spring, há uma lib no Github chamada Consul Client que requisita o Consul via blocking queries, ou seja, atualiza o valor assim que ele é modificado. A lib não precisa de configurações em arquivos yml ou properties. Um exemplo desta lib em um código que utiliza o Javalin, um framework web em Java/Kotlin, está descrito abaixo:

Exemplo com Javalin + Consul Client

Algumas definições da requisição ao Consul ficam explícitas no código, o que pode torná-lo um pouco mais difícil de manter. O listener usa uma blocking query que sustenta a requisição por 60 segundos e atualiza o valor assim que ele é alterado. Caso o valor seja modificado, o atributo newValue é modificado, decodificado, “printado” no console e atribuído ao consulValue, que responde às requisições ao endpoint “/” com o novo atributo modificado.

O código completo também está disponível no Github.

O uso de uma abordagem com pooling ou blocking query depende muito do seu cenário. Caso você trabalhe com valores que se alteram bastante, a blocking query é mais indicada. Caso os valores permaneçam a maior parte do tempo inalterados, uma estratégia com pooling ou um refresh pode valer a pena.

O Consul permite uma abordagem mais intuitiva para quem deseja manter uma infraestrutura com servidor de configuração centralizado. A interface gráfica, as requisições REST, a atualização “automática” e a facilidade de integração sem a necessidade de ferramentas adicionais como o Git são algumas das principais funcionalidades. Por outro lado, a falta de rastreabilidade, o número de issues abertas e o uso de valores em base 64 devem ser levadas em consideração.

De qualquer forma, quando ouvir algo sobre o Consul, pense não mais apenas em geladeiras, fogões e outros eletrodomésticos, e sim em uma ferramenta que facilite o desenvolvimento de aplicações. Entretanto, eu sei: você não vai conseguir! :D Ficou alguma dúvida ou tem algo a dizer? É só deixar um comentário. Até a próxima ;)

--

--

Demis Gomes
Accenture Digital Product Dev

Mestre em Ciência da Computação. Atualmente é Consultor de Desenvolvimento. Gosta também de um bom futebol, Fórmula 1, Star Wars, brega e doce de leite.