Exploring Spring Cloud Configuration Server in Microservices
In this article, we'll learn how to use the Spring Cloud Configuration server to centralize managing the configuration of our microservices.
Join the DZone community and get the full member experience.
Join For FreeIn this article, we will learn how to use the Spring Cloud Configuration server to centralize managing the configuration of our microservices. An increasing number of microservices typically come with an increasing number of configuration files that need to be managed and updated.
With the Spring Cloud Configuration server, we can place the configuration files for all our microservices in a central configuration repository that will make it much easier to handle them. Our microservices will be updated to retrieve their configuration from the configuration server at startup.
This article is an excerpt from my book, Microservices with Spring Boot and Spring Cloud, Second Edition – A step-by-step guide to creating and deploying production-quality microservices-based applications.
The Spring Cloud Configuration server (shortened to config server) will be added to the existing microservice landscape behind the edge server, in the same way as for the other microservices:
Figure 1: Adding a config server to the system landscape
When it comes to setting up a config server, there are a number of options to consider:
- Selecting a storage type for the configuration repository.
- Deciding on the initial client connection, either to the config server or to the discovery server.
- Securing the configuration, both against unauthorized access to the API and by avoiding storing sensitive information in plain text in the configuration repository.
Let's go through each option one by one and also introduce the API exposed by the config server.
Selecting Storage Type of the Configuration Repository
The config server supports the storing of configuration files in a number of different backends, for example:
- Git repository
- Local filesystem
- HashiCorp Vault
- A JDBC database
Other Spring projects have added extra backends for storing configuration, for example, the Spring Cloud AWS project, which has support for using either AWS Parameter Store or AWS Secrets Manager as backends.
In this section, we are going to use a local filesystem. To do this, we need to launch the config server with the Spring profile, native, enabled. The configuration repository location is specified by using the spring.cloud.config.server.native.searchLocations
property.
Deciding on the Initial Client Connection
By default, a client connects first to the config server to retrieve its configuration. Based on the configuration, it connects to the discovery server, Netflix Eureka in our case, to register itself. It is also possible to do this the other way around, that is, the client first connecting to the discovery server to find a config server instance and then connecting to the config server to get its configuration. There are pros and cons to both approaches.
Here, the clients will first connect to the config server. With this approach, it will be possible to store the configuration of the discovery server in the config server.
Note: One concern with connecting to the config server first is that the config server can become a single point of failure. If the clients connect first to a discovery server, such as Netflix Eureka, there can be multiple config server instances registered so that a single point of failure can be avoided.
Securing the Configuration
Configuration information will, in general, be handled as sensitive information. This means that we need to secure the configuration information both in transit and at rest. From a runtime perspective, the config server does not need to be exposed to the outside through the edge server. During development, however, it is useful to be able to access the API of the config server to check the configuration. In production environments, it is recommended to lock down external access to the config server.
Securing the Configuration in Transit
When the configuration information is asked for by a microservice, or anyone using the API of the config server, it will be protected against eavesdropping by the edge server since it already uses HTTPS.
To ensure that the API user is a known client, we will use HTTP Basic authentication. We can set up HTTP Basic authentication by using Spring Security in the config server and specifying the environment variables, SPRING_SECURITY_USER_NAME
and SPRING_SECURITY_USER_PASSWORD
, with the permitted credentials.
Securing the Configuration at Rest
To avoid a situation where someone with access to the configuration repository can steal sensitive information, such as passwords, the config server supports the encryption of configuration information when stored on a disk. The config server supports the use of both symmetric and asymmetric keys. Asymmetric keys are more secure but harder to manage.
In this chapter, we will use a symmetric key. The symmetric key is given to the config server at startup by specifying an environment variable, ENCRYPT_KEY
. The encrypted key is just a plain text string that needs to be protected in the same way as any sensitive information.
Introducing the Config Server API
The config server exposes a REST API that can be used by its clients to retrieve their configuration. In this chapter, we will use the following endpoints in the API:
/actuator
: The standard actuator endpoint is exposed by all microservices.
As always, these should be used with care. They are very useful during development but must be locked down before being used in production./encrypt and /decrypt
: Endpoints for encrypting and decrypting sensitive information. These must also be locked down before being used in production./{microservice}/{profile}
: Returns the configuration for the specified microservice and the specified Spring profile.
We will see some sample uses for the API when we try out the config server.
Setting up a Config Server
Setting up a config server on the basis of the decisions discussed is straightforward:
- Create a Spring Boot project using Spring Initializr.
- Add the dependencies,
spring-cloud-config-server
andspring-boot-starter-security
, to the Gradle build file,build.gradle
. - Add the annotation,
@EnableConfigServer
, to the application class,ConfigServerApplication
:
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
Add the configuration for the config server to the default property file,
application.yml
:
server.port: 8888
spring.cloud.config.server.native.searchLocations: file:${PWD}/config-repo
management.endpoint.health.show-details: "ALWAYS"
management.endpoints.web.exposure.include: "*"
logging.level.root: info
---
spring.config.activate.on-profile: docker
spring.cloud.config.server.native.searchLocations: file:/config-repo
The most important configuration is to specify where to find the configuration repository, indicated using the spring.cloud.config.server.native.searchLocations
property.
Add a routing rule to the edge server to make the API of the config server accessible from outside the microservice landscape.
Add a Dockerfile and a definition of the config server to the three Docker Compose files.
Externalize sensitive configuration parameters to the standard Docker Compose environment file,
.env
. The parameters are described below, in the Configuring the config server for use with the Docker section.
Add the config server to the common build file,
settings.gradle
:Javainclude ':spring-cloud:config-server'
The source code for the Spring Cloud Configuration server can be found in
$BOOK_HOME/Chapter12/spring-cloud/config-server
.
Now, let's look into how to set up the routing rule referred to in step 5 and how to configure the config server added in Docker Compose, as described in steps 6 and 7.
Setting up a Routing Rule in the Edge Server
To be able to access the API of the config server from outside the microservice landscape, we add a routing rule to the edge server. All requests to the edge server that begin with /config
will be routed to the config server with the following routing rule:
- id: config-server
uri: http://${app.config-server}:8888
predicates:
- Path=/config/**
filters:
- RewritePath=/config/(?<segment>.*), /$\{segment}
The RewritePath
filter in the routing rule will remove the leading part, /config
, from the incoming URL before it sends it to the config server.
The edge server is also configured to permit all requests to the config server, delegating the security checks to the config server. The following line is added to the SecurityConfig
class in the edge server:
.pathMatchers("/config/**").permitAll()
With this routing rule in place, we can use the API of the config server; for example, run the following command to ask for the configuration of the product
service when it uses the docker
Spring profile:
curl https://dev-usr:dev-pwd@localhost:8443/config/product/docker -ks | jq
We will run this command when we try out the config server later on.
Configuring the Config Server for Use With Docker
The Dockerfile of the config server looks the same as for the other microservices, except for the fact that it exposes port 8888
instead of port 8080
.
When it comes to adding the config server to the Docker Compose files, it looks a bit different from what we have seen for the other microservices:
config-server:
build: spring-cloud/config-server
mem_limit: 512m
environment:
- SPRING_PROFILES_ACTIVE=docker,native
- ENCRYPT_KEY=${CONFIG_SERVER_ENCRYPT_KEY}
- SPRING_SECURITY_USER_NAME=${CONFIG_SERVER_USR}
- SPRING_SECURITY_USER_PASSWORD=${CONFIG_SERVER_PWD}
volumes:
- $PWD/config-repo:/config-repo
Here are the explanations for the preceding source code:
- The Spring profile,
native
, is added to signal to the config server that the config repository is based on local files. - The environment variable
ENCRYPT_KEY
is used to specify the symmetric encryption key that will be used by the config server to encrypt and decrypt sensitive configuration information. - The environment variables
SPRING_SECURITY_USER_NAME
andSPRING_SECURITY_USER_PASSWORD
are used to specify the credentials to be used for protecting the APIs using basic HTTP authentication. - The volume declaration will make the
config-repo
folder accessible in the Docker container at/config-repo
.
The values of the three preceding environment variables, marked in the Docker Compose file with ${...}
, are fetched by Docker Compose from the .env
file:
CONFIG_SERVER_ENCRYPT_KEY=my-very-secure-encrypt-key
CONFIG_SERVER_USR=dev-usr
CONFIG_SERVER_PWD=dev-pwd
The information stored in the .env
file, that is, the username, password, and encryption key, is sensitive and must be protected if used for something other than development and testing. Also, note that losing the encryption key will lead to a situation where the encrypted information in the config repository cannot be decrypted!
Summary
In this article, we have seen how we can use the Spring Cloud Configuration server to centralize managing the configuration of our microservices. We can place the configuration files in a common configuration repository and share common configurations in a single configuration file while keeping microservice-specific configuration in microservice-specific configuration files. The microservices have been updated to retrieve their configuration from the config server at startup and are configured to handle temporary outages while retrieving their configuration from the config server. Microservices with Spring Boot and Spring Cloud, Second Edition
Published at DZone with permission of Magnus Larsson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments