<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="https://www.w3.org/2005/Atom">
    <title>ninjakoala</title>
    <subtitle>ninjakoala.com is the website of Neil Prosser</subtitle>
    <link href="https://ninjakoala.com/atom.xml" rel="self" />
    <link href="https://ninjakoala.com/" />
    <updated>2021-02-26T22:19:54+00:00</updated>
    <id>https://ninjakoala.com/</id>
    <author>
        <name>Neil Prosser</name>
        <email>neil.prosser@gmail.com</email>
    </author>
    
    <entry>
        <title>How we deploy at MixRadio</title>
        <link href="https://ninjakoala.com/how-we-deploy-at-mixradio/" />
        <updated>2014-10-31T00:00:00+00:00</updated>
        <id>https://ninjakoala.com/how-we-deploy-at-mixradio</id>
        <content type="html">&lt;p&gt;&lt;em&gt;This post was lifted from the (now defunct) MixRadio dev blog which ceased to exist in April 2016. It has been amended slightly to remove out of date links.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this post I’d like to talk about how we deploy our microservices into &lt;a href=&quot;https://aws.amazon.com/ec2/&quot;&gt;EC2&lt;/a&gt; here at MixRadio. I’ll spend a bit a time explaining our old process and some of the problems it had and then I’ll go into more detail on the new Clojure tools we created to solve them.&lt;/p&gt;

&lt;h2 id=&quot;out-with-the-old&quot;&gt;Out with the old…&lt;/h2&gt;

&lt;p&gt;Deon has described our journey from monolithic, big-bang releases to continuous delivery. This was a change that was undertaken while we were running MixRadio out of our own datacenter.&lt;/p&gt;

&lt;p&gt;As we became more comfortable with this new way of working we identified a number of flaws in the way we were deploying and running our services in the datacenter:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://martinfowler.com/bliki/SnowflakeServer.html&quot;&gt;Snowflake servers&lt;/a&gt; - Our servers were more than just an IP address and purpose. Configuration drift would see the servers gradually begin to differ as they lived longer.&lt;/li&gt;
  &lt;li&gt;Provisioning the servers took too long - There were too many manual tasks involved in getting a new server up and running. We had Puppet to cover the basic bootstrapping but still had to enter the server’s details into our deployment tool and load balancers.&lt;/li&gt;
  &lt;li&gt;Deployment time - As the number of instances increased, the time it took to deploy increased linearly. Not a desirable property if you want to maintain the ability to make frequent changes to a high-scale service.&lt;/li&gt;
  &lt;li&gt;Configuration was murky - Changes made to configuration were tracked but it was unclear exactly what had changed and in the event of a rollback being required we would frequently find properties were not reverted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;old-deployment-process&quot;&gt;Old deployment process&lt;/h3&gt;

&lt;p&gt;Our existing deployment process was the result of our transition to continuous delivery. The application which carried out deployments was created in-house and was beginning to show its age. We created it at a time when we were deploying a small number of applications to a couple of servers each for high-availability.&lt;/p&gt;

&lt;p&gt;To make a change to our live platform you would log into a website, enter the version of the service you were deploying, perhaps amend the configuration and kick off your deployment. The deployment tooling would go through each server hosting the service, in turn. It would use SSH to remove the old version of the application and then the new version, with new configuration, would be installed. This process is shown below for an example four server deployment:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/old-deployment.gif&quot; style=&quot;max-height:400px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As we needed to deploy to more servers to handle additional load we found that this approach to deployment was slowing us down. In the event of failure, this method of deploying would also require linear time to perform the same operations in reverse which is bad if a deployment has caused things to go haywire.&lt;/p&gt;

&lt;p&gt;We’d been steadily increasing the number of microservices we were running and the existing process wouldn’t allow the deployment of more than one service to an environment at a time. This was a self-imposed restriction we’d chosen back when we were starting out with continuous delivery because we weren’t happy with more than one piece of the platform changing at a time. We felt that it would make it difficult to determine where any regression or failure had come from if more than one thing was in-flight at a given time. Since that decision was made we’d become more adept at monitoring our services and the platform as a whole so we were keen to see how we would get on without enforcing that restriction in the new world.&lt;/p&gt;

