Spring Cloud Config Series Part 2: Git Backend

After the first part of the 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 the 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:

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:

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 which 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:

Standalone Config Setup

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, we 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:

  1. 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>
    
  2. 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 
    

    This bootstrap.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.

  3. 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>
    
  4. 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 the application.properties and not the bootstrap.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.

  5. 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 it starts loading up the application context.

If you want to look at an standalone setup example in a bit more detail, the code is available in Github. This sample repository contains two client applications and the server. They configuration for them is in this other repository.

Advantages

Disadvantages

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:

Standalone Config Setup

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 setps you’ll need to follow to embed the server:

  1. 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>
    
  2. 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 the bootstrap.properties and not the application.properties. Notice also how we had to define the property spring.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.

  3. Annotate your main application classes with @EnableConfigServer:
    @SpringBootApplication
    @EnableConfigServer
    public class EmbeddedGitFirstClientApplication {
       public static void main(String[] args) {
          SpringApplication.run(EmbeddedGitFirstClientApplication.class);
       }
    }
    

Advantages

Disadvantages

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:

Let’s take a look at an architectural overview of the solution:

Standalone Config Setup With Refresh

There are a few changes from the previous diagrams we were looking at:

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.

Tags