Padrões em microserviços — Servidor de configuração

Clayton K. N. Passos
codigorefinado
Published in
7 min readAug 26, 2019

Vamos falar sobre padrões adotados no modelo de arquitetura de micro serviços.

Servidor de configuração

Em uma arquitetura de microserviços, agora temos muitas configurações para gerenciar, pra complicar, estas configurações podem ser diferentes em ambientes distintos. Isto, pode trazer a você a necessidade de centralizar a configuração de todas as aplicações em um só lugar, é claro que você também deve ter o cuidado de definir uma configuração padrão direto em cada microserviço para o caso deste novo elemento na sua arquitetura estar fora do ar.

Utilizando o Spring Framework a definição de um valor padrão pode ser feito mais ou menos assim:

@Value("${some.key:true}")
private boolean booleanWithDefaultValue;
@Value("${some.key:42}")
private int intWithDefaultValue;

Externalizar configurações de sua aplicação é uma boa prática, permite alterar o comportamento sem que a aplicação fique fora de serviço.

Quando for necessário armazenar dados sensíveis junto as configurações (como tokens, usuário e senha), verifique a possibilidade de armazena-los de forma criptografada. Existem serviços especializados em armazenar este tipo de informação, caso precise, o Spring possuí integração com o Vault ou CrebHub, por exemplo.

Para implementar o servidor de configuração, caso você tenha escolhido por adotar o conjunto de soluções da Pivotal, temos o Spring Cloud Config, com ele seu micro serviço tem um lugar central para buscar as configurações, que ficam armazenado em um repositório do git.

Cada micro serviço irá buscar no servidor de configurações as informações de que precisa durante a inicialização.

O Spring Cloud Config possuí algumas funcionalidades interessantes, como:

  • Atualização de properties sem a necessidade de restart
  • Versionamento das configurações
  • Reaproveitamento de properties entre aplicações
  • Criptografia de properties

O armazenamento das configurações podem ser feitas em:

  • Git
  • SVN
  • Sistemas de arquivos local
  • Armazenamento em Banco de dados (JDBC)

O uso ou consumo das configurações,é feito via Rest, para isto basta adicionar a dependência spring-cloud-starter-config em seu projeto e configurar corretamente.

Arquivos de configuração

Caso decida utilizar o repositório no git, você deve seguir o padrão de profiles no nome dos arquivos, algo como:

  • bootstrap.properties
  • application.properties
  • application-dev.properties
  • application-prod.properties