&lt;h2 id=&quot;-in-with-the-new&quot;&gt;… in with the new&lt;/h2&gt;

&lt;p&gt;Last year, we knew that we wanted to migrate out of the datacenter and into AWS. It was a good time for us to change our deployment process and we had a vague idea of what it might look like from reading about other teams’ tools.&lt;/p&gt;

&lt;p&gt;We knew some of the drawbacks of our current process but wanted to make sure we avoided making the new tools painful as well. We used a large whiteboard in the office to let people put up suggestions for functionality the tools should have or things we should consider in the overall design.&lt;/p&gt;

&lt;p&gt;After about a month a group of developers got together and went through the suggestions. They were prioritised and became the goals for the team developing our new tooling. We wanted to begin migrating services to EC2 as soon as possible but had to balance that with making sure everything was working smoothly. We decided that the easiest way to get the tooling up and running was to attempt to do everything required to deploy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A skeleton service which represented a typical service with no dependencies.&lt;/li&gt;
  &lt;li&gt;An actual service which had dependencies on other services already running in our existing datacenter (this was important because we knew that we weren’t going to be doing a big-bang migration).&lt;/li&gt;
  &lt;li&gt;The services which formed the tooling itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was felt these would allow us to dogfood to a point where we were comfortable everything was working safely and the process reflected what we’d like to be using as developers. From there we would be able to open up the tooling to other developers who could begin the migration.&lt;/p&gt;

&lt;p&gt;We had six services which we’d need to create to form the tooling and provide the experience we were looking for:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;#metadata&quot;&gt;Metadata&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#baking&quot;&gt;Baking&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configuration&quot;&gt;Configuration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#infrastructure-management&quot;&gt;Infrastructure management&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deployment-orchestrator&quot;&gt;Deployment orchestrator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#command-line-tool&quot;&gt;Command-line tool&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;metadata&quot;&gt;Metadata&lt;/h3&gt;

&lt;p&gt;We had multiple copies of what was essentially the same list of services: we had one in our logging interface, one in the deployment application and others which all had to be kept up-to-date. We wanted to create a service which just provided that list and meant that anyone who wanted to iterate over the services (including our own tooling) could do so from one canonical source. We also realised that we could attach arbitrary metadata to those service descriptions which would allow us to answer frequent questions like &lt;em&gt;‘who should I go to with questions about service X?’&lt;/em&gt; and &lt;em&gt;‘where can I find the CI build for service Y?’&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We created a RESTful Clojure service which exposes JSON metadata for our services. The metadata for each application can be retrieved individually and edited one property at a time. The minimal output for an application ends up looking something like this:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;search&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contact&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;someone@somewhere.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Searching all the things&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The important thing here is that there’s no schema to the metadata. We have a few properties which are widely used throughout the core tooling but anyone is free to add anything else and do something useful with it.&lt;/p&gt;

&lt;h3 id=&quot;baking&quot;&gt;Baking&lt;/h3&gt;

&lt;p&gt;Having seen the awesome work the guys at &lt;a href=&quot;https://medium.com/netflix-techblog&quot;&gt;Netflix&lt;/a&gt; have done with their tooling we knew that we liked the idea of &lt;a href=&quot;https://medium.com/netflix-techblog/ami-creation-with-aminator-98d627ca37b0&quot;&gt;creating a machine image for each version of a service&lt;/a&gt; rather than setting up an instance and then repeatedly upgrading it over time. We already knew we had a problem with configuration drift and using an image-based approach would alleviate a lot of our problems with snowflake servers. Even if someone had made changes to an individual instance they would be lost whenever a new deployment happened or the instance was replaced. This pushes people towards making every change repeatable.&lt;/p&gt;

&lt;p&gt;We were aware of Neflix’s &lt;a href=&quot;https://github.com/Netflix/aminator&quot;&gt;Aminator&lt;/a&gt; which had just been released. However we had a few restrictions around authentication that made it difficult to use and wanted a little more flexibility than Aminator provided.&lt;/p&gt;

