This tutorial is about spring cloud config. Here, we will take a look into how we can manage to serve and store distributed external configurations properties using spring cloud config across different applications for different environments such as dev, local, prod etc.First we will develop a simple cloud application to externalize application configurations properties using cloud config and then extend the same application to use discovery server to register the application, updating the configuration at runtime and encrypting and decrypting sensitive properties.
In a distributive cloud system, we have many smaller systems that combinly makes a larger system and hence we have multiple configurations files. Foe example, if we are using microservices, each microservices will have their own configuration files and managing that configuration within that application becomes cumbersome as there can be multiple instances running and these configuration management becomes deployment-oriented.Even, it becomes challenging not to miss any configuration changes for some of the instances.
For this, spring cloud team provides easy implementation as spring cloud config it provides server and client-side support for externalized configuration in a distributed system. With the Config Server you have a central place to manage external properties for applications across all environments
Spring cloud config is a web application that exposes REST endpoints to access the configuration properties.It supports multiple output formats such as JSON, properties and yaml.The different backened stores it supports are git(default), SVN, filesystem and JDBC store. In our example, we will be using git as a backened store for our configuration properties.
Setting up Git Backened Store
First of all, let us set up our backened store. We will be using github to store our properties and for this purpose I have created a simple github project here to store the configurations.It has basically 3 .properties file. application.properties
for storing global properties, spring-cloud-config-client.properties
for storing global properties for the application spring-cloud-config-client and similarly we have spring-cloud-config-client-local.properties
to store local properties for the application spring-cloud-config-client
server.contextPath=spring-cloud-config-client test.property=property from cloud configspring-cloud-config-client-local.properties
test.local.property=test local property
The local properties file will have configurations properties to run the spring boot application with local profile and also you can define existing properties of global configurations file if you want to ovrride it in local environment such as DB properties.
Spring Cloud Config Server Implementation
This will be a simple spring boot app. For this implementation, first download a demo spring boot app from start.spring.io with below configs.We will be using the discovery server configuration later in this tutorial.
Now import it into the IDE and you can find following maven configurations.
pom.xml<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <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>
Let us define our application configurations for this app. To make our example simple,we won't have discovery server related configuration now.Following is the git URL that we discussed in the above section.
application.propertiesserver.port=8888 spring.cloud.config.server.git.uri=https://github.com/only2dhir/config-repo.git
Following is the implementation of our main spring boot application. On simple annotation - @EnableConfigServer
will enable the required configuration for spring cloud config.
Note: - Before running this class, you can comment eureka dependency in pom.xml to avoid unnecessary error logs as we have not made any discovery server related configurations now.
package com.devglan.springcloudconfigexample; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class SpringCloudConfigExampleApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudConfigExampleApplication.class, args); } }
Running above class as a java application will expose following REST endpoints.
/{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties
Here, application is the name of the application. For example if we have our client application name as spring-cloud-config-client then the endpoints URL becomes spring-cloud-config-client-dev.properties where dev is the spring boot active profile. The label here is the git brnach which is an optional parameter.
Spring Cloud Config Client Implementation
For the cloud config client we have following dependencies required.we require doscovery client later for service discovery. For now spring-cloud-starter-config is sufficient.
pom.xml<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <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>
To bootstrap our spring cloud config configuration with the client app, we require following entries in bootstrap.yml
. Following configuration will invoke the properties configuration file for app name spring-cloud-config-client and for active profile local and our cloud config server is running on http://localhost:8888
spring.application.name=spring-cloud-config-client spring.profiles.active=local spring.cloud.config.uri=http://localhost:8888
Now let us define our spring boot application class.
SpringCloudConfigClientApplication.javapackage com.devglan.springcloudconfigclient; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringCloudConfigClientApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudConfigClientApplication.class, args); } }
Now let us define our controller class and use @Value annotation to use external properties using spring cloud config.
DemoController.javapackage com.devglan.springcloudconfigclient.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class DemoController { @Value("${test.property}") private String testProperty; @Value("${test.local.property}") private String localTestProperty; @RequestMapping("/") public String test() { StringBuilder builder = new StringBuilder(); builder.append("test property - ").append(testProperty).append(" ") .append("local property - ").append(localTestProperty); return builder.toString(); } }
In the properties file we have defined test.property
and test.local.property
properties which is injected here in the controller.In the properties file we have defined server.contextPath
as spring-cloud-config-client and hence our client application will be accessible at http://localhost:8080/spring-cloud-config-client/
Integrating Service Discovery with Spring Cloud Config
In my previous article, we create a service discovery app using spring-cloud-netflix-eureka. We will be using the same discovery server which is running on the default port 8761.To integrate with the discovery server, let us first edit our application.properties
file of service application to register itself as a service with the discvery server.Following properties will register this application with discovery server as an application name - spring-cloud-config-example
spring.application.name=spring-cloud-config-example eureka.client.service-url.defaultZone=http://localhost:8761/eureka
Annotate the SpringCloudConfigExampleApplication.java
with @EnableDiscoveryClient so that this application registers itself with the discovery client.
Also, we need to configure the same in our client application to discover the config server using discovery server.For this annotate the SpringCloudConfigClientApplication.java
with @EnableDiscoveryClient and in bootstrap.properties
file make below entries for auto discovery of cloud config service.By default, cloud config client looks for application with name configserver with the discovery server for any cloud config server but in our case the applicaton name of cloud config server is spring-cloud-config-example and hence to override it in the client we have used the properties spring.cloud.config.discovery.serviceId
spring.application.name=spring-cloud-config-client spring.profiles.active=local #spring.cloud.config.uri=http://localhost:8888 spring.cloud.config.discovery.enabled=true eureka.client.service-url.defaultZone=http://localhost:8761/eureka spring.cloud.config.discovery.serviceId=spring-cloud-config-example
Now start the discovery server, then cloud config server and then the client application and hit http://localhost:8080/spring-cloud-config-client/ and you can expect the same result as above.
So, in brief, first discovery server is started and this exposes an endpoint as http://localhost:8761/eureka to register the service. Now when the cloud config server is started, it registers itself with service id spring-cloud-config-example and exposes endpoints as http://192.168.1.6:8888/. Now when the client is started, first it tries to resolve the config properties. For this it uses the discovery server to discover the config server with service id - spring-cloud-config-example. After this the base url is resolved and then it appends /{application}-{profile}.properties i.e. to this url and fetch the config properties. The final url becomes - http://localhost:8888/spring-cloud-config-client-local.properties
Updating Cloud Config Properties at Runtime
This is one of the cool feature of spring cloud config to update the configuration properties at runtime without restarting the application. For example you can change the log levels.To update cloud configuration at runtime, you can change the configuration properties in the git project and push to repository. Then we can either use spring boot actuator /refresh
endpoint or /bus/refresh
with spring cloud bus or with VCS + /monitor with spring-cloud-config-monitor and spring-cloud-bus. But doing so will not refresh the properties annotated with @Value or @Bean because these properties are initilaized during application start-up. To refresh these properties, spring provides @RefreshScope
annotation.We will be implementing this with an example in our next article - Spring cloud config refresh property at runtime
Encrypting and Decrypting Sensitive Configuration
This is yet again another useful feature provided by spring cloud config.The configuration such as database password, username are sensitive configuration and for this encryption and decryption spring provides many features such as encrypted configuration at REST or in-flight.It has also feature for encryption and decryption using symmetric and asymmetric keys. We will be creating a sample application with examples on this topic in our next tutorial.Following is a sample application.properties that has encrypted configurations.Here is the complete configuration for Encrypting and Decrypting Sensitive Configuration
application.propertiesspring.datasource.username=root spring.datasource.password={cipher}ABCFGVH75858GFHDRT
JDBC Backend Spring Cloud Config
Spring Cloud Config Server supports JDBC (relational database) as a backend for configuration properties. You can enable this feature by adding spring-jdbc to the classpath and using the jdbc profile or by adding a bean of type JdbcEnvironmentRepository.
The database needs to have a table called PROPERTIES with columns called APPLICATION, PROFILE, and LABEL (with the usual Environment meaning), plus KEY and VALUE for the key and value pairs in Properties style. All fields are of type String in Java, so you can make them VARCHAR of whatever length you need.
Th complete implementation of it is available in this article - JDBC Backend Spring Cloud Config
Conclusion
In this tutorial, we learned about the spring cloud config. We created our cloud config server, client and a discovery server to register the service. The source can be downloaded from here. If you have anything that you want to add or share then please share it below in the comment section