Hey everyone! Today, we'll be learning about config servers in a microservice architecture using Spring Cloud. As a practical example, we'll be extending the student and course API from our last guide to use a config server.
Prerequisites
To follow along with this tutorial, you should be able to follow along with our last guide on the basics of microservices and implementing them with Spring Cloud.
What is a Config Server
A config server is a microservice that other services use to import their configurations (in the case of Spring Cloud, settings that would go in your application.properties
file). This is useful for sharing common configurations for your microservices and creating externalized configurations that can be updated without redeployment! The config server can read configuration files from one location (often a git repository) and then other services can read the configuration files from the config server and import them.
Implementation
Now that you understand the idea of a config server, let's implement it. To begin, we'll be starting from the code we left off from in the last guide which you can find here. From that starting point, we'll need to create a new ConfigServer module:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.john.amiscaray</groupId>
<artifactId>MicroservicesClassroomExample</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>ConfigServer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
Notice the spring-cloud-config-server
and spring-cloud-starter-consul-discovery
dependencies. These will allow this microservice to act as a config server and register itself with Consul. Within this new Maven module, we need to add the following class to start up the Config Server:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
From there, we need to create a Git repository with the configs we want to share and then have this config server retrieve it. To do this, first, create a new Git repository and host it on GitHub. Within the repository, we'll put a simple application.properties
file in the root directory configuring a debug logging level for our testing purposes:
logging.level.root=DEBUG
Once you committed the configuration and pushed it to GitHub, you'll need to create a GitHub personal access token with private repository access. With it, your config service can authenticate with GitHub to fetch configurations from the repository. To generate the token, go to Settings > Developer settings > Personal access tokens > Tokens (classic) > Generate new token > Generate new token (classic). Select the following permissions for the access token:
Now that you have the token, you can configure your config service's application.properties
to access the GitHub repo for the configs:
spring.application.name=config-server
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/john-amiscaray/MicroservicesClassroomConfigs
# Set the GitHub username and access token using environment variables
spring.cloud.config.server.git.username=${GITHUB_USER}
spring.cloud.config.server.git.password=${GITHUB_REPO_TOKEN}
Now, we can configure our other microservices' application.properties
to import configurations from our config server like so:
spring.config.import=configserver:
# Same as the spring.application.name property of our config service
spring.cloud.config.discovery.service-id=config-service
spring.cloud.config.discovery.enabled=true
Along with that, we also need to add a spring-cloud-starter-config
dependency in our root pom.xml for all of our services:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Now, we can start up Consul and our microservices and see that our Student Service, API Gateway, and Course Service applications have debug-level logging in the console:
Importing Microservice-Specific Configs
With our config server and our Git repository, we can also import configs for specific microservices. Within the GitHub repository containing our configs, let’s create new course-service and student-service directories with course-service.properties
and student-service.properties
files for our Course Service and Student Service respectively. We name these property files the exact same as the names of the services (listed in their respective application.properties
files) so that the config server will know what to do with them. After adding these new files, the directory structure would look like so:
Within each properties file, let’s configure the data sources for its respective microservice:
# student-service configuration
spring.datasource.url=jdbc:mysql://localhost:3306/student
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create-drop
# course-service configuration
spring.datasource.url=jdbc:mysql://localhost:3306/course
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create-drop
After that, we need to update the application.properties
file in our config server. These new properties specify subdirectories in our GitHub repository where configs can be:
spring.cloud.config.server.git.search-paths[0]=course-service
spring.cloud.config.server.git.search-paths[1]=student-service
With all that, we can commit to our GitHub repository, remove the configuration from the microservices' resources/application.properties
files, re-run the microservices, and see that these data source configurations are being imported from the config server!
Profile Specific Configuration
As you might expect, we can create Spring profile-specific configurations for all our microservices. As you'd normally do for a traditional Spring application, we can define property files with a "-dev" suffix for configurations to use in a dev profile:
# student-service-dev.properties
spring.datasource.url=jdbc:h2:mem:student
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create-drop
# course-service-dev.properties
spring.datasource.url=jdbc:h2:mem:course
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create-drop
# application-dev.properties
logging.level.root=DEBUG
# application.properties
logging.level.root=INFO
With these new files, our repository should look like so:
Once we run all our services with the dev profile, you'll see that our microservices have debug-level logging and connect to H2 databases.
Conclusion
In this guide, we learned about what config servers are in a microservice architecture and how we can implement them with Spring Cloud. To review the work we did, you can have a look at the final code on GitHub. Happy coding!