&lt;p&gt;Our baking service is written in Clojure and shells out to &lt;a href=&quot;https://www.packer.io&quot;&gt;Packer&lt;/a&gt; which handles the &lt;a href=&quot;https://www.packer.io/docs/builders/amazon-ebs.html&quot;&gt;interactions with AWS&lt;/a&gt; and running commands on the box. We split our baking into two parts to ensure that baking a service is as fast as possible. The first part takes a plain image and installs anything common across all of our images. This is run once a week automatically, or on demand when necessary. The second part, which is much quicker, installs the service ready to be deployed.&lt;/p&gt;

&lt;h3 id=&quot;configuration&quot;&gt;Configuration&lt;/h3&gt;

&lt;p&gt;To handle differing configuration across our environments we created a service to store this information. We wanted to provide auditing capabilities to see how configuration had changed and have confidence that when we rolled back, we were reverting to the correct settings.&lt;/p&gt;

&lt;p&gt;We were busily planning away and thinking about how to solve the problem of concurrent updates to configuration and how to store the content when we realised that we actually already had the basics for this service on every developer’s machine. We’d veered dangerously close to attempting to write our own Git. It (thankfully) struck us that we could build a RESTful service (in Clojure, of course) which exposed the information within a Git repository. Developers wouldn’t make changes to the configuration via this service, it would be read-only. They would use the tools they’re familiar with from the comfort of their own machine to commit changes. Conflicts and merges would then be handled by software written by people far cleverer than us and auditing is as simple as showing the Git log.&lt;/p&gt;

&lt;p&gt;For each service and environment combination we have a Git repository containing three files which allow developers to control what’s going to happen when they deploy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application-properties.json&lt;/code&gt; - The JSON object in this file gets converted to a Java properties file and the service will use this for its configuration.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deployment-params.json&lt;/code&gt; - This file controls how many instances we want to launch, what type of instance they’ll be etc.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launch-data.json&lt;/code&gt; - This file contains an array of shell command strings which will be executed after the instance has been launched. This functionality doesn’t tend to get used for most services, but has allowed us to automatically create RAID disks from ephemeral storage or enable log-shipping only in certain environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We originally thought that we could just grab the configuration, based on its commit hash, from the configuration service during instance start-up. However, we realised that our configuration service could be down at that point, or the network could be flakey. That’s not a huge problem if someone is actively deploying but if the same situation occurs in the middle of the night when a box dies, we need to know there is very little that can stop that instance from launching successfully. For this reason the configuration file and any scripts which run at launch (which are likely to differ from environment to environment) are created at launch time from &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-user-data-retrieval&quot;&gt;user-data&lt;/a&gt; by &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/&quot;&gt;cloud-init&lt;/a&gt;. User-data is part of the &lt;a href=&quot;https://docs.aws.amazon.com/autoscaling/ec2/userguide/LaunchConfiguration.html&quot;&gt;launch configuration&lt;/a&gt; associated with the &lt;a href=&quot;https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroup.html&quot;&gt;auto scaling group&lt;/a&gt; and is obtained from the web-server which runs on every EC2 instance, making it a reliable place to keep that data. This method means that our service image can be baked without needing to know which environment it will eventually be deployed to, preventing us from having to bake a separate image for each environment.&lt;/p&gt;

&lt;h3 id=&quot;infrastructure-management&quot;&gt;Infrastructure management&lt;/h3&gt;

&lt;p&gt;The AWS console is an awesome tool (and keeps getting better) but it’s not the way we really wanted to have people configuring their infrastructure. By infrastructure we mean things which are going to last longer than the instances which a service uses (for example: load balancers, IAM roles and their policies etc.). We’ve already blogged about this service, but the basic idea is that we like our infrastructure configuration to be version-controlled and the changes made by machines.&lt;/p&gt;

&lt;h3 id=&quot;deployment-orchestrator&quot;&gt;Deployment orchestrator&lt;/h3&gt;

