.NET web applications tend to get treated very poorly in the real world – some people still think that copying and pasting the contents of their /bin/Release/ directory (lovingly referred to as “DLLs”) over Remote Desktop to a webserver and manually setting up IIS is acceptable – but this is now 2015 and the world has moved on. Here are my thoughts on some of the various ways you can deploy .NET apps to the cloud.
First things first – keeping your .NET app cloud ready
Real cloud environments are stateless. You must treat the web servers you use as ephemeral. DevOps practitioners treat virtual servers as cattle, not pets, and don’t nurse servers back to health if there is a problem. Instead they take them out back, shoot them in the head and spin up a new one.
The .NET Framework does not make building cloud-ready, stateless scalable applications easy by default, especially if you are still shaking off decade old WebForms habits. Here is some advice:
- Never use Session State. If you type HttpContext.Current.Session you lose. Using Session State either forces you to have a “Session State Server”, building a single point of failure into your architecture, or having to use sticky load balancers to force users to continuously hit the same web node where their in-memory session lives.
- You’ll need to synchronize your MachineKey settings between machines, so all nodes use the same keys for crypto.
- Multiple nodes will break ASP.NET MVC’s TempData (typically used for Flash messages) – try CookieTempData
- For configuration values, only use web.config AppSettings and ConnectionStrings. Sticking to this rule will give you maximum compatibility with the various cloud deployment platforms I’ll outline below. And no, don’t use Environment Variables, despite what The 12 Factor app enthuses – Windows apps do not use Environment Variables for application configuration. UPDATE Jan 2016: ASP.NET 5 has embraced Environment Variables as a first class configuration option bringing it inline with other web frameworks – if you are using ASP.NET 5 you can now use Environment Variables as an alternative to local config files. Don’t bother for ASP.NET 4.6 apps.
- Do not rely on any pre-installed software. All dependencies should be pulled from NuGet and distributed with your application package. If you use a vendor’s “solution” (custom PDF components? Using Office to create Excel files? CrystalReports?) insist on a NuGet package or remove the vendor’s software. This is 2015.
The granddaddy of .NET Platform as a Service and the cornerstone of almost every Azure demo. Azure Websites is a very high level abstraction over IIS and .NET web farms, supports lots of very cool deployment mechanisms and is easily scalable.
- Deploy from Github, TFS, Mercurial etc by monitoring branches. The very clever software under the hood (Kudu) monitors branches for changes, runs MSBuild for you and deploys your app.
- Lots of features – staging slots (with DNS switch over for zero-downtime deploys), scaling with a slider, monitoring and logging all included
- You don’t get access to the underlying Windows VM that the sites are running on – even if you pay to have dedicated VMs for your sites. This does mean that you get auto-patching, but if you have any exotic requirements (I’ve seen third party APIs have such broken SSL implementations you need to install their Root CA certificate on your web server) you’ll be out of luck as there is no way to run scripts on the servers.
- To configure your app, you can set variables that replace AppSettings or ConnectionStrings in your web.config at deployment time.
- Azure Websites also supports PHP, Java, node.js and more, if you are happy to run those frameworks on Windows. This blog is WordPress backed, so PHP, and running on Azure Websites!
An honorable mention goes out to App Harbor – they technically got there first by providing a Heroku-like experience for .NET developers. Also note that Azure has “Azure Cloud Services” – this is significantly more complex than Azure Websites and does tie you into the Azure platform significantly. Azure Cloud Services are typically chosen for long running cloud systems rather than transactional web sites (think Xbox Live rather than a high traffic blog).
Amazon Web Services Elastic Beanstalk
Amazon are by far the biggest cloud provider out there and they try to tick as many Windows feature boxes as possible to woo enterprises. Elastic Beanstalk is a Platform as a Service deployment platform, similar to Azure Websites, but completely platform agnostic. Since it uses all the existing EC2 APIs underneath (Elastic Load Balancing, Auto Scaling Groups etc), Language and OS support is much higher than Azure Websites, at the expense of not being optimised for Windows/.NET workloads.
- There is no cheap, shared tier. Your application runs on a dedicated VM that you have access to. This makes costs a bit higher (unless you are crazy and want to try to run .NET on micro instances) but gives you more control. As part of your deployment package you can include Powershell scripts that can execute on your VM.
- The user interface is very limited – when I last checked the only configuration values you could set via the UI were named “PARAM1”, “PARAM2”, “PARAM3” etc, which limited your AppSettings to using those names unless you wanted to completely script your deployment.
- If you want a SQL Server as a Service, you are limited to RDS which charges for the whole VM and SQL Server license. Azure’s SQL Server service charges for CPU time and disk space, which can work out quite a bit cheaper.
- Docker container support is available – this will become important for .NET developers when ASP.NET 5 is out of beta and CoreCLR is ready.
Opscode Chef + Azure or AWS VMs
Opscode Chef is a favorite of the “infrastructure as code” crowd, and it can be made to work on Windows. Given standard virtual machines on either AWS or Azure, you can install the Chef service on your nodes and execute Chef recipes.
- Chef recipes are written in Ruby. This may or may not be a problem depending on your team (I can count the number of .NET developers I know who are also good at Ruby on one hand) but is definitely extra skilled requirements. It is possible to use Chef recipes to bootstrap Powershell scripts, but then you have Rube Goldberg machine of pain.
- Ruby is simply not designed to run on Windows, let alone for long-running processes. The Chef Service had a long standing bug on Windows where Ruby would simply run out of memory. Anybody who has tried getting every gem in a typical Ruby on Rails gemfile to compile on Windows knows the pain I am talking about. Windows support for Ruby is an afterthought.
- One thing Azure has over AWS for Chef deployments is the ability to pre-install the Chef Client onto a VM when you start it, all from the UI. AWS requires you to manually apt-get the client.
- Chef recipes are based on the concept of convergence – where the desired state of the server is described and then a policy is calculated to bring the server to that state. Co-incidentally, this is exactly what Powershell Desired State Configuration does. Chef have plans to integrate with Powershell DSC.
Octopus Deploy + Azure or AWS VMs
Ocotpus Deploy is quickly becoming one of my favourite parts of the .NET ecosystem. Built by some of the finest .NET developers in the land, for .NET developers. It provides the Platform as a Service ease of Azure Websites with the power of running your own VMs. I think of it as bringing your own platform layer to infrastructure you might get elsewhere – I’ve dealt with a big deployment of Octopus on AWS.
- VMs can be assigned to environments, enabling a fully customisable Test-UAT-Staging-Production workflow with release promotion.
- Your build server needs to create “octopack” packages– a nuget package variant. These packages then get pushed to the Octopus server nuget feed and can be deployed.
- A deployment agent called a “Tentacle” is deployed on each VM. A single MSI command can install and enroll the node.
- Elastic scaling is not included – Octopus does not manage your environment for you.
- Deployment steps are fully customisable – you can create IIS sites, AppPools, run custom scripts or even install Windows Services
- Configuration settings for your application are set as variables that apply to AppSettings and ConnectionStrings in your web.config when you deploy.
The Octopus Deploy team is currently working on version 3.0, which will replace the RavenDB database with SQL Server. I’m very much looking forward to it. Octopus isn’t limited to cloud-deployments either – it can be used equally well for on-premise datacenters.
In summary then, I’d choose Azure Websites if the application is simple enough to work within it’s constraints. Given an application with multiple tiers (microservices etc) or special deployment requirements (third party software, certificates), I’d go for Octopus Deploy on top of whichever is your organisation’s favored cloud provider.
If you have any thoughts on the above, or can point out a mistake I’ve made, please drop me an email or leave a comment.