Spring Cloud Config server is a Java-based application that provides support for externalized configuration in a distributed system. A number of external configuration sources are supported like the local file system, Git or HashiCorp Vault. For this post, I use a Github repo as my configuration source. Centralizing configuration on a source control repo has several advantages, especially in a micro services architecture:

  • Config is externalized and separated from code. This ensures that the same code base (or build artifact) can be deployed to multiple different environments. Code should never change between deploys.
  • Configuration for multiple micro services is centralized in one location, which helps in managing configuration for the application as a whole.
  • Configuration itself is versioned and has a history.

Spring Cloud Config server offers a simple API for retreiving app configuration settings. Client libraries exist for numerous languages. For C#, Steeltoe offers integration with config server. It has a lot more to offer but that’s a topic for future posts.

Source code for this post can be found here.

The application

I’m working on a small demo application that should, when finished, show most Steeltoe features. In this first post, I focus on configuration. The demo app itself is based on an API owned by the Dutch Ministry of Foreign Affairs that issues travel advisories. This API is divided into two parts:

  • a paged list of countries where each country has a reference to a detailed country document
  • per country a document with a detailed travel advisory (our southern neigbors Belgium, for example)

The application fetches and caches this data to represent it to clients in a more accessible format. Furthermore, it should detect travel advisory changes to be able to notify clients of the API that a travel advisory for a specific country has changed.

The application starts with a periodic fetch of the list of countries. Fetching this list is implemented by the ‘world fetcher’ micro service. This service needs two configuration settings: the base url to fetch the data from and the polling interval. So let’s see how to configure Spring Cloud Config server to deliver these settings.

Spring Cloud Config server configuration

First of all we’re going to run Spring Cloud Config server locally. This is quite easy because someone has already packed everything inside a Docker container: hyness/spring-cloud-config-server. So we can do a docker pull hyness/spring-cloud-config-server and then we run the following command:

docker run -it \
    --name=spring-cloud-config-server \
    -p 8888:8888 \
    -e SPRING_CLOUD_CONFIG_SERVER_GIT_URI=https://github.com/rwwilden/steeltoe-demo-config \
    hyness/spring-cloud-config-server

So we give the Docker process a nice name: spring-cloud-config-server, map port 8888 and specify an environment variable with the name SPRING_CLOUD_CONFIG_SERVER_GIT_URI. This should point to a Git repo with configuration that can be read by Spring Cloud Config server. For the moment, the only configuration file there is worldfetch.yaml:

worldfetch.yaml view raw
baseDataUri: "https://opendata.nederlandwereldwijd.nl/v1/sources/nederlandwereldwijd/infotypes/traveladvice"
fetchInterval: "00:05:00"

If you have started the Docker container, you can run curl http://localhost:8888/worldfetch/development and you get back a nice JSON response with the two configured values.

Steeltoe

So we have a running config server that serves configuration from a Github repo. How do we get this configuration inside our ASP.NET Core micro service? The answer is Steeltoe. Steeltoe is a collection of libraries that allows interfacing from a .NET app with a number of Spring Cloud components (like config server).

First step is to configure the location of the config server. Since we’re still running only locally, this is http://localhost:8888 which we specify in appsettings.Development.json:

appsettings.Development.json view raw
1
2
3
4
5
6
7
8
{
  "spring": {
    "cloud": {
      "config": {
        "uri": "http://localhost:8888"
      }
    }
  },

Next step is adding the Steeltoe configuration provider in Program.cs:

Program.cs view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using Steeltoe.Extensions.Configuration.ConfigServer;

namespace worldfetch
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((webHostBuilderContext, configurationBuilder) => {
                    
                    var hostingEnvironment = webHostBuilderContext.HostingEnvironment;
                    configurationBuilder.AddConfigServer(hostingEnvironment.EnvironmentName);
                })
                .UseStartup<Startup>()
                .Build();
    }
}

Note I use the ConfigureAppConfiguration method, introduced in ASP.NET Core 2.0 and well documented here. On line 17 the config server is added as a configuration provider.

Next we need another configuration setting. Remember what we named our config file on Github: worldfetch.yaml. The Steeltoe configuration provider must know the name of our application so that it can collect the right configuration settings. This one we define in appsettings.json:

appsettings.json view raw
1
2
3
4
5
6
{
  "spring": {
    "application": {
      "name": "worldfetch"
    }
  },

Final step is to implement an options class to represent our settings so that we can inject them into other classes. This class is quite simple in our case because we have just two settings in worldfetch.yaml:

Lib/FetcherOptions.cs view raw
1
2
3
4
5
6
public class FetcherOptions
{
    public string BaseDataUri { get; set; }

    public TimeSpan FetchInterval { get; set; }
}

Configuration injection

Now all that is left to do is inject an IOptions<FetcherOptions> where we want to access the configuration settings from worldfetch.yaml and we’re done: configuration through Spring Cloud Config server from a Github repo.

What’s next?

We now have a small ASP.NET Core application that is configured via Spring Cloud Config server and fetches data from some URL. Next time we’re going to run all this in ‘the cloud’!