&lt;p&gt;When we started developing our tooling we were pretty green in terms of AWS knowledge and were, in some ways, struggling to know where to start. We knew about Netflix’s &lt;a href=&quot;https://medium.com/netflix-techblog/asgard-web-based-cloud-management-and-deployment-2c9fc4e4d3a1&quot;&gt;Asgard&lt;/a&gt; and the deployment model it encouraged made perfect sense as the base for our move into AWS.&lt;/p&gt;

&lt;p&gt;We started using Asgard but found that our needs were sufficiently different that we ended up moving away from it and creating something similar. I’ll run through our initial use of Asgard before describing what we came up with.&lt;/p&gt;

&lt;h4 id=&quot;red-black-deployment&quot;&gt;Red-black deployment&lt;/h4&gt;

&lt;p&gt;While we’re on the subject of Asgard’s deployment model I’ll describe red-black (or green-blue, red-white, a-b) deployment in case anyone isn’t familiar with it. As shown in our existing deployment model above, we had a problem with the linear increase in deployment (and, perhaps more importantly, rollback) time. We also didn’t like the idea that, during a deployment, we’d be decreasing capacity while we switched out and upgraded each of the instances. A number of our services run on two instances not due to load requirements but merely to provide high-availability so a deployment of these services would result in the traffic split going from 50% on each instance to 100% on one instance. At this point, if anything happens to that single instance the service would be unavailable.&lt;/p&gt;

&lt;p&gt;The red-black deployment model does a good job of solving these issues while also simplifying the logic required to make the deployment. Here’s our previous four server deployment in red-black style:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/new-deployment.gif&quot; style=&quot;max-height:400px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The main benefits of this deployment model are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Capacity is temporarily increased during deployment, rather than reduced.&lt;/li&gt;
  &lt;li&gt;There are opportunities to pause the deployment process and evaluate the new version of the service under live load for any ‘unforeseen consequences’.&lt;/li&gt;
  &lt;li&gt;Rollback is as simple as allowing traffic back onto the old version of the service and preventing traffic to the new version. How long we keep those instances alive after deployment is up to us.&lt;/li&gt;
  &lt;li&gt;The unit of deployment we’re dealing with is an auto scaling group which encourages their use for any deployment whether to single or multiple instances. This pushes us to automate enough that if an instance is being troublesome (or if it simply dies during the night) we have the confidence that it will be terminated and another will take its place.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;back-to-the-orchestration-service&quot;&gt;Back to the orchestration service…&lt;/h4&gt;

&lt;p&gt;This service is the way developers kick-off their deployments. It would then defer to the Asgard APIs to monitor the progress of the deployment and, since Asgard doesn’t have long-term storage, store deployment information so we could track what we’d been up to.&lt;/p&gt;

&lt;p&gt;We had originally intended for the service to use Asgard’s functionality to automatically move through the steps of a deployment but found that because we weren’t using &lt;a href=&quot;https://github.com/Netflix/eureka&quot;&gt;Eureka&lt;/a&gt;, we needed to be able to get in between those steps and check the health of our services. So the orchestration service was written to operate as a mixture of Asgard actions and custom code which performed ‘in-between’ actions for us.&lt;/p&gt;

&lt;p&gt;A deployment consisted of six actions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-asg&lt;/code&gt; - Use Asgard to create an auto scaling group.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait-for-instance-health&lt;/code&gt; - Once the auto scaling group is up and running hit the instances with an HTTP GET and wait for a healthcheck to come back successfully.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enable-asg&lt;/code&gt; - Use Asgard to enable traffic to the newly-created auto scaling group.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait-for-elb-health&lt;/code&gt; - An optional step, wait until the instances from the newly-created auto scaling group are shown as healthy in the load balancer.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable-asg&lt;/code&gt; - Use Asgard to disable traffic on the old auto scaling group.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete-asg&lt;/code&gt; - Use Asgard to delete the old auto scaling group and terminate its instances.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To us, a deployment is a combination of an AMI and the commit hash from the configuration Git repository for the chosen service and environment. During the creation of the auto scaling group our deployment tooling will create the relevant user-data for the commit hash. If we use the same combination of AMI and commit hash we will get the same code running against the same configuration. This is a vast improvement on our old tooling where we’d have to manually check we’d reset the configuration to the old values.&lt;/p&gt;

