Between Deploys

the

Engineering Blog

Functional Operations

Published on 05 Dec 2013 by Bright Fulton

I knew I needed to quit my job when I first saw the EC2 API. Seeing a few lines of code provision a massive network compelled me to drop what I had been doing and get involved. In the years since that moment, functional approaches have simplified application development. Meanwhile, our approach to deployment is still weighed down with complex state management. Where are our immutable networks? Our lambda cloud calculus? Our functional operations?

Just as functional programming rids us of the unnecessary state that complicates the maintenance of our code, functional operations rids us of the upgrading, quiescing, and process replacement that complicates so much infrastructure automation and deployment code. We intend our running network to be a function of our git repositories, with as few side effects and state transitions as possible.

At Swipely, we assemble running networks as functions of Docker container images, which we build as functions of git repositories. We declare each function and use it to build the output from the input from scratch, firing up new instances running freshly-built containers every time we deploy code, several times a day.

If you want to try Docker, or if you use AWS, then you can use our tools to reliably and repeatably build and deploy networks from your code.

First, use dockly to express your Docker container as a function of your git repo:

deb :my_package do
  package_name 'myapp'

  docker do
      name 'my_image'
      import 's3://.../base-image.tar.gz'
      git_archive '/path/to/myapp'

      build <<-EOF
        run cd /path/to/myapp && ./configure && make
      EOF
  end

  foreman do
    name 'myapp'
    log_dir '/data/logs/myapp'
    user 'appuser'
  end
end

Now you can package Docker containers with dockly build my_package. Use your CI environment to build a deployable container whenever you check in code.

Next, use aerosol -- which we're open-sourcing today -- to express your network as a function of your Docker image:

deploy :production_myapp do
  ssh :production_myapp
  auto_scaling :production_myapp
  stop_command 'sudo stop myapp'
  live_check '/version'
  app_port 14000
  post_deploy_command 'bundle exec rake honeybadger:deploy TO=production'
end

ssh :production_myapp do
  user 'aerosol'
end

auto_scaling :production_myapp do
  availability_zones ['us-east-1a', 'us-east-1b']
  min_size 2
  max_size 2
  launch_configuration :production_myapp
  tag 'Name' => 'prod-myapp'
end

launch_configuration :production_myapp do
  instance_type 'm1.large'
  ami 'ami-90374bf9'
  iam_role 'role-prod-myapp'
  key_name 'prod-myapp'
  security_groups ['prod-myapp', 'prod-ssh']
  user_data ERB.new(File.read('startup.sh.erb')).result(binding) # include install of Docker image
end

Now deploy and cut over to a brand new network with aerosol deploy production_myapp.

Finally, get the humans involved in whatever way best suits your development cycle and culture. At Swipely, we wrote a Hubot plugin to kick off and coordinate production deploys over HipChat:

HipChat plugin

Using dockly and aerosol, we're building and deploying code with the reliability and repeatability of clean functional operations.

Author
Bright Fulton