Photo by Mpho Mojapelo on Unsplash

Dynamic App Configuration: Inject configuration at “run time” using Spring Boot and Docker

Abdelkarim El Moussaoui
NEW IT Engineering
Published in
5 min readMar 16, 2020

--

Enterprise applications have become easy to implement, since great technologies like Spring Boot & Co. provide good and stable integrations to the most available frameworks. However, it can get a little weird when it comes to automatic deployment: applications should only contain the core business logic and preferably everything concerning configuration have to be kept outside of the application. In other words: the application should not contain any hardcoded configuration, much less credentials.

Spring Boot properties

The Spring Boot framework provides good ways to handle application configuration using the annotation processing (i.e. @ConditionalOnProperty, @PropertySource, @Configuration, etc.). In this manner, multiple configuration sources (e.g. properties files) could be used to set up parameters across the whole application. The values of these properties are defined in an application.yml/application.properties file. Furthermore, Spring Boot allows us to use different profiles for different purposes, such as separating development properties from production, or providing special test properties for unit and integration tests.

An example application.yml file.

As the image above shows, the properties are defined in a tree-like architectured document (in this case the YML format). The properties tree “random” could be referred to in a class like:

An example Java class to map properties from the application.yml file above.

The annotation @ConfigurationProperties will automatically bind any property with the prefix “random” in the application.yml file to the appropriate field in the RandomProperties.java class.

Situation

Using the Spring Boot property concept, we could easily map our properties to Java classes. But what about if we are dealing with configuration which have to be set at deployment time and not at build time? The answer is to find a suitable way to integrate Spring Boot configuration processing with some other secure and reliable mechanisms to provide dynamic configuration (i.e. or secrets) appliance at application deployment time.

Spring Boot environment scan strategy

The Spring Boot framework comes with some useful built-in environment variables. However, we may want to define our customized variables to be used within the application. To look up an environment variable, spring boot tries firstly to find it within the application context. If it was not found, it checks then the next overlying environment until it ends at the runtime environment (i.e. JVM or the container/system running the JVM). If then a value was found, it’s returned. Otherwise the default value (if available) or null is returned.

For example, Spring Boot will try to find a value for: “randomValue1” using the above mentioned environment scan strategy. If a value was found, then Spring Boot will map it to the property “randomProperty1”. Otherwise the default value of this property will be returned (in this case “devRandomValue1”).

Spring Boot environment scan strategy.

Docker as an environment gateway

As we have seen, Spring Boot is very flexible at the environment scanning level. But how could we pass the right configuration at runtime?

Docker has become the most and widest used container platform: It lets us package, ship and run applications independent from the underlying host system in containers. A Docker container is an encapsulated environment to run applications based on its docker image (i.e. the image is like a snapshot of the application including all its dependencies and resources).

Docker function model. *Source

Since our application is served as a Docker image, we could spawn multiple containers and provide each container with specific configuration using the Docker environment variables.

Creating containers with different configuration at run time.

To create the Docker image, we need a Dockerfile (see the picture below). After the image is created, we could then create our containers based on it. In the example above we created three containers: Container 1 and Container 3 have been fed with some configuration variables at run time. Whereas Container 2 has got no configuration.

Following you can see how we have created each container:

· Container 1: $ docker run -e “randomValue1=a random string” -e “randomValue2=12” -e “randomValue3=false” sampleImage

· Container 2: $ docker run sampleImage

· Container 3: $ docker run -e “randomValue1=another random string” -e “randomValue2=0” sampleImage

All the variables passed using the Docker -e command have to be previously defined as ENV variables in the Dockerfile or the script’s entry point:

An example Dockerfile.

Please note: If we need to inject environment variables at container runtime, we have to define them as ENV variables. otherwise, if we desire to embed some properties in the Docker image itself, we have then to mark them with ARG instead.

ARG vs ENV assigned variables.

Conclusion

The presented architecture above lets the developers concentrate on implementing the desired business logic without caring about how to pass the needed configuration at deployment time. On the one hand, developers could use hardcoded configuration parameters for development or testing purposes only. On the other hand, DevOps people could deploy the application using any deployment pipeline (i.e. Jenkins, GitlabCI, etc.) and provide the needed parameters just at runtime using the Docker environment variables. The proposed solution works in general for all kinds of parameters. We will see in a next post how to apply this solution pattern for secure credentials.

--

--