&lt;p&gt;As we started migrating more services out of the datacenter we found that we wanted more customisation of the deployment process than Asgard provided. We were already running a version of Asgard which had been customised in places and were finding it difficult to keep it up-to-date while maintaining our changes. We made the decision to recreate the deployment process for ourselves and keep Asgard as a very handy window into the state of our AWS accounts.&lt;/p&gt;

&lt;p&gt;We stuck to the same naming-conventions as Asgard, which meant that we could still use it to display our information, but recreated the entire deployment process using Clojure. It wasn’t an easy decision to make but it was considered valuable to us to have complete control over our deployment process without pestering the guys at Netflix to merge pull-requests for functionality which was probably useful only to us.&lt;/p&gt;

&lt;p&gt;We’re really happy with our Asgard-esque clone. We broke the existing six actions down into smaller pieces and a deployment now runs through over fifty actions for each deployment.&lt;/p&gt;

&lt;p&gt;A deployment is still fundamentally the same as before:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Grab the configuration data for the service and environment we’re deploying it to.&lt;/li&gt;
  &lt;li&gt;Generate user-data which will create the necessary environment-specific configuration on the instances.&lt;/li&gt;
  &lt;li&gt;Create a launch configuration and auto scaling group.&lt;/li&gt;
  &lt;li&gt;Wait for the instances to start.&lt;/li&gt;
  &lt;li&gt;Make sure the instances are healthy.&lt;/li&gt;
  &lt;li&gt;Start sending traffic to the new instances.&lt;/li&gt;
  &lt;li&gt;Once we’re happy with the result the old auto scaling group is deactivated and deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only difference is that we’re now able to control the ordering of actions at a fine-grained level and quickly introduce new actions when they’re required.&lt;/p&gt;

&lt;p&gt;Once a deployment has started the work is all done by the orchestration service as it moves through the actions. A human can only step in to pause a deployment and undo it if there are problems. For a typical deployment a single command will kick off the deployment and the developer can watch the new version being rolled out followed by the old version being cleaned up. Undoing a deployment consists of running the same list of deployment actions, but recreating the old state of the service rather than the new.&lt;/p&gt;

&lt;h3 id=&quot;command-line-tool&quot;&gt;Command-line tool&lt;/h3&gt;

&lt;p&gt;In our whiteboard exercise, people had expressed a preference for CLI-driven deployment tooling which could be used in scripts, chained together etc. so we wanted to prioritise the command-line as the primary method for deploying services, with a web-interface only created for read-only display of information.&lt;/p&gt;

&lt;p&gt;We love &lt;a href=&quot;https://hubot.github.com/&quot;&gt;Hubot&lt;/a&gt; and love Hubot in &lt;a href=&quot;https://campfirenow.com/&quot;&gt;Campfire&lt;/a&gt; even more, so we created a command-line application, written in &lt;a href=&quot;https://golang.org&quot;&gt;Go&lt;/a&gt;, which has become the primary method for developers to carry out a large number of the tasks required when building, deploying and managing our services. We’ve written ourselves a Hubot plugin which allows us to use the command-line application from Campfire, which means that I can kick off that bake I forgot to start before I went to lunch while I’m standing in the queue.&lt;/p&gt;

&lt;p&gt;The choice of Go for the tool was an interesting one. We make &lt;a href=&quot;https://www.infoq.com/presentations/java-nokia-case-study&quot;&gt;no secret that we’re&lt;/a&gt; &lt;a href=&quot;https://skillsmatter.com/skillscasts/3891-clojure-at-nokia-entertainment&quot;&gt;big fans of Clojure&lt;/a&gt; here at MixRadio but the JVM has a start-up cost which isn’t suitable for a command-line tool that is being used for quick-fire commands. It was a shoot-out between Go, &lt;a href=&quot;https://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; and &lt;a href=&quot;https://www.python.org/&quot;&gt;Python&lt;/a&gt;. In the end Go won because it starts up quickly, can produce a self-contained binary for any platform (so no fiddling with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt;) &lt;em&gt;and&lt;/em&gt; we wanted to have a go at something new.&lt;/p&gt;

