Problem definition
I have already described why do we need external configuration management.
In short, let's say we have several environments (production, simulation, test). As the development goes we run different versions of our application on these environments. Part of the configuration of the application is specific to an environment and may change out of sync with our releases. So it is bound to the application version and the environment but should not be stored in the same repository as the code.
We used to have a tool to handle such external configuration management but since it was in-house developed and I moved away from that company I would have to write it from the scratch. We also made some design mistakes (detailed in the aforementioned blog post). So I looked around and found Spring Cloud Config server which can be backed with Git repository to store configuration. The beauty of having Git repository for configuration is that every configuration change is tracked and hence audited. Also tools to edit the configuration are already out there - file managers, IDEs, text editors. Moreover it should be easy to set up since it is just a Spring Boot application.
Spring Cloud Config
I gave it a try. I created a project which has only the dependency to Spring Cloud Config. I set the URL to the Git repository with configuration, deployed it and was ready to go.
pom.xml
application.properties4.0.0 com.company config-server 1.0-SNAPSHOT org.springframework.cloud spring-cloud-config-server 1.1.2.RELEASE org.springframework spring-core 4.2.6.RELEASE config-server org.springframework.boot spring-boot-maven-plugin 1.4.0.RELEASE true repackage
server.port: 8888 spring.cloud.config.server.git.uri: ssh://git@hostname:1111/our-app-config.git
The server supports API calls like
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
-H "Accept: application/octet-stream".
Versioning
Since we strive to get continuous delivery working the version is assigned with every commit. Consequently, any commit can be deployed up to production. This version is derived from the last annotated tag (e.g. 1.0), number of commits since then, and the commit SHA. It may look like: 1.0.42-gda31562.
We have four environments:
- Test - for our testing
- Acceptance - for customer testing
- Simulation - production hotfixes
- Production
And two external configuration files:
env.properties - environment specific properties logback.xml - Logback configuration
We use Ansible for automating the deployment. It places these two files to the lib folder of Tomcat.
The configuration Git repository that is used by Spring Cloud Config contains these files in the root folder:
env.properties - with defaults app-acc-env.properties app-simu-env.properties app-prod-env.properties app-logback.xml app-prod-logback.xmlAnsible makes API calls like: http hostname:8888/app/acc/master/app-env.properties to retrieve app-acc-env.properties from master branch. So far so good.
Branching
Ansible can either ask for master branch and get the latest configuration (we use that to deploy local builds to 'test') or it can use the 'label' to get configuration for specific version (hence we use version as a 'label'). That forces us to create an annotated tag everytime we release to the Artifactory - so everytime we release to acceptance or higher.
If ever we need to change the configuration for a version we convert the tag for that version to a branch and make changes on the branch.
Changing Configuration
We expect there will be two scenarios:
- One needs to add/modify a property because of a new feature, new code. Then we can commit the changed configuration to master branch in the configuration repository. When the next version with the change is released the property is there. That is also one of the main benefits of having the external configuration management.
- One needs to change a property for running version on one or more of the systems. Then it gets a bit tricky. Let's say we have tags: 1.0.0, 1.0.10, 1.0.25, and 1.0.40 I have 1.0.10 running in production and want to set say new email server URL. Then I need to convert tags 1.0.10, 1.0.25 and 1.0.40 to branches and make my change in all the three branches. That may get rather annoying eventually especially if we do not release to prod often and there are many tags.
Problems
The main problems I see with this approach are:
- The need to tag every release.
- The need to change potentially many of these tags - convert them to branches and perform the same modification.
I'm new to Spring Cloud Config so maybe I missed something obvious. Let me know your thoughts.
Benefits
- Configuration can be altered outside of the release cycle of the code.
- Configuration changes are audited and documented (with commit messages).
- It is possible to look up what the configuration was at the point of the deployment - the deployment script logs time of deployment and version so one can look up corresponding configuration.
- We don't need to think about all the configuration changes necessary for given release since they are performed in advance and then updated as the changes occur and not at the point of the release.
The drawback is that you can't commit changes on any branch before the release date, because the changes would get published with next service restart it refresh
ReplyDeleteNice .Keep sharing
ReplyDeleteBiztalk online course Bangalore