DISCLAIMER: There’s some stuff in here which could cause all sorts of chaos if it doesn’t sit happily alongside your individual setup – in particular, hacking your internal DNS records is a really bad idea unless you know what’s already in there, and you understand how DNS resolution works within your organisation. Be careful, and if you’re not responsible for your DNS setup, probably best to discuss this with whoever is responsible for it first.
I’ve been setting up a continuous integration system for our main software products. We host 20+ web sites and applications across four different domain names, ranging from ancient legacy ASP applications based on VBScript and recordsets, to ASP.NET MVC apps built with TDD, Windsor, NHibernate and “alt-net” stack.
Here’s a couple of things we’ve come up with that make the whole process run a little more smoothly. Let’s imagine our developers are Alice, Bob and myself, and we’re running a three-stage deployment process. Here’s how it works.
- Alice implements a new feature, working on code on her local workstation. She has a full local copy of every site, under Subversion control, which she can view and test at www.website.com.local
- Once the feature’s done, Alice commits her code. TeamCity – the continuous integration server – will pull the change from Subversion, build it, and deploy the results to www.website.com.build
- We run tests – both automated and manual – against this build site. If everything looks OK, we send this .build URL to the stakeholders and testers to get their feedback on the new feature.
- Once the tests are green and the stakeholders are happy, the feature is ready for launch. We’ll now use msdeploy to push the entire modified website onto the test server - www.website.com.test
- We run integration tests that hit www.website.com.test – and also www.website.com.test/some_app, www.website2.co.uk.test, www.another-site.com.test – basically, they verify that not only do the individual apps and sites all work, but that they’re co-existing happily on the same server.
- Finally, we have a couple of msdeploy tasks set up in TeamCity, that will deploy the entire server configuration from the test server to the public-facing servers.
Setting up Developer Workstations
Most of our developer machines are running Windows 7, which includes IIS7, which supports multiple websites (this used to be a huge limitation of XP Professional, which would only run a single local website). We have a standard setup across workstations, build, test and live servers – they all have a 100Gb+ D: drive dedicated for web hosting, which means we can use msdeploy.exe to clone the test server onto new workstations (or just to reset your own configuration if things get messed up), and to handle the deployment from build to test to live.
Note that this doesn’t mean we’re hard-coding paths to D:\ drives – the apps and sites will happily run from any location on the server, since they use virtual directories and Server.MapPath() to handle any filesystem access. However, it does make life much easier to set up configuration once, and then clone this config across the various systems.
Finally, note that our workstations are 64-bit and the servers are 32-bit, which works fine with one caveat – you can sync and deploy configuration from the servers to the workstations, but not vice versa. In practise, this is actually quite useful – anything being pushed onto the servers should be getting there via Subversion and TeamCity anyway
Using DNS to manage .local, .build and .test zones
Unless you want to maintain a lot of /etc/hosts files, you’ll need your own local DNS servers for this part – but if your organisation is using Active Directory, you’re sorted because your domain controllers are all local DNS servers anyway. The trick here is to create “fake” locally-accessible DNS zones containing wildcard records. We have a zone called local, which contains a single DNS CNAME record that points * at 127.0.0.1. This means that anything.you.like.local will resolve to 127.0.0.1 – so developers can access their local copies of every site by using something like www.sitename.com.local.
There’s a DNS zone called build, which contains an ALIAS record pointing * at build-server.mydomain.com, and another one called test, which has an ALIAS record pointing * at test-server.mydomain.com. We’ve also set up *.dylan as an alias for my workstation, and *.alice as an alias for Alice’s PC, and *.bob as an alias for Bob’s PC, and so on.
This seems simple but it’ll actually give you some very neat capabilities:
- Every developer can see their own projects at www.website.com.local and www.othersite.co.uk.local
- Everyone in the organization can see a particular developer’s work in progress, by going to www.website.com.alice or www.othersite.co.uk.bob
- Everyone in the organization can see the build server – www.website.com.build – and the test server, www.website.com.test
Of course, this doesn’t work unless there’s a web server on the other end that’s listening for those requests, so our common IIS configuration has the following bindings set up for every site:
This looks like a lot of work to maintain, but because developer workstations are set up by using msdeploy to clone the test server’s configuration, these mappings only need to be created once, manually, on the test server, and they’ll be transferred across along with everything else.
I’d be interested to hear from anyone who’s using a similar setup – or who’s found an alternative approach to the same problem. Leave a comment here or drop me a line – or better still, blog your own set-up and send me a link, and I’ll add it here.