&lt;p&gt;Now, the typical workflow for deploying changes to a service looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Make awesome changes to a service and commit them
$ klink build search
  ... Release output tells us we've built version 3.14 ...
# Bake a new image
$ klink bake search 3.14
  ... Baking output tells us that our AMI is ami-deadbeef ...
# Deploy the new image and configuration to the 'prod' environment
$ klink deploy search prod ami-deadbeef -m &quot;Making everything faster and better&quot;
  ... Deployment output tells us it was successful ...
# Profit!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Hopefully this overview has given a taste of how we manage deployments here at MixRadio. If there’s anything which unclear then please get in touch with us via the comments below or on &lt;a href=&quot;https://twitter.com/neilprosser&quot;&gt;Twitter&lt;/a&gt;. We value any feedback people are willing to give.&lt;/p&gt;

&lt;p&gt;We’re proud of our deployment tooling and are grateful to those who have provided inspiration and the code on which we’ve built.&lt;/p&gt;
</content>
    </entry>
    
    <entry>
        <title>Looking backward</title>
        <link href="https://ninjakoala.com/looking-backward/" />
        <updated>2012-01-09T00:00:00+00:00</updated>
        <id>https://ninjakoala.com/looking-backward</id>
        <content type="html">&lt;p&gt;Matt asked me the other day “What’s on your ‘learn this in 2012’ list?” It got me thinking that rather than tell someone and not have it recorded anywhere, I should be able to be held accountable for what I said I’d do. Welcome to the fruit of that particular bit of thinking. Rather than dive straight in looking forward I thought it’d be helpful to take a look back at 2011 and make sense of what I’ve picked up during a pretty hectic year.&lt;/p&gt;

&lt;h3 id=&quot;where-we-started&quot;&gt;Where we started&lt;/h3&gt;
&lt;p&gt;We started January in the middle of rewriting two of our most important services, at the same time. Our team are responsible for the metadata within Nokia Entertainment. We maintain six services that combine to provide storage, indexing and search functionality which underpins our entertainment platform.&lt;/p&gt;

&lt;p&gt;We hadn’t deployed anything more than a hotfix to live for a few months and I get the feeling we were viewed by those around us as a team that weren’t really doing anything of value. It took six months of toil to get those services out there but the lessons learned along the way were so helpful and have permeated almost every aspect of our work since. Like those awful motivational posters say: “Learning is a journey, not a destination”, or something similarly twee.&lt;/p&gt;

&lt;h3 id=&quot;real-developers-ship&quot;&gt;Real developers ship&lt;/h3&gt;
&lt;p&gt;Being a developer and not having anything deployed for users to actually use is a frustrating feeling. Our main reason for doing the rewrite was to add the new functionality that was needed for the continued expansion of our platform. Our original code was written as we were all learning Java and we had made some mistakes that we decided would be too time-consuming to unpick. It would also see us join the 21st century and get on board with our continuous delivery strategy. This would see us going from ‘big-bang’ deploys of months of work by non-developers to us pushing the button on all of our releases when we decided it was time for a deployment.&lt;/p&gt;

&lt;p&gt;I remember one of the team working on our continuous delivery service asking me what my expectations were. I said that if we were able to have an idea on Monday and have it in production by Friday I’d be a happy little developer. It turns out my wishes more than came true. We went from having no control over deployments to being able to write code and get it in front of users within twenty minutes. To say that this was empowering would be a monumental understatement. The bugs that are inevitable in our job can be fixed-forward rather than rolled-back, we can tweak configuration values within minutes if something isn’t behaving and most of all we, as developers, are in control.&lt;/p&gt;

&lt;p&gt;This is by no means a new idea, but it’s helped us stop thinking in terms of cramming as much as we can into big releases. We can make small iterations and we don’t forget what we’re about to deploy because it was written weeks ago.&lt;/p&gt;

