Configurações distribuídas com Spring Cloud Config

Configurações de projetos se tornaram mais fáceis com o Spring Boot, que com o conceito de auto configuração ficam menos verbosas e centralizadas nos application.properties. Porém, como são configurações em arquivos isso acaba sendo um pouco repetitivo quando utilizadas em diversas aplicações e diversos ambientes, como na arquiteturas de microsserviços onde há várias aplicações, consequentemente várias configurações que ainda são multiplicada pela quantidade de ambientes(desenvolvimento, homologação, produção).

Com isso, visando simplificar o cenário de configurações em diversas aplicações a Spring disponibiliza o projeto Spring Cloud Config, que é um servidor de configurações facilmente integrado com aplicações Spring Boot, o qual tem objetivo de gerenciar externamente e disponibilizar as configurações para as aplicações de forma centralizada, além de proporcionar algumas features como: atualização de properties sem a necessidade de restart, versionamento das configurações, reaproveitamento de properties entre aplicações e criptografia de properties.

Spring Cloud Config

O Spring Cloud Config é executado em uma aplicação standalone, para que faça o gerenciamento das configurações fora do projeto e o armazenamento dos arquivos de configuração podem ser feitos de três formas: repositório de código fonte(Git ou SVN), sistema de arquivo local do próprio Config Server e armazenamento em banco de dados através de JDBC.

A utilização das configurações pelas aplicações são através de serviços Rest, onde o Config Service disponibiliza através de suas APIs, quando utilizando Spring Boot, basta adicionar a dependência do spring-cloud-starter-config no projeto que automaticamente a aplicação faz uma chamada Rest para o servidor solicitando as configurações configuradas no profile.

Exemplo

Para exemplificar a utilização do Spring Config Server vamos criar um cenário básico de uma aplicação Spring Boot que mantém suas configurações fora da aplicação,que serão armazenadas e versionadas em um repositório git e gerenciadas por uma segunda aplicação Spring Boot habilitada como servidor de configuração, conforme ilustrado na imagem abaixo.

Config Server

O Spring Cloud Config Server pode ser executado com uma aplicação Spring Boot, apenas adicionando a dependência spring-cloud-config-server no pom.

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
        <version>1.4.3.RELEASE</version>
    </dependency>
</dependencies>

E posteriormente, ativando o servidor com a anotação @EnableConfigServer.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class CloudConfigAppConfig {

    public static void main(String[] args) {
        SpringApplication.run(CloudConfigAppConfig.class, args);
    }
}

A porta padrão utilizada pelo Config Server é a 8888.

Após a aplicação pronta para executar como Config Server, basta configurar a forma e local de armazenamento, que é o endereço do Github como os arquivos de configurações.

server.port=8888

spring.application.name=configServer
spring.cloud.config.server.git.uri=https://github.com/emmanuelneri-blog/spring-cloud-config-configuration

Obs: Como é um repositório aberto não é necessário as credenciais para acessar o repositório remoto, que podem ser configuradas por usuário e senha: spring.cloud.config.server.git.username e spring.cloud.config.server.git.password ou por ssh: spring.cloud.config.server.git.privateKey.

Arquivos de configurações

Como vamos utilizar aplicações Spring Boot, os arquivos de configurações são no padrão de profiles como demonstrado no trecho abaixo:

application.properties
application-dev.properties
application-prod.properties

Obs: Caso queira saber mais sobre os profiles no Spring Boot acesse o post.

Uma das vantagens da utilização do Config Server para gerenciamento dos arquivos de configurações, é o reaproveitamento de configurações entre os profile e entre as aplicações, ou seja, é possível manter configurações em arquivos mais genéricos (como application.properties) que vão ser aplicados em todos os projetos que utilizarem a estrutura de configuração, assim possibilitando as configurações abaixo:

application.properties
application-{profile}.properties
{app}.properties
{app}{profile}.properties
  • application.properties: Configuração global, que será aplicada em todas aplicações;
  • application-{profile}.properties: Configuração geral do profile, será aplicado em todas aplicações que utilizarem aquele ambiente
  • {app}.properties: Configuração específica para uma aplicação, que herda configurações globais do application.properties;
  • {app}-{profile}.properties: Configuração específica para a aplicação e profile, que também herdas configurações de todos outros níveis(aplicação e profile).

