Best Practices for Build and Release Management Part 3: Making software deployments rapid, reliable and repeatable

When we do our deployments, it’s usually at that time in the project everyone in the world is eager for something to be delivered. With all eyes on you, the last thing you want is a complicated, arduous, risk-filled deployment task. What you need is a rapid, reliable and repeatable task that you have the utmost faith in.

Of course, the simple solution in a nutshell is to automate deployments. This easier said than done of course, but it’s also not rocket science.

If I think of some of the deployments I’ve done in the past and break them down into their constituent manual steps, I might come up with something along these lines:

  1. Download a tar ball from a repository
  2. Extract the tar and move some files.
  3. Make changes to the config files
  4. Add some third party files (maybe tomcat for instance)
  5. Tar it back up
  6. Prep target servers by removing the existing version
  7. Send the new version to each server
  8. Extract it on each host
  9. Start the application
  10. Test that it works

That’s quite a few steps, and in reality each one of these steps would probably consist of several others.

These tasks might not seem risk-filled and arduous but in actual fact they are. If all these steps were manual, then there’d be ample opportunity for human error. Perhaps the downloading of the tar file was incomplete – we’ve introduced a machine error that might not be caught until step 10!

What about the configurations? We might have different configurations for each locale. In a manual process we might have to do this by hand. The opportunity for human error is greatly extended, and mistakes in config files can be costly.

And what about the sheer amount of time it would take if we tried to do these tasks ourselves? I know I’d rather be getting on with something else more constructive!

So let’s see what we can do about making this deployment rapid, reliable and repeatable.

In actual fact we can easily automate all these steps.

We can write a script to download the tar file and then check it against an md5 checksum. An Ant script could do this for us nicely, and hey presto, we’ve removed the risk of that machine error.

Here’s an Ant code snippet for getting a tar file from a maven repository (handy if you use Maven ;-)) and doing an md5 check:

<target name=”get_tar”>
<echo>Getting the distribution from ${sourcehost}</echo>
<scp file=”${sourcefile}” todir=”${tars.dir}/${app.name}/${filename}” trust=”true” keyfile=”${key}” passphrase=” “/>
</target>

Next Step – extracting the file and moving some files about: Really we should look to minimise the amount of file moving we need to do, if the application doesn’t get delivered in the right structure then this should be addressed earlier in the process so that it does get delivered in exactly the right structure for our production system. Go back to the developer/vendor and tell them how you would expect the delivery to look. If this isn’t an option, I’ve again used ant tasks to get my releases into the state I want them in. Here’s an example:

<!– copy the tomcat files into the release –>

<copy todir=”${release.dir}/supportapps/java/${jre_jdk.version}” includeEmptyDirs=”false”>

<fileset dir=”${supportapps.dir}/${jre_jdk.version}”>

</fileset>

</copy>

<!– copy the application configs files into the right directory –>

<copy todir=”${release.dir}/config” overwrite=”true” includeEmptyDirs=”false”>

<fileset dir=”${config.template.dir}/” />

<filterset>

<filter token=”VERSION” value=”${version.num}” />

<filter token=”SERVERNAME” value=”${destination.name}” />

<filter token=”DBNAME” value=”${dbname}” />

<filter token=”UID” value=”${username}” />

</filterset>

</copy>

As you can see, I’ve also made some changes to the configs files here while I copied them from one directory to another. In theory, the only difference between an application deployed on a test environment, and the same application deployed on the production environment, should be the config files. In reality we also have databases which can affect the functionality of our system, but we shall leave that to one side for the moment. There are numerous ways of managing changes to config files, one method (using token replacement) is covered here. The important thing is that this step is carefully managed. I would always prefer to automate this step and write a test script to check that the configs look like I expect them to, rather than ever get involved in manually updating them – the risk of error is simply too high.

What I like to do is store all the configs in source control (I usually use svn), and then either include them in the build (so that when you actually get a release, the configs are all already in there), or get them from the tag branch. Either way, I like them to be tokenised. Then I use the method shown above to replace the tokens with proper values. I like to do this at deploy time because at that point you know the destination hosts you’re going to do deployments to, and can therefore retrieve the right corresponding values to replace the tokens. I also like the practice of keeping the values in a db, and simply pulling them out of the db at deploy time. There are obviously a million ways you can do this. You can even embed it in the ant deploy script like so:

<target name=”getDbName”>

<property name=”temp_sql” value=”${temp.dir}/getdbname.sql”/>

 

<echo file=”${temp_sql}”>select dbname from configuration where servername = ‘${destination.name}’ </echo>

 

<exec executable=”${psql_exec}” failonerror=”false” outputproperty=”dbname.tmp”>

<env key=”PGPASSWORD” value=”${dbpasswd}”/>

<arg line=”-h ${dbsrvname}”/>

<arg line=”-U ${dbusr}”/>

<arg line=”-d ${dbname}”/>

<arg line=”-f ${temp_sql} -t”/>

</exec>

<echo>${dbname}</echo>

</target>

The package is now “ready” to copy over to the destination host – i.e. the configs files are correct for the destination we’re pushing to, the package structure is correct, and any third party files have been included (see jre/jdk above). I tend to tar or zip up the package, simply because it makes the package smaller, and this can be useful if you’re copying the release package to another datacentre and bandwidth is unpredictable. Of course, this step is entirely optional. Anyway, in keeping with the previous examples, here’s an ant snippet:

<target name=”zip”>

<zip destfile=”${release.dir}/release.zip” basedir=”${release.dir}” update=”true”/>

</target>

As simple as that. We’re now ready to move on and automate steps 6-10, which is in the next post!