&lt;h3 id=&quot;acceptance-testing&quot;&gt;Acceptance testing&lt;/h3&gt;
&lt;p&gt;When we were starting out we decided to write our acceptance tests using &lt;a href=&quot;https://twitter.com/unclebobmartin&quot;&gt;Uncle Bob&lt;/a&gt;’s excellent &lt;a href=&quot;http://fitnesse.org/&quot;&gt;FitNesse&lt;/a&gt;. We had the mistaken belief that our product owners might want to write our acceptance tests for us. This wasn’t the case. We also made the mistake of wiring our services up to run our acceptance tests (more like integration tests) which made our tests complicated and brittle.&lt;/p&gt;

&lt;p&gt;Based on those lessons we now do our acceptance testing with &lt;a href=&quot;https://junit.org&quot;&gt;JUnit&lt;/a&gt; against an instance of our service running against (in most cases) mocked dependencies. This approach and the libraries we wrote to help test our REST services spawned my first real experience of open source programming with &lt;a href=&quot;https://github.com/rest-driver/rest-driver&quot;&gt;REST-driver&lt;/a&gt;. We’re now at the point where someone new to a project can run the tests with two commands (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mvn verify&lt;/code&gt;). Using JUnit means that we’re writing our tests in the same language we use to write our services and the tests are easy to run (meaning there’s no excuse for not running them).&lt;/p&gt;

&lt;p&gt;Our FitNesse tests now form an impressive regression suite that we run daily over our services. It’s a bit of a chore to edit and even more of a chore to run, down to the way we’ve used it not FitNesse itself. It will be retired when the versions of our APIs that it tests are finally deprecated.&lt;/p&gt;

&lt;h3 id=&quot;performance-testing-isnt-a-silver-bullet&quot;&gt;Performance testing isn’t a silver bullet&lt;/h3&gt;
&lt;p&gt;I spent the early part of 2011 banging on to anyone who’d listen about the importance of performance testing our services. As a team, we must have spent a month or so solidly working on performance tests for the rewritten services. &lt;a href=&quot;https://jmeter.apache.org/&quot;&gt;JMeter&lt;/a&gt; was recruited for hitting the service, &lt;a href=&quot;http://www.groovy-lang.org&quot;&gt;Groovy&lt;/a&gt; was used for some extra scripting and &lt;a href=&quot;https://mbostock.github.io/protovis/&quot;&gt;Protovis&lt;/a&gt; was used for customised graph creation. I expected our performance test graphs to be flat lines with a little jump when someone committed some code which was slow and then promptly flattened back out as we spotted the bottleneck and fixed it. What we got was a completely different story. Most of our lines were jumping all over the place. We then proceeded to let our performance tests decay and I don’t think they’ve been run for months.&lt;/p&gt;

&lt;p&gt;We got some confidence that our services were at least comparable speed-wise to the previous versions but it turns out performance testing wasn’t as important as I thought it was.&lt;/p&gt;

&lt;h3 id=&quot;being-able-to-see-whats-going-on&quot;&gt;Being able to see what’s going on&lt;/h3&gt;
&lt;p&gt;What actually gave us more confidence was the introduction of metrics to our services. Previously to have any inkling of how our services were performing we were limited to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://ganglia.sourceforge.net/&quot;&gt;Ganglia&lt;/a&gt; which gave us ‘metal’ stats for our servers&lt;/li&gt;
  &lt;li&gt;Getting someone to jump on our live boxes and let us browse the log files or asking someone to send us the log files&lt;/li&gt;
  &lt;li&gt;A single email notification that someone had scripted for us as a cron job&lt;/li&gt;
  &lt;li&gt;Our internal logging service (which has an interface which makes it difficult to use)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following on from the disappointment of our performance testing and spurred on by a proof-of-concept created by one of the team we found &lt;a href=&quot;https://codahale.com/&quot;&gt;Coda Hale&lt;/a&gt;’s &lt;a href=&quot;https://github.com/codahale/metrics&quot;&gt;Metrics&lt;/a&gt; library. &lt;strong&gt;I can’t emphasise enough how important this little library has been to us.&lt;/strong&gt; We’ve gone from a position of having virtually no visibility of how our services are acting to having a group of metrics so detailed and up-to-date that we are beginning to get to a position of predicting when something is about to go wrong, or diagnosing problems early.&lt;/p&gt;

&lt;p&gt;We now begin to make performance decisions based on these metrics. Rather than performing outside-in performance testing (such as described above) where our service is called and we measure total response times we’re able to get all this and more by building the measurements into our service. Again, this is by no means ground-breaking in the grand scheme of things but, for us, it’s an epic win which has changed the way we work and how we are able to indicate to our stakeholders what is going on.&lt;/p&gt;

&lt;p&gt;This metrics-based approach is being adopted by other teams so we’re able to overlay the behaviour of communicating services on top of each other, allowing us to interpret trends and help improve the experience for our customers.&lt;/p&gt;

&lt;h3 id=&quot;making-stuff-visible&quot;&gt;Making stuff visible&lt;/h3&gt;
&lt;p&gt;It seems strange now to look back at the position we were in a year ago and not be able to get ‘live’ stats from our services. We had an information radiator which showed our CI status but that was it. We now feed our metrics into &lt;a href=&quot;https://graphiteapp.org&quot;&gt;Graphite&lt;/a&gt; and have a comprehensive set of stats and graphs that rotate throughout the day on a big screen in the middle of our team. In some cases spotting impending doom has been as simple as watching for a sudden change in the lines of a graph. All of this data is available to our stakeholders which means that they can hold us to account on how we’re doing as well.&lt;/p&gt;

&lt;h3 id=&quot;going-off-piste-every-so-often&quot;&gt;Going ‘off-piste’ every so often&lt;/h3&gt;
&lt;p&gt;A by-product of our job is that the truly interesting tasks aren’t necessarily what we spend most of our time doing. I’m grateful my job throws up more than enough interesting challenges but sometimes you’ll find something shiny that you just have to get your grubby hands on there and then. As a result of some of this ‘shiny-following’ I’ve had the chance to scratch my JavaScript and data visualisation itches. I’ve seen others doing similar things and more often than not this stuff ends up helping with work eventually. We’ve all got jobs that we’re paid for but a little distraction can fire-up some enthusiasm or spark something new.&lt;/p&gt;

&lt;h3 id=&quot;meeting-people&quot;&gt;Meeting people&lt;/h3&gt;
&lt;p&gt;The change of approach mentioned above hasn’t just occurred as a result of browsing the web or coming up with it internally. In March a number of people attended &lt;a href=&quot;https://qconlondon.com/&quot;&gt;QCon&lt;/a&gt; and we learned that, as a company, we weren’t far off the industry leaders in terms of our continuous delivery strategy but there was plenty of inspiration for how we structure our services and monitor them. This inspiration came back to the office and started to sneak into the things we were doing. I can’t really remember any developers attending conferences before QCon 2011 but it has got people keeping an eye out for events to attend.&lt;/p&gt;

&lt;p&gt;Getting out, meeting new people and ranting with them is a great way to start solving problems or to spot problems you might be wandering blindly towards. Plus, it’s always nice to go somewhere and geek out with a bunch of like-minded people.&lt;/p&gt;

&lt;h3 id=&quot;finally-some-small-ones&quot;&gt;Finally, some small ones&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Use &lt;a href=&quot;https://git-scm.com&quot;&gt;Git&lt;/a&gt; - branches are a fantastic way to prevent toe-treading and I’m still amazed by its ability to smoosh things together smoothly.&lt;/li&gt;
  &lt;li&gt;Get an SSD - sitting in front of your machine while it boots or Eclipse fires up is no fun.&lt;/li&gt;
  &lt;li&gt;Read &lt;a href=&quot;https://twitter.com/&quot;&gt;Twitter&lt;/a&gt; - I’ve found that my focus is moving from the RSS feeds I track using &lt;a href=&quot;https://www.google.com/reader&quot;&gt;Google Reader&lt;/a&gt; to Twitter. I’ve got lucky and started following a group of people who are feeding me interesting links all the time, and I don’t even have to pay them!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, with that out the way, I can start looking forward to what I hope 2012 has in store.&lt;/p&gt;
</content>
    </entry>
    
</feed>