Para ilustrar essas possibilidades, segue a imagem abaixo demonstrando as configurações que representam uma forma de hierarquia de configuração:

Obs: Os exemplos foram demonstrado usando arquivos no formato .properties, mas também podem utilizar o formato .yml.

Acessando as configurações

O Config Server disponibiliza APIs baseado nos arquivos de configurações, com isso, as configurações podem ser requisitadas das seguintes formas:

GET /{application}/{profile}/{label}
GET /{application}-{profile}.yml
GET /{label}/{application}-{profile}.yml
GET /{application}-{profile}.properties
GET /{label}/{application}-{profile}.properties

Quando utilizado o armazenamento com repositórios de código fonte, o conceito de label são as branchs, assim quando não especificado a label, sempre será o arquivo da branch master.

Assim, para retornar a configuração de produção da aplicação app fazemos uma chamada GET para o host: http://localhost:8888/app/prod, onde é retornado o seguinte conteúdo:

{
   "name":"app",
   "profiles":[
      "prod"
   ],
   "label":null,
   "version":"3586952764950d4eb09bea10d45ed8165ff02416",
   "state":null,
   "propertySources":[
      {
         "name":"https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/app-prod.properties",
         "source":{
            "hello.api.active":"false",
            "security.user.name":"user",
            "management.security.enabled":"true",
            "security.user.password":"teste"
         }
      },
      {
         "name":"https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/application-prod.properties",
         "source":{
            "logging.level.br.com.emmanuelneri":"INFO"
         }
      },
      {
         "name":"https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/app.properties",
         "source":{
            "server.port":"8090",
            "cliente.name":"Client I"
         }
      },
      {
         "name":"https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/application.properties",
         "source":{
            "health.config.enabled":"true"
         }
      }
   ]
}
  • name: Nome da aplicação requisitada, para retornar a configuração;
  • label: Como não foi passado nenhuma label, é desconsiderado e utilizado a versão da master;
  • profiles:A Lista de profiles requisitados, no caso do exemplo apenas prod, mas lembrando que uma aplicação pode ter mais de um profile;
  • propertySources: A lista dos properties de configuração, separados por arquivo.

Assim, como foi requisitado a aplicação app é retornado os arquivos app.properties e application.properties e o profile prod retornado app-prod.properties e application-prod.properties.

Client

Do lado do client, que são as aplicação que vão utilizar as configurações, basta adicionar a dependência na aplicação e apontar o endereço do Config Server.

Com o conceito de auto-configuração do Spring Boot, adicionando a dependência do spring-cloud-starter-config no projeto, automaticamente a aplicação vai requisitar a configuração no Config Server.

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
        <version>1.4.3.RELEASE</version>
    </dependency>
</dependencies>

Como as configuração não ficam no projeto é preciso apenas informar o nome da aplicação e o endereço do Config Server através das properties spring.application.name e spring.cloud.config.uri, porém elas não são mais configuradas no application.properties e sim no bootstrap.properties, que é um arquivo de configuração que executa antes do application.properties.

resources/bootstrap.properties

spring.application.name=app
spring.cloud.config.uri=http://localhost:8888

Feito isso, no startup da aplicação será apresentados novos logs, a aplicação fazendo conexão com o Config Server, como demostrando abaixo:

2018-05-02 20:15:21.913  INFO 1439 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:8888
2018-05-02 20:15:24.569  INFO 1439 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=app, profiles=[default], label=null, version=a25a6352e3e116797554904045593b75911ac7df, state=null
2018-05-02 20:15:24.570  INFO 1439 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource [name='configService', propertySources=[MapPropertySource {name='configClient'}, MapPropertySource {name='https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/app-default.properties'}, MapPropertySource {name='https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/application-default.properties'}, MapPropertySource {name='https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/app.properties'}, MapPropertySource {name='https://github.com/emmanuelneri-blog/spring-cloud-config-configuration/application.properties'}]]
2018-05-02 20:15:24.604  INFO 1439 --- [           main] br.com.emmanuelneri.app.AppConfig        : No active profile set, falling back to default profiles: default

Como não foi atribuído nenhum profile via parâmetro, a aplicação busca pelo profile default no Config Server, que é uma arquivo configuração nome-default.properties.

Segurança no Cloud Config

