Spring Cloud Config (Part 2): Git Backend
Now that you've gotten started with Spring Cloud Config, let's see how you can tie a Git backend to your app and the architectural considerations involved.
Join the DZone community and get the full member experience.
Join For FreeAfter the first part of this series, where we looked at a Spring Cloud Config introduction, in this post, we’ll look at a concrete implementation with a Git backend and the different options available using this backend.
Introduction
As we saw previously, one of the factors in the Twelve Factor App Manifesto was to move configuration out of our application. There are quite a few different ways of doing this, but here are some other concerns you need to think about when choosing a tool to manage your configuration:
- Auditing changes: should be easy to see the history of what changes were done by whom and when they were applied.
- Approval of changes: a lot of production issues are caused by the wrong configuration. Before making a configuration change, ideally, someone should take another look at it.
- Resiliency: What happens if the configuration tool is not available?
- Synchronization: How do the applications know that there is a change in the configuration?
Looking at the bullet points above, most of them describe a Version Control System, which is what we use to store the source code of our applications. The same way you sometimes want to know who changed some code in a Java class and why, you should be able to find out who changed some timeout configuration and why, so why not use a VCS for your configuration too?
That is exactly what using Git as a Spring Cloud Config backend gives you. First class control over your configuration changes. If you mix Git’s capabilities with something like Pull Requests, you have got a great workflow to have better visibility over the configuration changes applied to your environment, as well as better tools to figure out if something in the environment has changed, who did the change and a description of the reason for that change.
Using Git as the Backend
When using Git as your Spring Cloud Config backend, there are three main libraries we need to talk about:
spring-cloud-context
: As we saw in the previous post, Spring Cloud introduced the concept of abootstrap
application context, which uses aPropertySourceLocator
to load remote configuration. This interface is the entry point to the different backend implementations, and it lives in this library, which is included in any Spring Cloud application. It also contains theRefreshEndpoint
, which will allow us to trigger a refresh of the application context by invokingPOST /refresh
.spring-cloud-config-server
: This is the default backend implementation for Spring Cloud Config. It adds aPropertySourceLocator
, which will use one or moreEnvironmentRepository
that talk to different systems. These repositories are the ones responsible for resolving theEnvironment
that applies for a specificapplication
,profile
, andlabel
. There are a few different implementations ofEnvironmentRepository
that can connect to Git, SVN, or Vault.spring-cloud-config-client
: This library is used when you deploy the Spring Cloud Config Server into its own application. When the server is independent, you need a client which will connect to it through HTTP. That is exactly what this library does, it adds aPropertySourceLocator
that will call the server to load up the applicable configuration.
Structure of the Git Configuration Repository
If you have used Spring Boot before, you are probably familiar with how it loads configuration. The main way you usually manage your configuration is by adding some application-{profile}.properties
or application-{profile}.yml
, having the environment-specific properties in different profile files. For example, if you have a UAT environment and a PROD environment, you could have something like the tree below, where the profile-specific files contain configuration for that specific environment, and the application.properties
file contains configuration properties that apply to all environments.
$ tree
.
├── application-PROD.properties
├── application-UAT.properties
└── application.properties
With Spring Cloud Config, this property structures varies slightly. Since you will most likely use a Git configuration repository to store configuration for several applications, the naming structure needs to support it. The way this is supported is by naming the files after the application names, so if in the previous example, your application was named (using spring.application.name
) as StandaloneGitFirstClient
and you also had another application named StandaloneGitSecondClient
, then your property files in your configuration repository would look something like this:
$ tree
.
├── StandaloneGitFirstClient-PROD.properties
├── StandaloneGitFirstClient-UAT.properties
├── StandaloneGitFirstClient.properties
├── StandaloneGitSecondClient-PROD.properties
├── StandaloneGitSecondClient-UAT.properties
├── StandaloneGitSecondClient.properties
└── application.properties
Looking at the tree, we now group the .properties
very similarly, but instead of just using a generic application-
prefix, we use the application name.
You have probably noticed that there is still an application.properties
file in there. If this file exists, its properties will apply to all applications in all environments. You can also have an application-UAT.properties
, which would apply to all applications in the UAT environment, etc.
Spring Cloud Config also gives you options to have different directories for each application, or even different repositories. If you want more information on the different options available, check out the Reference Documentation.
Architectural Options
As we described earlier, the Spring Cloud Config Server is the library that communicates with Git, and once the repository is cloned, it figures out, based on the different parameters, which configuration properties apply.
This library can be used in two different ways — having a central application that will use this library and getting all client applications to communicate with this server application, or including the library into all your client applications. Let’s look at these options in more detail.
Standalone Config Server Setup
The first option we are going to look at is the more traditional one where we deploy a separate application which manages the configuration for us. In the diagram below you can see what it looks like:
As you can see, the Spring Cloud Config Server lives in its own application. All this application does is clone Git repositories, listen for requests from client applications asking for configuration, and return the applicable configuration for those requests.
The client applications themselves just include the RefreshEndpoint
we described earlier, as well as the Spring Cloud Config Client, which will run in the bootstrap phase, calling the server for the applicable configuration passing the name, profile and label. This last parameter can be a specific Git branch or commit.
Implementation Details
In order to use this setup, assuming that your applications are already Spring Boot apps, you should do at least the following things:
- Your client applications will need to import the Spring Cloud Config Client maven dependency.
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
- Define a file named
bootstrap.properties
in the root of your classpath, which will need to include the following properties:#The name of the application spring.application.name: StandaloneGitFirstClient #The URI of your Spring Cloud Config Server spring.cloud.config.uri: http://localhost:8888
Thisbootstrap.properties
file defines the configuration that is used in the bootstrap phase of your application startup. Since the remote configuration is loaded before the main application context starts up, we need a different file to define the configuration that applies for this specific part of the process. Usually, this file just contains properties for Spring Cloud Config concerns. - In your server application, import the Spring Cloud Config Server Maven dependency:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
- Configure your server application with the appropriate Git repositories by editing the
application.properties
.# The URI of the Git Repository where the configuration is stored spring.cloud.config.server.git.uri: https://github.com/erecarte/blog-spring-cloud-config-configuration.git # The port on which this application runs server.port: 8888
The reason this application updates theapplication.properties
and not thebootstrap.properties
is because the server does not need to use the Git configuration for itself, it just uses it to return it to other applications, so no bootstrap configuration is needed. - Annotate your main application class in the server with
@EnableConfigServer
:@SpringBootApplication @EnableConfigServer public class StandaloneGitServerApplication { public static void main(String[] args) { SpringApplication.run(StandaloneGitServerApplication.class); } }
After you have followed those steps, you should first start your server application, and then start the clients. When the clients are starting up, the first log entries you’ll see look like:
2017-08-03 14:07:57.708 INFO 74429 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f6ee6e4: startup date [Thu Aug 03 14:07:57 BST 2017]; root of context hierarchy
2017-08-03 14:07:57.904 INFO 74429 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$ed33cade] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.2.RELEASE)
2017-08-03 14:07:58.142 INFO 74429 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:8888
2017-08-03 14:08:01.631 INFO 74429 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=StandaloneGitFirstClient, profiles=[default], label=null, version=null, state=null
If you look at the last two lines of the snippet, right before doing anything, it first fetches the remote configuration from the Spring Cloud Config Server, and only then does it start loading up the application context.
If you want to look at a standalone setup example in a bit more detail, the code is available in GitHub. This sample repository contains two client applications and the server. The configuration for them is in this other repository.
Advantages
- Only one application communicates with Git, which, depending on your Architecture, might be a security advantage.
- Your applications just need to include a thin client that talks over HTTP.
Disadvantages
- Not as resilient. If your server application goes down, the client applications will not be able to start up, or will start up without configuration, having to rely on default values. You can configure whether your application should start up or not after this failure with the property
spring.cloud.config.fail-fast
, which defaults tofalse
. - The server becomes another application to be deployed and maintained.
Embedded Config Server Setup
Now let’s look at the other setup. Instead of having a different application running the server, we could just embed the server inside our client applications. The following diagram illustrates what this would look like:
You can see that the diagram is slightly simpler. The client applications don’t use the Spring Cloud Config Client anymore because they don’t need to talk through HTTP to the server, they have the server inside the application instead.
Implementation Details
These are the steps you’ll need to follow to embed the server:
- Your client applications will need to import the Spring Cloud Config Server Maven dependency:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
- Configure your client applications with the appropriate Git repositories in the
bootstrap.properties
:#The name of the application spring.application.name: StandaloneGitFirstClient # The URI of the Git Repository where the configuration is stored spring.cloud.config.server.git.uri: https://github.com/erecarte/blog-spring-cloud-config-configuration.git # Whether the Spring Cloud Config Server should configure iteslf with the loaded configuration. spring.cloud.config.server.bootstrap: true
Again, notice how now this configuration goes into thebootstrap.properties
and not theapplication.properties
. Notice also how we had to define the propertyspring.cloud.config.server.bootstrap=true
to force the library to bootstrap itself. This is because now we need the embedded Spring Cloud Config Server to configure itself from the loaded configuration in the bootstrap phase. - Annotate your main application classes with
@EnableConfigServer
:@SpringBootApplication @EnableConfigServer public class EmbeddedGitFirstClientApplication { public static void main(String[] args) { SpringApplication.run(EmbeddedGitFirstClientApplication.class); } }
Advantages
- Simplified infrastructure.
- The application is more resilient. You don’t depend on the intermediate server being up, and also the fact that the server library clones the repository on to the disk means it can use the local version if the pull fails on startup. If the Git server is down, your application will start up with the last pulled configuration, reducing the chances of the configuration being wrong and having to use defaults.
Disadvantages
- The first application startup will be slower since it needs to check out the full Git repository. This could be significant if you have one configuration repository for your full company and you have hundreds or thousands of services. For this reason, I’d probably recommend have configuration repositories per team or area in your organization.
- All your applications will communicate with the Git server. This is especially problematic if you use the same Git server for your application code and your configuration code, since it widens the security threat.
Detecting Configuration Changes
We already saw a quick overview of how you can refresh your application’s context in the previous post, but in this section, we’ll take a look at an example with a real implementation of how this could be done.
Spring Cloud provides a solution to detecting configuration changes in your applications based on a couple of libraries:
spring-cloud-config-monitor
: This library adds an endpoint to your application that understands webhooks from a few different Git server providers such as GitHub or Bitbucket. If you include this dependency, it will add a/monitor
endpoint. You then just need to configure the webhook in the Git repository to point wherever this endpoint is deployed. Once this endpoint is called, it will parse the payload to understand what has changed, and it will then use the next library to tell the client applications that there is a change in configuration, so they should refresh the application context. You can find out more information about this library in this blog post.spring-cloud-bus
: This is a simple library that Spring provides to communicate global events to other applications through some message brokers. It’s mostly used for this use case, where you need to send anRefreshRemoteApplicationEvent
event to all/some applications telling them that they should refresh their application context. It integrates with either RabbitMQ or Kafka as the message brokers.
Let’s take a look at an architectural overview of the solution:
There are a few changes from the previous diagrams we were looking at:
- The Server Application now has the Spring Cloud Config Monitor component, which will listen for requests to
POST /monitor
. - There is a RabbitMQ broker now.
- The Client Application also integrates with Spring Cloud Bus, which adds a
RefreshListener
, which will listen forRefreshRemoteApplicationEvent
. If this event happens, it will trigger a context refresh the same way theRefreshEndpoint
does.
In this example, I’ve only considered the standalone setup discussed earlier. For the embedded setup, you would have to have a custom solution, since there is no centralized server that will manage all the configuration to which you can add the /monitor
endpoint.
Conclusion
In this post, we have taken an in-depth look at how you can use Git to store the configuration for your applications and use Spring Cloud Config to connect to Git and manage the configuration for you.
We’ve also seen some different options when designing your solution from an infrastructure point of view, as well as one implementation for automatically updating the application when there is a change in the configuration.
Having gone through Git, the next post will look at a different backend where we can manage our configuration: Zookeeper.
Published at DZone with permission of Enrique Recarte. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments