Jul 2 2011

Continuous Deployment Using TeamCity 6, MSDeploy, and psake

Category: Matt @ 03:44

Some might call continuous deployment the holy grail of agile practices.  The idea that any code change will be live in production within minutes of being committed is both terrifying and powerful.  In this post I will give you some background on what continuous deployment is and why you should care, and I’ll show you how easy it is to start doing continuous deployment today.

Continuous Deployment – What is it?

Most developers are familiar with continuous integration, but fewer are familiar with the idea of continuous deployment.  Put simply, continuous deployment is a process in which you are continuously releasing new code  into production.  This is in sharp contrast to the typical release process which often involves lots of ceremony and occurs only infrequently.  In a typical software development life cycle, days or weeks may pass between when code is committed to source control and when that code shows up in production.  With continuous deployment, that cycle is reduced to minutes.  This provides a development team with much faster feedback and alleviates the burden of a heavy manual release process.  It also reduces the risk associated with a release since each release contains far fewer changes.

For more basic info about continuous deployment, check out Eric Ries’ article, “Continuous deployment in 5 easy steps.”

When to (and When Not to) Continuously Deploy

In order to do continuous deployment, I strongly recommend that you have a library of good automated unit tests, and preferably automated acceptance tests as well.  With these in place, you can craft a process that will only go forward with an automated deployment once all test cases have successfully executed, and you can have (more) confidence that the changes being deployed are production-worthy.  You also need to have good automated measures in place to monitor your application and detect when a deployment has gone wrong.  If you find out that something is wrong because a customer complains, you need to invest in additional monitoring for your application.

There are certainly some risks in doing continuous deployment.  It’s not for everyone and every application in every scenario.  Inevitably, some code that’s not up to par is going to find its way into your source control system, which means it could find it’s way out into the wild.  If you are working in a domain where there’s no tolerance at all for that sort of thing, I would advise against doing continuous deployment.  In such scenarios, a more heavy weight process may actually be what you need, at least for a production environment.  You can (and should!) still leverage continuous deployment for your lower environments though!  And even in domains where errors in production are costly, you may be able to build in enough automation and monitoring to make continuous deployment preferable to large, monolithic releases.

Our Continuous Deployment Process

The process we’re going to implement is actually quite simple.  When a developer commits code, we’re going to automatically compile the code, run all available tests (unit tests, integration tests, and automated acceptance tests if we have them), then automatically decide whether to deploy a new version of the application. 

image

If the code compiles and all tests pass, we’ll proceed with deploying the code.  If not, we’ll send error E-mails and leave the previous version of the application unchanged.  The process is simple, and we only need a few tools to help us achieve complete automation. 

The Essential Tools

You, too, can start doing continuous deployment today using a few small, free tools.  The process I’m going to describe is built using TeamCity, MSDeploy (also known as the Web Deployment tool), and psake.  Do note that while I very much like these tools, they are far from the only ones you could use to implement this process.  Almost any automated build server will do (check out Hudson or CruiseControl.NET), and there are numerous tools you can use to perform the actual build (MSBuild, NAnt, etc.) and deployment (rsync, FTP, etc.) of your application. 

A Simple psake Deploy Script Using MSDeploy

I’ve written recently about why I like psake.  I’ve also written about MSDeploy (way back in February of last year!), and I am still a fan today.  It’s a great tool for publishing application changes.  In the simple script below, I’m actually publishing to the box that’s invoking the deployment process, but MSDeploy can do much more than copy files around locally.  It can deploy through firewalls using HTTP, it can sync instances of IIS, it can even apply database changes and file system permissions.  That flexibility is precisely one of the reasons that I like MSDeploy so much.  Without MSDeploy, I might use xcopy to deploy locally or over my LAN, but would need to fall back to FTP or something similar to deploy over the web, and I might still run into issues with setting custom file permissions or deploying database changes.  MSDeploy can handle both scenarios quite easily.

The following script is the actual deployment script for RageFeed (my social networking startup that had many of the same features that Google+ has now “stolen, ” but I digress  Smile):

properties {
    $TargetDir = "c:\inetpub\ragefeed-wwwroot\"
    $BaseDir = Resolve-Path "..\"
    $SolutionFile = "$BaseDir\Source\LazyDeveloperGroup.RageFeed.sln"
    $OutputDir = "$BaseDir\Deploy\Package\"
    $OutputWebDir = "$OutputDir" + "_PublishedWebsites\LazyDeveloperGroup.RageFeed.Web"
    $WebConfigFile = "$OutputWebDir\Web.config"
    $Version = "dev"
    $Debug="false"
}

task default -depends Deploy

task Init {
    cls
}

task Clean {
    if (Test-Path $OutputDir) {
        ri $OutputDir -Recurse
    }
}

task Build {
    exec { msbuild $SolutionFile "/p:MvcBuildViews=False;OutDir=$OutputDir" }
}

task Deploy -depends Init,Clean,Build {
    #We'll look at this in a sec...
}

This should look similar to the script I posted for publishing to NuGet: there are properties that can be overridden from the command-line or from the build server (which is how the $Version property is set in this case), standard Init, Clean, and Build tasks, and the Deploy task. 

The real interesting stuff is in the Deploy task:

task Deploy -depends Init,Clean,Build {
    ri "$OutputWebDir\Web.*.config"
    ri "$OutputWebDir\packages.config"
    
    $WebConfig = [xml](get-content "$WebConfigFile")
    ($WebConfig.configuration.appSettings.add | ? {$_.key -eq "Environment"}).value = "Production"    
    ($WebConfig.configuration.appSettings.add | ? {$_.key -eq "Version"}).value = "1.0.0.$Version"    
    $WebConfig.configuration."system.web".compilation.debug="$Debug"
    $WebConfig.save($WebConfigFile)

    exec { msdeploy "-verb:sync" "-source:contentPath=$OutputWebDir" "-dest:contentPath=$TargetDir" }
}

Aside from a little Web.config manipulation, all the real work is done by a single call to msdeploy.  The “sync” verb tells MSDeploy “make the destination look like the source”.  Both the “source” and “dest” parameters can be set to different types of content providers, including a remote server.  For more about providers, check out the docs.

At this point, you should be able to run your psake script to deploy your application, but that’s still not continuous deployment.  To get to continuous deployment, you need to automate things further.  That’s where a build server, in this case TeamCity, comes in.

Configuring TeamCity

I won’t go into much depth on basic TeamCity configuration since it’s fairly well documented elsewhere.  If you don’t at least have a build project configured that’s monitoring your source control system, checking out and building the code, and executing your unit tests, the TeamCity documentation has all the info you need to get started.

Let’s assume you already have a project configured to checkout, build, and test your application.  Now all you need is a project that will deploy your application if (and only if) your build-and-test project completes successfully.  Create a copy of your build-and-test project, and remove the existing build steps.  Add a new Powershell build step (NOTE: full support for Powershell was added in TeamCity 6.5.  In prior versions of TeamCity, like I’m running for RageFeed, you need to install the Powershell plug-in).  Here’s how I’ve configured my Powershell step for deploying RageFeed:

image

There’s only a couple of things you need to do: change to the directory containing your deployment-related scripts, then call your psake script.  In an effort to keep the TeamCity configuration as simple as possible, I’ve actually created a very thin wrapper around psake, and I call that wrapper script instead.  I also hand the wrapper script the TeamCity build number, which is then used by the deploy script to update an AppSetting in the RageFeed web.config file (see the psake script above).  The wrapper script has a few interesting things in it:

param (
    $Properties = @{}
)
import-module .\psake.psm1
$psake.use_exit_on_error = $true
invoke-psake -framework 4.0 -taskList Deploy -properties $Properties
remove-module psake

First, the wrapper allows properties to be passed through to the underlying deployment script.  This gives me the flexibility to override any deploy parameters from TeamCity should I ever need to.  I’m also telling psake to actually return an error should something go wrong ($psake.use_exit_on_error = $true).  Without this, TeamCity won’t be able to determine when something goes wrong in the psake script, which means you won’t be notified when a deployment goes awry.  The rest of the script is self-explanatory.

The final bit of TeamCity configuration you need to perform is creating a dependency between your new deployment project and the build-and-test project.  Go to the “Dependencies” configuration for your deployment project in TeamCity, then add a new snapshot dependency.  Select your build-and-test project, and be sure the “Run build even if dependency has failed” option is not selected.  Your snapshot settings should look something like this:

image

Click save, and you’re all set: you have continuous deployment enabled for your application!

Enhancements

The process I’ve outlined is very primitive, and there’s a lot of things you can build into it to meet the needs of your particular application.  Perhaps you have the more traditional setup of separate development, staging, and production environments.  You can add in more configurability so that your psake script can be used for any of the environments.  You should also add automatic monitoring and error reporting to your application so that you’ll know immediately when an automated release has introduced problems.  You may need to add additional deployment steps to handle things like upgrading databases or gracefully taking your application offline while the new version is deployed.   All of these things can be added on quite easily thanks to the power of Powershell and psake. 

Further Reading

I touched briefly on the need to automatically detect (and hopefully prevent) as many types of errors as possible.  A good way to start identifying sources of errors is to practice “The Five Whys”.  Though it’s certainly applicable even if you aren’t doing continuous deployment, it becomes almost a necessity when you are.  Asking “why?” every time something bad reaches production will help you identify things you can improve upon, and investing in preventing those problems will give you more and more confidence in your continuous deployment process over time. 

Next Steps

Assuming your application’s deployment can be scripted out (and it really should be), continuous deployment is actually quite easy to do.  All the tools you need to do it are free.  Go download TeamCity, setup your projects, and start doing continuous deployment to a non-production environment.  If that works well for your project and your team, consider taking it further and applying continuous deployment to a higher environment.  Be sure you keep investing in good tests and other automation that can prevent a bad release from reaching production.  You won’t regret it.

Tags:

blog comments powered by Disqus