Deploying Go With Capistrano

As you could see in our last post from we like to handle our deploys “ruby on rails” style with
capistrano.

Today i want to show how we take the same approach to deploy software written in go.

First off if you are new to go you need to read “How to Write Go Code” to understand why we have todo certain things. Also you should probably understand what capistrano does if you don’t know what it does.

The list why we use capistrano is long but just to name a few things i like about this solution:

  • automatic deploys (just type cap deploy and everything is done for you)
  • auto rollbacks if there a compile time problems
  • self contained version rollback (just in case)
  • easy integration to our errbit system

Great, right? So let’s not waste any time and start:

Assumptions

  • every app is deployed as a unique user on the server
  • your app is hosted at github.com
  • we’ll use capper
  • you need ruby and bundler installed in your development env.

Setting up github

Sadly as of writing this the go get command only supports github.com via https. This means you cannot use the ssh client forwarding capistrano uses to get the current version of your code from your repo. So in order to be able to install your app as go package even though it is in a private repository you need to create a special user with pull privileges for your apps repo.

Go ahead do this right now. Give him a sensible name like “go-server-deploy-user” (i’ll assume this for the rest of this post). Also note his password, you will need it in a second.

If you have and trouble doing this

Preparing the server

After you or your admin created a user:

1. create the go folder

$> ssh [email protected]
$> mkdir -p go/src
$> mkdir -p go/pkg
$> mkdir -p go/bin
$> echo "export GOPATH=$HOME/go" >> .bashrc

2. set up git

please not that you need git version >= 1.7.9

$> ssh [email protected]
$> vim .gitconfig

set it to

1
2
[credential]
    helper = store

save and move on to

$> vim .git-credentials

set it to

1
https://USERNAME:[email protected]

so in our case

1
https://go-server-deploy-user:[email protected]

3. add the github.com ssh key

In case your admin doesn’t have this as a users default (our @roa does;)

$> ssh github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)?
> yes

4. set up your deploy.rb

in your app folder create a Gemfile

# Gemfile
source :rubygems
gem 'capper', :git => "[email protected]:adeven/capper.git"

$> bundle
...
$> capify .

now head to the config/deploy.rb my suggestion looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
set :application, "APPNAME"
set :repository, "[email protected]:YOU/APPNAME.git"
ssh_options[:forward_agent] = true
set :user, 'USERNAME'
set :deploy_to, "/home/#{user}/app"
set :gopath, deploy_to
set :pid_file, deploy_to+'/pids/PIDFILE'
set :symlinks, { "pids" => "pids" }

task :staging do
  server "1.stage.example.com", :app
end

task :production do
  server '1.app.example.com', :app
  server '2.app.example.com', :app
end

after 'deploy:update_code', 'go:build'

namespace :go do
  task :build do
    with_env('GOPATH', gopath) do
      run "go get -u github.com/YOU/APPNAME"
      run "mkdir #{release_path}/bin"
      run "cp /home/#{user}/go/bin/APPNAME #{release_path}/bin/"
    end
  end
end

So what does this do?

  • after you updated the code you go get your repo
  • build it will all it dependecies
  • copy the resulting binary back to your release_path

But why all the fuzz and not just go get it?

This is the obvious question you may ask at this point and the answer is quite simple. Because you may also deploy configuration files or other non compiled stuff that may change with different versions. And since we like the ability to have encapsulated versions (fully independent) with the above script we store the binary toghether with the corresponding auxilliary files.

What are those “encapsulated versions” about?

Image you go get your app on the production server and suddenly there is a problem compiling. You’ll have the broken files and a compiled binary that doesn’t work with your current config. The approach presented here simply (and automagically) rolls back to a stable consistent release.

That’s it for now. Next time you’ll learn how to start/stop your go app using init.d.

Have fun.

Comments