Padrões em microserviços — Servidor de configuração
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