Como o Cloud Config é executado em uma aplicação Spring Boot, fica a cargo do implementador decidir qual a forma de autenticação a ser utilizar. Uma boa alternativa é utilizar o starter do Spring Security que já está preparado para lidar com esse cenário.

Assim, quando utilizado algum mecanismo de autenticação, basta configurar os dados de autenticação no bootstrap.properties dos clients.

bootstrap.properties

spring.application.name=app
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.username=config
spring.cloud.config.password=config

Features

Além dos benefícios citados anteriormente o Spring Cloud Config também possibilita algumas novas features com sua utilização. Em seguida, vamos conhecer duas dela: a atualização de properties em tempo de execução e criptografia nas properties.

Atualizando properties em tempo de execução

Com o Config Server é possível atualizar properties sem a necessidade de reiniciar a aplicação, isso é possível em conjunto com contexto de refresh do Spring e subprojeto actuator para acionar o refresh das configurações.

Para que isso seja possível, as properties que podem sofrer atualizações em tempo de execução devem estar dentro de uma classe do contexto Spring (exemplo: @Component) e anotada com o escopo @RefreshScope.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties
@RefreshScope
public class RefreshProperties {

    @Value("${cliente.name}")
    private String clientName;

    @Value("${hello.api.active}")
    private boolean helloApiActive;

    public String getClientName() {
        return clientName;
    }

    public boolean isHelloApiActive() {
        return helloApiActive;
    }
}

Obs: Por cautela, é bom que nem todas as properties possam ser alteradas com o sistema em execução, para que evite erros nos ambientes com alterações indevidas, como alterar uma URL do banco de dados.

Mesmo adicionado no refreshScope as propriedades não serão atualizadas autenticamente após alterações no Config Server, assim, é preciso adicionar o Actuator no projeto, que é um subprojeto que disponibiliza alguns serviços com informações e ações nas aplicações Spring Boot.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

Após adicionado as dependências acima e reiniciado a aplicação é possível utilizar o serviço /refresh na aplicação client para atualizar o escopo e a aplicação fazer um novo request no Config Server em busca de alterações nas configurações.

curl -d {} http://localhost:8090/refresh

Obs: Para testes, pode-se utilizar a configuração management.security.enabled=false na aplicação para liberar as políticas de segurança nos serviços.

Criptografando properties

A partir do momento que as configurações estão fora dos projetos(exemplo: em repositórios git) e que vão ser trafegadas pela rede (através das chamadas Rest), vamos precisar aumentar a segurança de informações sensíveis como usuários e senhas de acessos utilizadas pela aplicação, com isso, o Cloud Config disponibiliza a funcionalidade de criptografar e descriptografar properties nos arquivos de configurações.

O primeiro passo para utilizar a criptografia é definir uma chave para geração dos conteúdos criptografados no arquivo bootstrap.properties.

encrypt.key=SpringCloudConfigEncryptSecret

Observação: Para maior segurança em ambientes de produção também é possível gerar a chave através de certificados, mais informações podem ser encontradas na doc.

Após a configuração, o Config Server disponibilizar o endpoint POST /encrypt que retorna o texto criptografado, como demostrado abaixo:

curl -d 'teste' http://localhost:8888/encrypt

737caa9ae5ea6f8c75e72d42864ed705af9bbbdf4fbbd73f981e05903f668568

Observação: Para utilizar esse mecanismo de criptografia do Cloud Config é necessário que algumas alterações na JCE(Java Cryptography Extension) da JVM, assim, é necessário fazer download e aplicar a extensão Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files ou utilizar o seguinte DockerFile com as configurações aplicadas na JVM.

Por fim, atualizar a property com a chave gerada, adicionando o prefixo {cipher}

security.user.password={cipher}c54bbe3579b5d8258cbf7db8bb19786a20816e5659d8db7b4363defb2d08d419

Assim, quando utilizado o prefixo {cipher} antes do valor da properties, o Config Server vai descriptografar automaticamente, pois ele possui a chave utilizada na criptografia, e aplicar o valor na configuração.

Conclusão

Concluindo, o Config Server é uma boa solução para quando estamos lidando com várias aplicações, onde centralizados as configurações em um servidor externo, que com isso, proporciona alguns benefícios como melhor gestão das configurações, reaproveitamento de configurações, atualizações de configurações sem restart da aplicação e possibilidade criptografia das configurações sensíveis.

O código fonte do exemplo está disponível no github.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.