Spring Cloud Config: Centralized Configuration Management
Spring Cloud Config provides server and client-side support for externalized configuration in a distributed system. With Spring Cloud Config, you can manage your application configurations centrally, eliminating the need to hardcode properties or rely on local configuration files. This article will explore the features of Spring Cloud Config, provide step-by-step examples of setting up a config server and client, and demonstrate how to manage configurations effectively.
Features of Spring Cloud Config
- Centralized Configuration Management: Centralize your application configuration in one place, which simplifies management and ensures consistency across different environments.
- Version Control Integration: Integrate with version control systems (e.g., Git, SVN) to version and track configuration changes.
- JDBC, Vault: Other backends like JDBC databases, or HashiCorp Vault can be used.
- Environment-Specific Configurations: Manage different configurations for different environments (development, testing, production).
- Dynamic Configuration Updates: Automatically update configurations in running applications without restarting them.
- Secure Configuration Management: Secure sensitive information using encryption and decryption mechanisms.
Setting Up Spring Cloud Config Server
Step 1: Create a Spring Boot Application
First create a new Spring Boot project using Spring Initializr (https://start.spring.io/) to act as the Config Server. Add the necessary dependencies in your pom.xml
file:
##language-xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Step 2: Enable Config Server
Enable the Config Server by adding the @EnableConfigServer
annotation to your main application class:
##language-java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
Step 3: Configure the Config Server
Configure the server to load config files from the local filesystem for this demo. Add the following properties to your application.properties
or application.yml
file. There are many backends available including git, jdbc
##language-ini
server.port=8888
# Enable spring cloud config native profile
spring.profiles.active=native
# Configure cloud config to lookup properties from a local folder
spring.cloud.config.server.native.searchLocations=file:./config
spring.security.user.name=root
spring.security.user.password=changeit
Step 2: Configure the Config Client
Configure the client to connect to the Config Server. Add the following properties to your application.properties
or application.yml
file:
##language-ini
# The application name is used in the cloud config server to allow
# the server to provide configs for multiple applications
spring.application.name=my-client-app
# Load the dev profile (config/my-client-app-dev.properties)
spring.profiles.active=dev
# Be sure to include http auth here if configured
# prefix with `optional:` if the app should be able
# to come up when the server is unavailable.
spring.config.import=configserver:http://root:changeit@localhost:8888
Step 3: Create Configuration Files
Create configuration files in the folder specified in the Config Server. For example, create a file named config/my-client-app.properties
:
##language-ini
message=Hello from Spring Cloud Config
Multiple property files are merged in a similar way that spring already loads property files, there is a specific ordering where each subsequent property file overwrites any existing keys.
application.properties
application-{profile}.properties
{application-name}.properties
{application-name}-{profile}.properties
Properties can also be stored inside folders representing the application name, eg:
/{application}/application-{profile}.yml
/{application}/application.yml
Step 4: Access Configuration Properties
Access the configuration properties in your client application using the @Value
annotation or @ConfigurationProperties
:
##language-java
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class MessageController {
@Value("${message}")
private String message;
@GetMapping("/message")
public String getMessage() {
return this.message;
}
/** Run code when the context is refreshed */
@EventListener({ ContextRefreshedEvent.class })
public void onContextRefreshed() {
/** code to execute when context is refreshed */
System.out.println("Context refreshed, new message value: " + message);
}
}
Managing Environment-Specific Configurations
Spring Cloud Config supports environment-specific configurations. You can create different configuration files for different environments (e.g., application-dev.properties
, application-prod.properties
).
Example
In the config folder, create three files.
config/my-client-app.properties
:
##language-ini
message=Hello from Default Environment
config/my-client-app-dev.properties
:
##language-ini
message=Hello from Development Environment
config/my-client-app-prod.properties
:
##language-ini
message=Hello from Production Environment
Spring cloud config works by serving properties via get requests, the endpoint format is simple, {spring.application.name}/{spring.profile}
with default
being the default profile. Spring cloud client's simply make the get request and reload any components that have the @RefreshScope
annotation with the new properties, optionally you can be notified when this happens via annotating a handler in the component with @EventListener({ ContextRefreshedEvent.class })
##language-shell
> curl http://root:changeit@localhost:8888/my-client-app/dev | json_pp
{
"label" : null,
"name" : "my-client-app",
"profiles" : [
"default"
],
"propertySources" : [
{
"name" : "file:config/my-client-app.properties",
"source" : {
"message" : "Hello from Default environment"
}
}
],
"state" : null,
"version" : null
}
> curl http://root:changeit@localhost:8888/my-client-app/dev | json_pp
{
"label" : null,
"name" : "my-client-app",
"profiles" : [
"dev"
],
"propertySources" : [
{
"name" : "file:config/my-client-app-dev.properties",
"source" : {
"message" : "Hello from Development Environment"
}
}
],
"state" : null,
"version" : null
}
Switching Profiles
Switch profiles by setting the spring.profiles.active
property in your application.properties
file:
spring.profiles.active=dev
Or use the command-line argument:
##language-shell
java -jar my-client-app.jar --spring.profiles.active=prod
Dynamic Configuration Updates
Spring Cloud Config supports dynamic configuration updates using Spring Cloud Bus. The way this works is all clients and servers connect to a message bus (eg, RabbitMQ) and subscribe to some topics. The server can be configured to be triggered by an external source such as a git hook (or manually triggered) to publish property update messages over the message broker. When a client receives a message to update their properties the client performs the same update behavior as above (performing a get request to the server).
To enable this we need a message broker, spinning one up in docker is easy.
Create a docker-compose.yml file to spin up a rabbitmq service
##language-yaml
services:
rabbitmq:
image: rabbitmq:management
container_name: rabbitmq
environment:
RABBITMQ_DEFAULT_USER: user
RABBITMQ_DEFAULT_PASS: changeit
ports:
- "5672:5672"
- "15672:15672"
Just start it up with docker-compose up
Then be sure to add dependencies to the Config server and Client applications:
##language-xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
Configure the message broker (e.g., RabbitMQ) in both applications:
##language-ini
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=user
spring.rabbitmq.password=changeit
# Only do this for local development
management.security.enabled=false
# This enables actuator endpoints
# and needs to be done in both server/client
# you should not expose these endpoints publically
management.endpoints.web.exposure.include=*
Disable CSRF security validation so we can test the refresh endpoint via curl
##language-java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.ignoringRequestMatchers(
"/**"
));
return http.build();
}
Start up both the server and client via mvn spring-boot:run
Then update one of the property files then send a POST
request to the Config Server to refresh the configurations.
This is a different endpoint from /refresh
above in the client and tells the server to notify clients over the message broker to do remote refreshes.
##language-shell
curl -X POST http://localhost:8888/actuator/busrefresh
It is possible for spring cloud config server to use encrypted property values, that is outside the scope of this document but there are endpoints /encrypt
and /decrypt
you can use to look up how to do this.
Code available here
Conclusion
Spring Cloud Config is a powerful tool for managing application configurations centrally. It provides features like environment-specific configurations, dynamic updates, and secure configuration management. By following the steps outlined in this article, you can set up a Spring Cloud Config Server and Client, manage configurations effectively, and ensure that your applications are always running with the correct configurations.