In the previous four posts, I covered how I went about setting up my server for Phindee and how I configured Unicorn and Nginx. Here in part 5, I will now talk about how I configured Capistrano to actually deploy Phindee.
If you’re not familiar with it, Capistrano is the de-facto deployment tool for Rails apps; it makes deployment easier by automating a lot of the work for you, and it can be easily customized to suit your particular needs. If you’ve never used it before, I hope this post will give you a taste of what it can do.
By the way, I’ll be using version 3, which came out last summer; it’s a complete rewrite that ended up reducing Capistrano’s footprint to just 700 lines of code! If you’re coming from version 2, I recommend reading this post to learn about the differences.
One of the ways the core team was able to keep it so lean was by breaking framework-specific tasks into separate gems, which means that in addition to installing the Capistrano gem itself, we’ll need to install Rails-specific gems as well. Here is what you should add to your Gemfile
:
1 2 3 4 5 6 7 8 |
|
Since we’ll only be using Capistrano in development, we put all the gems in the :development
group. Note that we added Rails-specific tasks through the capistrano-rails
gem, Bundler-specific tasks through the capistrano-bundler
gem, and rbenv-specific tasks through the capistrano-rbenv
gem.
We can install them by running bundle
in the root directory of our Rails app. After Bundler finishes the install, we’ll tell Capistrano to create the necessary files it needs to do its job by running the following:
1
|
|
One of the files this created is called Capfile
, which will be located in the root directory of your Rails app. It’ll contain various require
statements to load the necessary code that Capistrano will need to do its job. We’ll open it up and uncomment the following lines to load the gems we just installed:
1 2 3 |
|
You’ll also see the following line at the end of the file:
1
|
|
This will load any custom tasks from lib/capistrano/tasks
, which we will later define.
Roll up Your Sleeves
One cool thing about Capistrano is it’s designed to work with different deployment scenarios. You could, for example, have both a production server running your “live” application and a staging server meant for testing newly developed features before they’re pushed to the production server. In other words, you’d have two deployment stages: production and staging. When we ran cap install
, Capistrano actually already created the necessary files for this; they’re located inside the /config/deploy
directory and are named production.rb
and staging.rb
, respectively. We’ll use them to define stage-specific configurations, while configurations that are meant to be shared across all stages will be set in config/deploy.rb
, and that’s where we’ll start first.
General Configuration
Below is how my deploy.rb
file looks like for Phindee:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
The very first line locks the configurations in this file to Capistrano 3.1, and if you have any other version installed, the file won’t run. (This is designed to help prevent configurations from braking between version updates.)
Next, we’re using the set()
function to initialize the :application
variable to “phindee.” (We’ll retrieve this variable’s value later using the corresponding fetch()
function.) We’re also setting the :repo_url
variable to the URL of the GitHub repository containing your code so Capistrano knows where to look when we deploy. By the way, if your code is on a branch other than “master,” you’ll need to specify its name by adding set :branch, “branch-name”
; otherwise, this is not needed because Capistrano sets it to “master” by default.
The next line sets the :deploy_to
variable to the path where you want Capistrano storing the code it downloads from GitHub. This should be the same path you previously set in unicorn.rb
, but without the /current
directory appended to it. This is because /current
represents the directory with the latest deploy code, while Capistrano is interested in the general app directory.
:deploy_user
is then set to the user Capistrano will be deploying as, and this should match the user you created when you setup your server in part 1.
The next few lines set variables needed by rbenv, and I actually copied and pasted these lines from the capistrano-rbenv
README file. The key variable here is :rbenv_ruby
, which sets the Ruby version that rbenv installed on your machine, and you can run ls ~/.rbenv/versions
in the command line to find which version that is. If this is not set correctly, the deploy will fail.
The other variable worth mentioning here is :rbenv_type
. We could set it to :system
if rbenv was installed system-wide on our machine, but since we installed rbenv on a per-user basis inside ~/.rbenv
, we’re setting it to :user
. System-wide installs can lead to problems with permissions, and it’s generally cleaner to just do a per-user install. The other three variables don’t need to be modified, and you can leave them the way they are.
Moving on, we’re setting the :linked_files
variable to an array of strings initialized to config/database.yml
. This tells Capistrano to store our app’s config/database.yml
file inside a directory called /shared
, which is meant for any files we want to persist between deploys. Since the contents of database.yml
won’t change between deploys, it’s a good idea to store it there.
Similarly, :linked_dirs
contains directories that are meant to persist between deploys, and they too will be stored inside /shared
. These include directories containing things like log files, Unicorn sockets, and .pid
files that will all stay the same between deploys.
And finally, :keep_releases
tells Capistrano to only keep the last 5 deploys and discard everything else. This can be useful whenever you need to rollback to a previous release, but you also don’t want releases piling up, so it’s best not to set this number too high.
Stage-Specific Configuration
Now that deploy.rb
is configured, we’ll move on to defining stage-specific configurations. Since I currently don’t have a separate environment for staging, I’ll only be going over the config/deploy/production.rb
file, and you can just leave staging.rb
the way it is by default. Below is how my production.rb
file looks like:
1 2 3 4 |
|
As you can see, there isn’t much going on here. We’re first setting the :stage
variable to :production
to let Capistrano know that this file is meant for production. We’re also setting the :rails_env
variable to the same thing to make sure Rails runs in the production environment. But the key line is the last line, which tells Capistrano how to access our VPS server. Make sure you replace the Xs with the IP address of the server you setup in part 1, along with the user and port number it’s set up with.
We’re then using the :roles
variable to let Capistrano know that our database server (PostgreSQL) represented by db
, web server (Nginx) represented by web
, and application server (Unicorn) represented by app
all run on the same machine. Apps with lots of traffic, on the other hand, might have multiple separate physical servers for each of these. Setting :primary
to true
then tells Capistrano that this is our primary database server, and Capistrano will run migrations only on the one we designate as :primary
. Even if we’re running all our servers on the same physical machine, setting :primary
is still necessary.
Enabling Agent Forwarding
Now that Capistrano knows how to access our VPS, we need to make sure it can also access our code on GitHub. We’ll be using agent forwarding to allow us to reuse the local key we generated in part 1 to authenticate with GitHub. In order for this to work, we’ll need to add the key to GitHub, and you can do so by following step 3 on this GitHub page.
To enable agent forwarding in Capistrano 2, you had explicitly set it in deploy.rb
, but in Capistrano 3, it’s already taken care of and enabled by default. The only thing we have left to do is log in to our VPS and run the following command to add github.com to the list of known hosts; this ensures Capistrano won’t have any problems with it being unknown when it tries downloading your code from GitHub to your server:
1
|
|
You’ll get a warning asking if you’re sure you want to continue connecting. Verify that the key fingerprint matches the one you just added to GitHub, and enter “yes”. If you get an “access denied” message, see this page for potential solutions. If you’re experiencing some other agent forwarding problems, this page might help you out.
Setting Permissions
If you look at deploy.rb
, you’ll notice I set the :deploy_to
variable to “/var/www/phindee,” but on my VPS, the /var
directory doesn’t yet contain the /www
directory. That’s not a problem since Capistrano will create it for me through the user bob
, as specified in deploy.rb
, but it needs write permissions to do so.
If you read part 1, you’ll remember we created a group called deployers
to contain users with deployment privileges and added the user bob
into it. This means we can give the necessary permissions bob
will need by simply giving them to deployers
, and since bob
is a member of the group, he will automatically inherit them.
I’m already logged in to my VPS as bob
, and I can change the /var
directory’s group to deployers
with the following command:
1
|
|
We can then give this group write permissions so its members can create directories within /var
:
1
|
|
There are two other places where we need to repeat this process. The first is the /etc/nginx/sites-enabled
directory, which Nginx uses to store its configuration files, and this is where our config/nginx.conf
file that we created in part 4 will go. But we actually won’t be storing the file itself there; we’ll create a symlink to it, instead. This will make our deploys easier to manage because we won’t need to add our nginx.conf
file to the /etc/nginx/sites-enabled
directory every time we deploy. We can simply symlink it since Capistrano will always store our latest deploy code in the same place (/var/www/phindee/current
).
Same thing is needed for the config/unicorn_init.sh
file from part 3. We’ll need to create a symlink inside /etc/init.d
, since that’s the directory Linux uses to store all the shell scripts used to manage the various services installed on the system. When we installed Nginx in part 2, for example, a shell script was automatically installed there to help us manage Nginx, and it will be invoked whenever we run a command like sudo service nginx restart
. There is nothing like this for Unicorn yet, which is why we need to create a symlink to our unicorn_init.sh
script to give us similar functionality.
In order for Capistrano to create symlinks, it needs write permissions in the relevant directories. We can give them with the following commands:
1 2 3 4 5 |
|
And now Capistrano should have the necessary permissions to do its work.
Having Capistrano configured, we’re ready to move on and start writing custom tasks to help us deploy our code, and that’s exactly what we’ll do in the next and last post of this series. If you want to be notified when it’s out, feel free to subscribe, and you’ll get it delivered to your inbox as soon as it’s released.