Automating Infrastructures: Testing, Scripting, Versioning, Continuous
While we often employ a similar process when automating other parts of a software delivery system (build, deployment, database, etc.), in this article, I’m focusing on automating infrastructures. Typically, we go through a 5-step process toward automating environments that will be used as part of a Continuous Delivery pipeline. They are: document, test, script, version and continuous. They are described in more detail below.
- Document – We document the process required to manually install all operating system and software components on each OS. The key difference with this kind of documentation is that you’re documenting in such a way that it can be automated later on. You might use a wiki or similar tool to document the environment creation process. Before moving on, be sure you’re able to manually create the environment using the documented instructions – at least once. Some example documentation written in a Confluence wiki is shown below.
- Test – We apply a rigorous acceptance test-driven approach in which automated tests are written along with the scripts that automate the creation of the infrastructure. Tools that we often use are Cucumber and Cucumber-Nagios and other tools that apply an acceptance test-driven approach to the infrastructure. An example using Cucumber is shown below.
Feature: Scripted provisioning of target environment
As a developer
I would like a scripted installation of my target environment
so that I can assume the environment will be the same everytime and my deployments will be predictable
Background:
Given I am sshed into the environment
Scenario: Is Passenger installed?
When I run “gem list”
Then I should see “passenger”
Scenario: Is the proper version of Ruby installed?
When I run “/usr/bin/ruby -v”
Then I should see “ruby 1.8.7”
- Script – We script the entire process using tools like Chef, Puppet and CloudFormation so that the process can be repeated and the knowledge required to install and configure the components is not isolated to a few key individuals. These scripts adhere to the behavior described in the automated test(s). A partial example using AWS CloudFormation is shown below.
"Domain" : {
"Type" : "AWS::Route53::RecordSetGroup",
"Properties" : {
"HostedZoneName" : { "Fn::Join" : [ "", [ {"Ref" : "HostedZone"}, "." ]]},
"RecordSets" : [
{
"Name" : { "Fn::Join" : ["", [ { "Ref" : "ApplicationName" }, ".", { "Ref" : "HostedZone" }, "." ]]},
"Type" : "A",
"TTL" : "900",
"ResourceRecords" : [ { "Ref" : "IPAddress" } ]
}]
}
},
“WebServer”: {
“Type”: “AWS::EC2::Instance”,
“Metadata” : {
“AWS::CloudFormation::Init” : {
“config” : {
“packages” : {
“yum” : {
“puppet” : []
}
},
- Version – Since the entire operating system and related components of the infrastructure are scripted, we ensure every infrastructure automation script and test is versioned in a version-control repository so that no change is ever lost. At this point, the documentation in step 1 is no longer used. Version-control systems typically used include Git, Subversion and Perforce – to name a few.
git add .
git commit -m "added route 53 hosted zone"
git push
- Continuous – Finally, we ensure that the infrastructure can be recreated with every change that anyone applies to not just the infrastructure, but the entire software system. We use a Continuous Integration server to do this. The CI server polls the version-control system for changes; once it finds these changes, it creates the infrastructure and runs the infrastructure tests. If any failure occurs, it stops the creation of the environment and reports the error through the CI server, email and other feedback mechanisms. Typically, we use tools such as Jenkins and Anthill Pro to run a “continuous” process like this.
We use this approach when automating everything – from builds to deployments to creating the database and data. We’ve found that when companies are looking to automate their processes, they tend to only focus on scripting and not the rest. We find that an effective Continuous Delivery system requires all five steps so that every change – application code, configuration, data and infrastructure – is part of a single path to production.