Externalized configuration in the overall architecture

Location of configuration is another challenge for developers. Configuration burning inside the code is surely not a good idea. For each change in configuration, the developer has to make a new release; also, if some configurations are the same in multiple microservices such as the database server IP, these kinds of properties are duplicated in all the services. So, a better idea is to keep the configuration out of the code base. To implement this idea, we share a common location between microservices for properties, DevOps people can mount a drive on all the microservice instances. Using the NFS drive for this is an option. For each environment, there will be a different NFS server and drive mounted.

If you are on AWS, then mounting an S3 bucket in the entire microservice instance runs another option. Bucket can have some hierarchy to give customized properties to any microservice. Each microservice can have a different folder name and property files in it. The normal convention to save the property files is s3://<bucketName>/<Environment_Name>/<microservice_name>.

In this type of a scenario, there will be no track of change in the configuration. To solve this, we can introduce any version control system to maintain the configuration. Developers can commit the configuration in this repository and CI/CD tool while deploying a microservice deploy; they also deploy these configurations on the common sharing path. By looking at the log of version control, one can identify which configuration went with any specific version of microservice. Tagging the configuration with every release of microservices is a key. History is maintained now from version control, but usually organizations have multiple environments, and each environment has its own properties. This problem can be addressed by having multiple branches or multiple repositories per environment. In this case, the CI/CD tool should be aware of which environment it will deploy to, and from that particular repository or branch, it will deploy the configurations.

As this book is dealing with Spring Boot, let's discuss how we can achieve this in Spring Boot. Spring Boot has a particular order to search for the properties mentioned in the code. The order is as follows:

  1. It looks for any command-line arguments.
  2. On second step it try to find properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
  3. Next step it try to find properties from JNDI attributes from default java:comp/env.
  1. On fourth try it will look from System.getProperties().
  2. Next it try to look into operating system specific environment variables.
  3. Then, From RandomValuePropertySources.
  4. Profile-specific application properties packaged inside/outside your JAR (application-{profile}.properties).
  5. Application properties inside/outside of your packaged JAR.
  6. The @PropertySource annotations.
  7. At Last spring boot try to find property values in properties specified using SpringApplication.setDefaultProperties.

We are not discussing all of the flow here, but readers are encouraged to read and try out the different ways mentioned here to try and find the best way to configure properties for their project. We are using @PropertySources in our sample project as well, so here is an example of how to use this annotation in code. Normally, this annotation is used in the configuration class, as follows:

@PropertySources({ @PropertySource("file:/home/user/microserviceA.props")}) 

In the preceding example, properties are loaded from the file whose path is mentioned as /home/user/microserviceA.props. Typically, this file can have a key-value-pair type of property defined that can be used in any of the beans in the application with a placeholder as follows:

@Value("${Connect.ip}") 
private String connectionIp; 

Using this statement, Spring looks into the earlier mentioned file and looks for the property like Connect.ip=xx.xx.xx.xx. This statement will take the value from property file into the connectionIp variable.

In this type of configuration, there is a problem of a fixed path mentioned in annotation. Using any environment variable, we can get rid of this static path. Let's suppose our configuration resides in /mnt/share/prod/config in the production environment, and /mnt/share/stag/config in the staging environment. Then our annotation will look like this:

@PropertySources({ @PropertySource("file:${CONFIG_PATH}/microserviceA.props") }) 

Here, CONFIG_PATH can be the environment variable and have values as per the environment. In Linux, it can be set as export CONFIG_PATH=/mnt/share/stag/config.

Sometimes, the developers have logically different configuration files in the same microservice; this annotation supports loading properties from multiple files as well. The following is an example:

@PropertySources({ @PropertySource("file:${CONFIG_PATH}/microserviceA.props"), 
@PropertySource("file:${CONF IG_PATH}/connect.props") }) 

In the preceding example, properties will be loaded from both the files. For any placeholder, Spring looks for the property in both the files.

If we move towards the Spring Cloud with Spring Boot, it gives the flexibility of having the configuration server, which reads the property from any given URL. Any client service coming up will know where the configuration service is running. Client service asks configuration service if it has any properties related to this particular client. Then, the configuration server looks at the URI configured path for the file name that matched the client application name and sends back the JSON to the client. Configuration service is very easy to start; it just needs to use the @EnableConfigServer annotation. URI where configuration service looks for any asking property file is mentioned in the Spring Cloud property file as follows:

Server.port=8888 
spring.cloud.config.server.git.uri =   
https://github.com/abc/microserviceA-configuration

The preceding file can also be in YAML format, the same way we defined the configuration service IP and port in the client configuration file. While client comes up and hits the configuration service to get configuration, configuration looks for the property name (spring.application.name) in the client configuration and for the {clientName}.props file in the mentioned location. In Spring Cloud, client can also be annotated as @RefreshScope. This annotation helps you dynamically load the property without any restart. It exposes the endpoint such as <service endpoint>/refresh, and hitting this endpoint will result in the recreation of bean.

In this section, we have seen many ways to achieve the externalization of configuration. However, I have only covered a couple of methods and left the others for you to explore. Having configuration service will help in dynamically changing the environment with hundreds of services running and sharing some common properties. Using this way, also increase the overhead work for the developers to maintain the configuration server and keep it running up. One can also use caching in front of the configuration server so that, in case of failure of the configuration service, caching can server request for some time. The API Gateway pattern can help in the externalization of some of the configuration of microservices. In the next section, we will discuss more about using the gateway pattern.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset