Centralized Configuration of Spring Boot Applications Using Spring Cloud Config in SAP Cloud Platform
Spring framework and projects associated with it play key role in Java community and enterprise world nowadays – and SAP Cloud Platform (SCP) is well-equipped for deployment and execution of Spring based applications. Matthias Steiner did great job in providing detailed guidance and hands-on examples on how sample Spring Boot application can be developed and deployed to SCP – I strongly recommend going through his blog series on this topic (part 1 and part 2) unless you are already familiar with the concept and required steps, because material presented in this blog heavily relies on understanding of Spring Boot application development concepts.
Popularization of microservices architecture as opposite to classic monolithic architecture, and evolution of Spring ecosystem that goes hand in hand with this trend and provides necessary tooling (such as Spring Cloud), together with platform services that are already provided by SCP, made SCP a good candidate to be looked at when assessing options for building enterprise applications on top of microservices.
Large diversity and number of running microservices in highly distributed landscape commonly causes operational overhead in their management and configuration faced by development and service operations teams. The blog doesn’t aim looking into all or even most commonly faced, but is focused on the problem of provisioning centralized configuration capabilities that can be enabled using SCP.
Why central configuration is so crucial in microservices environment? It is for sure not a good idea to bundle configuration of an application together with its implementation (for example, in Spring based application, putting all configuration required by the application, into application.properties file and embedding it into application build) – to allow separation of application implementation and application configuration, application properties are commonly externalized and placed in configuration files that can be accessed and modified without necessity of re-building and re-deploying the whole application. This approach works well to certain extent, but along with growth of fleet of microservices, lack of scalability offered by the approach turns into its bottleneck and can cause significant increase in support efforts and potential inconsistencies in rolled out application configurations.
In order to address this problem, some frameworks provide tools that can help in managing applications’ configuration / properties centrally. In Spring ecosystem, this can be achieved using Spring Cloud’s Config components. The idea here is that configuration properties that are required by applications, are all stored centrally on the dedicated configuration server, and client applications acquire required configuration properties during startup (to be more precise, as one of steps of application bootstrapping procedure) from the configuration server. This allows better consistency as there is no more need in maintaining and keeping up-to-date local configuration files in applications that can be potentially distributed across several cluster nodes and when application is scaled out to additional nodes, as well as better infrastructure scalability as configuration properties of large number of applications can be checked, modified and audited centrally.
Another feature that makes the described approach and setup curious and efficient, is ability to refresh client application’s configuration without need of application restart, which makes it extremely useful when it comes to the question of application availability and application downtime minimization.
Below we will have a look into how basic model supporting centralized application configuration, can be deployed leveraging SCP services. You might want to look into further examples of how this concept can be applied in practice, as there are various tutorials and extensive documentation on this topic at SCP agnostic and Spring centric Internet resources.
Overview and notes
High level overview and component diagram that depicts involved components employed in the demo, are illustrated below:
Applications 1, 2, …, N are Spring Boot client applications that represent variety of business applications, they implement certain business functions and expose microservices. Each of Spring Boot applications that needs to be enabled for centralized externalization of its configuration properties, is bundled with extra component – Spring Cloud Config Client – that allows an application to query the configuration server and poll required configuration properties from it. In the demo, the application is deployed to Tomcat Web Container provided by SCP.
Configuration server is a specific type of Spring Boot application that is equipped with Spring Cloud Config Server component, which enables it to expose required services to other applications and allow them to query and poll configuration properties via endpoints exposed by the configuration server services, as well as communicate with the repository that persists configuration properties during process of fetching configuration properties from configuration files available in the repository. Given the configuration server is yet another Spring Boot application, it can provide other services apart from the configuration server services (for example, sophisticated user interface to access and maintain configuration files, etc.) and host other Spring Boot components, but in sake of simplification, I set it up as the configuration server with no extra service capabilities. In the demo, the configuration server is deployed to Tomcat Web Container provided by SCP.
Repository provides required persistence layer to the configuration server – it persists configuration files that are pushed to it and that contain configuration properties required by other applications, and can optionally provide version control and audit capabilities. The configuration server accesses repository when it services configuration properties poll requests coming from client applications, fetches corresponding configuration file and returns properties maintained in it, to the calling client application (unless caching is enabled in the configuration server to reduce number of calls to repository). In the demo, I use Git managed repository hosted by SCP. Generally speaking, Spring Cloud Config Server component supports several different technologies for repositories that it can communicate to. Git has already become one of de-facto standards in the field of distributed version control systems, and it provides fairly optimal combination of required capabilities for the demo, which made it to be a reasonable choice. Although it is possible to make use of any arbitrary Git managed repository, I set Git repository hosted by SCP in order to leverage benefit of setting the entire infrastructure on basis of the same platform.
In sake of simplicity and in order to stay focused on the blog’s subject, the described demo doesn’t provide outlook neither makes use of other Spring Boot components (some of them – such as Spring Security – being essential when designing production-ready applications), except Spring Boot Actuator (used to acquire information on application health and its environment) and Spring Boot DevTools (used to facilitate rapid development by enabling automatic restart of the developed application when any of files included in the classpath, is changed – consequently, enabling changes to application’s source code to take effect at runtime by immediate restart of the application upon saving these changes). As a result, neither the client application, nor the configuration server used in the demo, enforce any authentication methods to be applied when calling exposed endpoints.
The developed client application follows fail-safe design – here, meaning that if required service (configuration server) is not available at application startup, it will default required properties and continue with execution. Given this application is provided for demonstration purposes, this approach is reasonable, but in real-life applications, it shall be thoroughly considered what design approach is more suitable for the developed application – fail-safe (application tries to startup and run even if it comes across certain types of failures) or fail-fast (application notifies about failure immediately and terminates normal processing – here, for example, if configuration server is not available at application startup, an application throws startup exception and terminates startup procedure).
Another simplification that is applied in the demo, is environments layout. In production environments, we would commonly face multi-tier setup (such as 2-tier – Development / Production, or 3-tier – Develop / Quality Assurance / Production, or more advanced setups), which implies usage of environment-specific application profiles, hence, leading to maintenance of configuration files that are environment specific. This principle allows us to deploy the same application across used environments, but instruct it to use different properties values, depending on environment to which the application is deployed. Demo relies on profile agnostic configuration parameterization – meaning, the client application identifies itself when issuing configuration properties polling request to the configuration server, but doesn’t query for profile specific configuration properties.
Development of involved Spring Boot applications has been carried in Eclipse IDE extended with Spring related plugins, dependencies management and build has been conducted using Gradle. Applications’ source code, configuration properties and Gradle build scripts can be found in GitHub repository.
Create Git repository, so that it will provide central hosting for applications’ configuration files. In the demo, I create repository named ‘config’ (take a note of this as repository name will be a part of the generated repository URL):
Take a note of generated repository URL as it will be required when setting up configuration server:
After the remote central repository is set, it has to be cloned to a local machine, so that configuration files and changes to them can be staged and committed to a local repository and then pushed to the central repository, following general practices of working with Git.
Create Spring Boot application project. I used Spring Boot Starter project wizard – alternatively, Spring Initializr tool can be utilized.
Add dependency on Spring Cloud Config Server (artifact ID ‘spring-cloud-config-server’ of group ID ‘org.springframework.cloud’) to add required config server components to the built application:
Add annotation ‘@EnableConfigServer’ to the application’s main class (the class implementing method main()) to enable config server nature for it.
After this is done, repository that holds applications’ configuration files has to be specified in configuration properties of the configuration server – this is where we need to make use of the earlier obtained URL of the Git repository, as well as maintain authentication credentials (here, SCP’s user account and its password, if basic authentication method is used):
Development of the configuration server is now completed. Ensure that build script is configured properly (for example, all required project properties are maintained, required dependencies are specified, embedded Tomcat container and logging framework are excluded from the build, etc.) and assemble it as a web application into WAR file by running corresponding Gradle task (for example, clean assemble).
After build step is completed, deploy the assembled WAR file to SCP as Java application. When deploying, ensure that selected runtime is ‘Java Web Tomcat 8’ and JVM version is ‘JRE 8’. In the demo, I create application named ‘config’ (take a note of this as application name will be a part of the generated application URL):
When deployment is completed, as an optional step, display name of the application can be adjusted to provide more user-friendly name that is displayed in SCP administrator cockpit.
Finally, the configuration server application can be started. After application startup is completed, take a note of generated application URL as it will be required when setting up demo client application:
Create another Spring Boot application project.
Add dependency on Spring Cloud Client (artifact ID ‘spring-cloud-starter-config’ of group ID ‘org.springframework.cloud’) to add required config client components to the built application:
In sake of demonstration, I create a simple Spring MVC application that exposes an endpoint which can be accessed by sending corresponding HTTP requests to it.
Layout of involved classes is presented below:
Controller bean provides implementation accompanied by respective service bean, such that for GET request sent to the URI ‘/messages/<name>’, where <name> is URL path parameter containing some arbitrary String value, the response containing identifier (dynamic part – UUID generated by the demo application for every received request), name (static part inherited from request – the same name value as contained in the submitted request) and comment (static part originating from application configuration – value that comes as a fixed value from application’s configuration property ‘demo.comment’) is produced in JSON format and sent back to the caller client:
In the demo, I use Postman to generate HTTP requests – alternatively, any tool that is capable of producing HTTP GET request (browser, other HTTP clients), can be utilized.
Attention shall be paid to adding annotation ‘@RefreshScope’ to corresponding bean classes where properties refreshment using config client shall be enabled – in the demo, this is enabled for the service bean that is used by controller:
Another important aspect is the way how configuration properties are defined in the application. Since we don’t maintain them directly in locally accessible configuration file ‘application.properties’ and want to externalize them, corresponding used properties are defined in respective configuration properties’ components – such as the one provided below, that specifically looks for properties which name starts from the prefix ‘demo’. Note post-construction logic that has been added to this component to enable fail-safe mode for it by defaulting the required property value in case the actual property cannot be obtained from the configuration server (for example, if configuration server application is stopped or incorrect URL for it is maintained in client application). It shall also be noted that although the application includes a dedicated component for configuration properties, that component doesn’t need to be annotated with ‘@RefreshScope’ (as opposite to a service bean), since Spring framework auto-enables refreshment of general configuration properties’ components:
The remaining step is to provide application with instructions regarding location from which it can obtain its configuration properties. This is done in application’s bootstrap configuration file (‘bootstrap.properties’) – it is not created in the project by default, hence, shall be created manually. Note that apart from configuration server URL, it shall contain information of application self-identification – application name (in the demo, it is named ‘demo-app’) that will be used by configuration server when searching for relevant configuration file:
Development of the demo application is now completed. Build and deploy it to SCP using same type of Java runtime as for earlier deployed configuration server. In the demo, I create application named ‘demo’ (take a note of this as application name will be a part of the generated application URL).
Finally, the demo application can be started. After application startup is completed, take a note of generated application URL as it will be required when calling it in subsequent examples:
Configuration server provides convenient service endpoint that can be used to check configuration properties that can be retrieved by it for a given application and, optionally, its profile. I will use the basic pattern of the URI path to check available configuration properties – which is ‘/<application name>/'<profile>/<label> (consult Spring Cloud Config Server configuration to explore other URI notation alternatives). Given the demo application is named ‘demo-app’ and doesn’t use application profiles (hence, its profile is defaulted to ‘default’), as well as considering that configuration files are pushed to a master branch of the Git repository, URL to acquire corresponding configuration properties from the configuration server will be: ‘<Configuration server URL>/demo-app/default/master’:
As it can be seen, the configuration server is not yet aware of configuration properties for application ‘demo-app’, as the Git repository is empty and doesn’t contain any configuration files.
Execute a query (HTTP GET) to the demo application’s custom developed endpoint – as it can be seen, so far the application doesn’t populate JSON attribute ‘comment’:
Now let’s create the configuration file named ‘demo-app.properties’ (generally speaking, ‘<application name>.properties’) and add configuration property that is required by demo client application (‘demo.comment’) to it:
Commit it and push it to the remote (SCP hosted) Git repository. Now repository contains the configuration file for the application ‘demo-app’.
Query the configuration server again to check available configuration properties for the application ‘demo-app’ (using the same URL as above) – as it can be seen, the configuration server successfully recognized the uploaded configuration file and configuration property maintained in it:
Execute a query to the demo application’s custom developed endpoint again – as it can be seen, although configuration server is now aware of configuration properties for the application ‘demo-app’, the client application hasn’t yet acquired them:
This happens because the demo client application has to refresh its configuration properties, which can be done by sending HTTP POST request to its URI ‘/application/refresh’, which is exposed by Spring Cloud Config Client component:
Response enumerates configuration properties that have been refreshed. Before proceeding to re-execution of the query to the endpoint, it is alternatively possible to check current application’s environment properties (by sending HTTP GET request to its URI ‘/application/env’) and evidence that the application is now aware of the configuration property ‘demo.comment’:
Execute a query to the demo application’s custom developed endpoint once again – as it can be seen now, configuration property polled from the configuration server, took effect and got reflected in the response:
Note that the demo application has never been restarted after its initial startup – polling of configuration properties and making use of them within the application were performed online, without any application downtime.
Configuration properties file can now be modified as many times as required – as soon as changes to it get pushed to the Git repository hosted by SCP, it gets immediately recognized by the configuration server, and the next time the demo application polls configuration properties, they take effect in the application.
Alternatives and advanced options
As it can was observed, refreshment of client application configuration properties has been triggered manually, by calling corresponding endpoint (‘/refresh’). This is relatively basic approach (it is built-in functionality and doesn’t require any further additional services) that is good to start with, but it is not the only one that can be employed. More advanced options involve automatic refreshment – and here we have variety of approaches to select from.
Client application is configured to poll configuration properties from the specified configuration server.
- Manual execution (as illustrated in the demo)
Advantage: controlled refreshment of application configuration.
Disadvantage: necessity of manual interference in order to refresh application configuration.
- Automatic execution of scheduled task by client application
The approach makes use of the same underlying functionality of Spring Cloud Config Client as it has been done in case of manual refreshment execution (namely, invocation of a bean for RefreshEndpointAutoConfiguration, that triggers polling of configuration properties from the configuration server), and can be achieved by implementing Spring scheduled task that is periodically executed (refer to documentation on annotation ‘@Scheduled’ for more information) and makes invocation of the mentioned bean’s method refresh().
Advantage: refreshment of application configuration is automated and doesn’t require manual interference.
Disadvantage: in case of large number of client applications, and especially if combined with frequent configuration properties polling requests, can cause severe overload of the configuration server and in extreme cases, provoke its denial of service, unless the configuration server is properly sized.
- Usage of Git hooks (or equivalent, if available, in case other type of repository is used)
The approach makes use of Git hooks, that can be used to execute certain actions / scripts (such as send broadcast message or notify system / user) for specific available Git events. For example, hook implemented for server-side post-receive event, can be utilized to produce notification about update of configuration file(s), for which commit has been pushed and handled by Git server. Commonly, notification is implemented using message queuing integration pattern – such that implementation attached to the appropriate Git hook, publishes a message to message broker’s topic / exchange, which is then broadcasted to corresponding subscribers (client applications) that consume the message and refresh their configuration.
Advantage: refreshment of application configuration can be performed in near real time mode – delay between update of configuration files and refreshment of application configuration is eliminated; no overload of the configuration server due to large number of configuration properties polling requests, since polling that is initiated by client applications, is replaced with notifications published by the configuration server, leading to only impacted applications to trigger configuration properties polling requests.
Disadvantage: relative complexity of implementation and involvement of additional components (such as message broker supporting publish/subscribe pattern). In case of setting the entire infrastructure purely by means of SCP, such capability can be achieved by means of RabbitMQ – AMQP message broker that is offered as a part of SCP.
Configuration server registers itself at a dedicated discovery server, allowing client applications to acquire configuration properties indirectly, by means of involvement of service discovery process.
- Usage of Discovery server
The approach requires rethinking of the way how different applications discover each other and get information on each other’s location – instead of setting called service’s destination (for example, in form of URLs in configuration properties), each application (configuration server being one of examples of such applications) registers itself in the central discovery server, that is accessed by other applications when they need to make use of a specific service.
Advantage: simplification and unification of client applications’ configuration combined with more flexibility in part of building highly distributed environments, because any client application has to possess information about location of the discovery server and service that needs to be accessed, without knowledge of actual specific location of the queried service.
Disadvantage: relative complexity of implementation and involvement of additional components (discovery server).