Você também pode encontrar uma construção diferente, identificando a aplicação no nome do arquivo além do profile, algo como:

  • bootstrap.properties — Arquivo encontrado no cliente, utilizado para encontrar o servidor de configurações (spring.cloud.config.uri: http://myconfigserver.com). Por padrão, o spring irá procurar as configurações com arquivos com o nome da aplicação (spring.application.name: myapp)
  • application.properties — Configuração global, aplicada em todas as aplicações
  • application-{profile}.properties — Aplicada apenas nas aplicações que utilizarem este profile
  • {app}.properties — Aplicada na aplicação específica
  • {app}{profile}.properties — Aplicada na aplicação e profile, que também herda as configurações dos níveis anteriores

Parâmetro da VM para mudar o arquivo sendo carregado para o do ambiente local:

-Dspring.profiles.active=dev

Ou usando o Spring Boot:

mvn spring-boot:run -Drun.profiles=dev

Você pode utilizar .yml além do .properties :D

Como acessar as configurações?

Lembra que disse que será disponibilizado via Rest? Pois bem, você pode acessar suas configurações via HTTP, assim:

GET /{application}/{profile}/{label}GET /{application}-{profile}.ymlGET /{label}/{application}-{profile}.ymlGET /{application}-{profile}.propertiesGET /{label}/{application}-{profile}.properties

Lembre-se que:

  • application é o nome dado no properties que idealmente identifica a aplicação
  • label é a branch
  • profile é o mesmo que profile do spring

Exemplos:

  • Mostra a configuração da aplicação configclient no profile dev na branch master
    /{application}/{profile}[/{label}]
    http://localhost:8888/configclient/dev/master
  • Mostra as configurações da aplicação configclient do profile prod no formato yaml, oque tem na master
    /{application}-{profile}.yml
    http://localhost:8888/configclient-prod.yml
  • Mostra as configurações da aplicação configclient do profile prod no formato properties, oque tem na master
    /{label}/{application}-{profile}.properties
    http://localhost:8888/master/configclient-prod.properties
  • Mostra a configuração dabranch dev, para o arquivo configclient-dev.properties
    /{label}/{application}-{profile}.properties
    http://localhost:8888/dev/configclient-dev.properties
  • Mostra qual será o arquivo padrão, application.properties, não vai mostrar as configurações
    http://localhost:8888/master/configclient

Você deve ter observado, que apareceu na URL a palavra label, que identifica a branch, caso não especifique uma, será utilizado a branch Master.

Como acessar definir qual configuração o servidor deve utilizar?

Pode-se definir o profile ativo assim:

mvn clean spring-boot:run -Dspring.profiles.active=dev

Ou definir uma lista de profiles

mvn clean spring-boot:run -Dspring.profiles=dev,outro

Há opções via properties

spring:
profiles:
active: dev

Define o repositório e branch a ser utilizada

spring:
cloud:
config:
server:
git:
# uri: https://example.com/my/repo
uri: https://github.com/myorg/{application}-{profile}
username: someuser
password: somepass
default-label: branchname

Definindo a branch feature/configserver por linha de comando

mvn clean spring-boot:run -Dspring.cloud.config.server.default-label=feature/configserver

Como acessar as configurações pela aplicação cliente?

No cliente, adicione a dependência do `spring-cloud-starter-config` no pom.xml

      <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Adicione a gestão de dependências no pom.xml

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Na tag `properties` adicione a desejada


<spring-cloud.version>Greenwich.SR2</spring-cloud.version>

Você pode conferir as versões disponíveis em:

https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies

Você já deve ter deduzido que as configurações da aplicação cliente, não ficarão mais no application.properties da própria aplicação, devemos criar o arquivo `bootstrap.properties` e informar o nome da nossa aplicação cliente e o o endereço onde está o nosso servidor de configuração, mais ou menos assim:

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

Se você configurou a porta para o client utilizar, você já vai poder ver o client subindo na porta configurada.

2019-06-05 16:17:30.468  INFO 10140 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8888

Se o servidor de configurações não estiver em execução, a aplicação cliente irá pegar as configurações padrões, o caso da porta, vai subir na 8080

2019-06-05 16:21:14.474  INFO 10304 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080

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.

Caso a configuração nos arquivos sejam alterados, você não precisa reiniciar suas aplicações, você pode incluir nos objetos gerenciado do Spring a annotation @RefreshScope e ela se encarregará de atualizar o valor

@RefreshScope
@RestController
class MessageRestController {

@Value("${message:Hello default}")
private String message;

@RequestMapping("/message")
String getMessage() {
return this.message;
}
}

Caso a atualização não ocorra rápido o suficiente, com a ajuda do actuator você pode forçar enviando um POST vazio ao endereço do client(no Spring boot 2)

$ curl localhost:8888/actuator/refresh -d {} -H "Content-Type: application/json"

Lembre-se de que por padrão, no Spring Boot 2 a maioria dos endereços do actuator vem desabilitados, para habilitar, inclua no seu application.properties algo como:

management.endpoints.web.exposure.include=*

Trafegando dados com segurança

Minha recomendação é que você coloque sua aplicação em um Kubernets, e configure o mesmo para trafegar dados criptografados (TLS), delegando a responsabilidade de rede a quem cuida da camada de rede :D

Configuração de atualização do servidor

Por padrão o servidor clona o repositório na primeira requisição, é possível clonar na inicialização da aplicação.

spring.cloud.config.server.git.clone-on-start=true

Talvez seja necessário forçar o pull, caso existam sujeira ao clonar o repositório

spring.cloud.config.server.git.uri = https://github.com/spring-cloud-samples/config-repospring.cloud.config.server.git.force-pull= true

Caso branches remotas sejam removidas, talvez você queira apagar localmente:

spring.cloud.config.server.git.uri = https://github.com/spring-cloud-samples/config-repospring.cloud.config.server.git.deleteUntrackedBranches=true

Por padrão, sempre que uma requisição é feita, o servidor irá tentar atualizar o repositório caso não queria que isto ocorra,você precisa definir a propriedade refreshRate maior que 0, este número é em segundos

spring.cloud.config.server.git.refreshRate=0

Para forçar a atualização dos parâmetros, sem precisar reiniciar o servidor de configurações, você pode utilizar o actuator (org.springframework.boot:spring-boot-starter-actuator), envie um post para o servidor de configurações http://localhost:8888/actuator/refresh

curl localhost:8888/actuator/refresh -d {} -H “Content-Type: application/json”

Os clientes precisam conhecer o servidor, por isto é necessário informá-los onde encontra-lo:

spring.cloud.config.uri=http://localhost:8888

Local dos arquivos de configuração

No exemplo a baixo, o servidor irá procurar no subdiretório foo, e então em outro subdiretório que comece com bar

spring.cloud.config.server.git.uri = https://github.com/spring-cloud-samples/config-repospring.cloud.config.server.git.searchPaths = foo, bar*

No exemplo abaixo o servidor irá procurar por um subdiretório com o nome da aplicação utilizando o pattern match.

spring.cloud.config.server.git.uri= https://github.com/spring-cloud-samples/config-repospring.cloud.config.server.git.searchPaths: {application}

Repositório privado

Utilize quando for um repositório privado :D

spring.cloud.config.server.git.username=github_username
spring.cloud.config.server.git.password=github_password

Você também pode passar na uri o SSH Location, é importante que o arquivo ~/.ssh/known_host e o ssh-rsa estejam devidamente configurados.

spring.cloud.config.server.git.uri=git@github.com:configuration/cloud-configuration

Outras configurações

A fim de aumentar a disponibilidade você deve manter as configurações em um repositório, no exemplo a baixo você vê a configuração para buscar as configurações em um repositório no github, e pulando a o SSL

spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repospring.cloud.config.server.git.skipSslValidation=true

Com a configuração abaixo, o servidor de configuração irá utilizar arquivos da máquina local, da pasta config-repo dentro de resources, você também pode utilizar a sintaxe: file:, mas utilizar classpath: costuma ser uma ideia melhor.

spring.profiles.include=nativespring.cloud.config.server.native.search-locations=classpath:/config-repo

Abaixo você vê um exemplo de configuração que carrega os properties da raiz, e da pasta 04-ConfigServer da branch feature/configserver, observe que o search-paths pode receber um conjunto, indicando várias pastas

spring:
cloud:
config:
server:
git:
uri: https://github.com/codigorefinado/curso-spring-cloud
search-paths:
- 04-ConfigServer
default-label: feature/configserver

Você pode definir o profile passando argumento na VM:

-Dspring.profiles.active=native
-Dspring.profiles.active=native,local

Habilita sobrescrever as propriedades remotas pelas locais:

spring.cloud.config.allow-override=true
spring.cloud.config.override-none=true

Configurações do cliente

A propriedade a baixo serve para definir onde está o servidor de configurações, para melhorar a disponibilidade é possível informar vários servidores separando-os por virgula. Evidentemente, você pode utilizar o registro em um “Service Registry” como o Eureka para ter o mesmo efeito.

spring.cloud.config.uri=url1, url2, url3

Configura se irá mostrar se foi carregado as configurações do servidor, por padrão esta informação é cacheada por 5 minutos.

health.config.enabled=false

Para mudar o tempo do cache de 5 minutos pode utilizar a configuração a baixo, passando um valor em mile segundos

health.config.time-to-live

Se quiser que a aplicação falhe na inicialização caso não encontre o servidor, utilize no bootstrap.properties a configuração abaixo:

spring.cloud.config.fail-fast=true

Eventualmente o servidor de configuração pode estar fora. Caso queira que a aplicação tente outras vezes, utilize a confiuração abaixo no bootstrap.properties e adicione a dependências

spring.cloud.config.fail-fast=true
spring.cloud.config.retry.max-attempts=5
spring.cloud.config.retry.initial-interval=1500
spring.cloud.config.retry.multiplier=1.5

Dependências:

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Por padrão, o cliente irá procurar as configurações no arquivo {application}-{profile}.properties na branch master.

"name" = ${spring.application.name}
"profile" = ${spring.profiles.active} (actually Environment.getActiveProfiles())
"label" = "master"

Pode-se alterar o padrão. O atributo label pode receber um conjunto de branches, separado por virgula, sendo que ele irá utilizar aquele que encontrar primeiro.

spring.cloud.config.name=minhaaplicacao
spring.cloud.config.profile=devlocal
spring.cloud.config.label=myfeature,develop

--

--