Capistrano series - deploying application changes

So, we have Nginx and Capistrano setup and we've deployed our application for the first time. Everything seems to be zinging along very nicely.

What happens now though? How do I deploy any future application changes?

Let's take look.


Application Changes

Although we can take a well deserved break at this point (we have done a lot to our Slice and local workstation) we do have to think about why we went through this process in the first place:

To make it easier to deploy application changes.

It really is as simple as that: Once we have made a change to our application during the development process, we want to get it onto the Slice and serve it as quickly and as efficiently as possible.

The whole point of this series is to get down to one command which will deploy the changes and restart the necessary services for us.

after tasks

Let's take a look at a pretty cool feature of Capistrano called 'after' tasks (you can also define 'before' tasks).

You are able to create custom tasks that Capistrano will execute (as an example, I am going to create a task to reload Nginx).

I'm not going to go into it here but a 'before' task is one that will be executed before another one (you'll get the idea from the 'after' tasks).

deploy.rb

Let's start by opening the deploy.rb file (again, remember this is on the workstation and not the Slice):

nano config/deploy.rb

Task creation

Creating a task in Capistrano is very easy: It needs a description and then the task itself.

An example task to reload Nginx may look like this:

desc "Reload Nginx"
task :reload_nginx do
  sudo "/etc/init.d/nginx reload"
end

Pretty neat and straight forward but remember all we have done here is define a task: It won't actually be executed.

Chronological execution

Posh title for a simple concept as all we need to do is define when the task will be executed.

In this example, I'm going to use the reload_nginx task and I want to run the task after the deployment:

after "deploy", "reload_nginx"

Where did the "deploy" come from?

Well, the Capistrano command to deploy the changes to our application is like so:

cap deploy

As such, once the deploy task has been completed, Capistrano will execute the "reload_nginx" task.

Keep in mind that this is just an example task - there is no need to reload Nginx after the deployment. It is simply here to demonstrate the process.

final deploy.rb

So, if we were going to use the reload_nginx task, the final deploy.rb would look like this:

set :application, "domain.com"
set :user, "demo"
set :repository,  "svn+project1ssh://123.45.67.890/home/demo/repository/project1"

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
# set :deploy_to, "/var/www/#{application}"

set :port, 30000

set :deploy_to, "/home/demo/public_html/#{application}"

# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion


role :app, application
role :web, application
role :db, application , :primary => true

set :deploy_via, :copy

set :runner, user

desc "Reload Nginx"
task :reload_nginx do
  sudo "/etc/init.d/nginx reload"
end

after "deploy", "reload_nginx"

As an aside, you can create separate files for the tasks - they do not have to be in the main deploy.rb file which, after a few custom tasks, can get a little unwieldy (we'll look at extending Capistrano in future articles).

Cleanup

OK, so now we've seen how to create a task and how to ensure it is executed at a particular point in the deployment, let's have a look at a built in task that is often overlooked.

Cleanup is a very useful (and highly recommended) task that is already incorporated into Capistrano.

You can take a look at the available tasks with a simple:

cap -T

One of the listed items is:

cap deploy:cleanup       # Clean up old releases.

Remember that each time Capistrano deploys your changes, it creates a new 'release' and points the 'current' symlink to the latest releases folder.

That's pretty cool but what about all the old updates?

Well, they just sit there and take up space.

In all reality, there will be no need to keep more than 4 old releases. If you need to roll back to an older release then the best strategy would be to roll back using subversion and deploy in the normal way.

Cleanup does the dirty work for you and deletes old releases, leaving the current one and 4 older ones.

As an example, on my Slice releases directory I had these:

ls
20080123131250  20080125121833  20080125124511  20080125124701  0080125133103
20080125121642  20080125124359  20080125124549  20080125130309

Quite a few releases that were taking up space and doing nothing.

On my workstation I executed this command:

cap deploy:cleanup

Once it had completed the Slice was left with:

ls
20080125124511  20080125124549  20080125124701  20080125130309  20080125133103

Much better.

However, the idea is to deploy our application changes with one command. So let's add the cleanup task to the deploy.rb file so it's executed after the main deployment.

After task

Using the principles shown above, we need to add an 'after' task but instead of defining a new task, we can use the built in 'cleanup' task.

This is easily done via the deploy.rb file with just one line:

after "deploy", "deploy:cleanup"

Remember the main deployment task is called 'deploy' so we're telling Capistrano to execute the 'deploy:cleanup' task after the main deployment has been completed.

Nice and simple.

So, the final deploy.rb file looks like this:

set :application, "domain.com"
set :user, "demo"
set :repository,  "svn+project1ssh://123.45.67.890/home/demo/repository/project1"

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
# set :deploy_to, "/var/www/#{application}"

set :port, 30000

set :deploy_to, "/home/demo/public_html/#{application}"

# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion


role :app, application
role :web, application
role :db, application , :primary => true

set :deploy_via, :copy

set :runner, user

after "deploy", "deploy:cleanup"

Nice.

Deploy it!

Ok, make a change to the application. Even as small as renaming the index.html page - just something that we can verify has been actioned by Capistrano during a deploy.

Once you've done that, commit the changes to the subversion repository:

svn commit -m "renamed index.html file for Capistrano deploy check"

Now we can deploy the application, just as we will for any future changes:

cap deploy

Output

Capistrano outputs a fair but of, er, output but I want to show you the last few lines:

** [out :: domain.com] Restarting 8447
 ** [out :: domain.com] Restarting 8448
    command finished
    triggering after callbacks for `deploy'
  * executing `deploy:cleanup'
  * executing "ls -x /home/demo/public_html/domain.com/releases"
    servers: ["domain.com"]
    [domain1.usefulvps.com] executing command
    command finished
 ** keeping 5 of 9 deployed releases
  * executing "sudo -p 'sudo password: ' rm -rf
/home/demo/public_html/domain.com/releases/20080125124511
/home/demo/public_html/domain.com/releases/20080125124549
/home/demo/public_html/domain.com/releases/20080125124701
/home/demo/public_html/domain.com/releases/20080125130309"
    servers: ["domain.com"]
    [domain1.com] executing command
    command finished

The first couple of line (Restarting 8447 and 8448) is Capistrano restarting the mongrels.

The bit that I particularly want to show are the next few lines:

** keeping 5 of 9 deployed releases
  * executing "sudo -p 'sudo password: ' rm -rf
/home/demo/public_html/domain.com/releases/20080125124511

That's exactly what we wanted - Capistrano is deleting all but 5 of the older releases (the current release and 4 older ones).

That will happen each time we deploy a change - keeping the Slice clean and uncluttered.

So, any future changes are as simple as a:

cap deploy

Summary

Going through the setup of Capistrano bit by bit shows that it can be pretty easy to setup and deploy your application(s).

There is a lot more you can do with Capistrano and I will be adding some 'auxiliary' articles which take a look at db migrations and some other tasks and recipes that you can create and use.

However, I hope you can see the general aim and use of Capistrano and how it can make a huge difference to how you spend your time.

PickledOnion

Article Comments:

David Parker commented Fri Jan 25 23:30:11 UTC 2008:

Great series! Wow. This is EXACTLY what I've been looking for, and learning to do all by my lonesome (from start to finish) with a little help from Peepcode's Capistrano 2 video.

Keep up the good work!

Eric H commented Sat Apr 05 18:13:23 UTC 2008:

shouldn't the final deploy.rb file include both the reload after task and the cleanup after task? or does the cleanup task include the reload?

Daniel Waite commented Wed Aug 27 01:31:19 UTC 2008:

@Eric H Yes, the final deploy.rb file should include both "after" tasks:

after "deploy", "reload_nginx" after "deploy", "deploy:cleanup"

Bruce Krysiak commented Tue Dec 02 21:15:40 UTC 2008:

I also had to set up the web user to run sudo commands without passwords (else you'll have to type in your web user's password on your slice every time you do a cap deploy - on your slice: $ sudo visudo

# Cmnd alias specification
Cmnd_Alias      WEB = /etc/init.d/nginx [a-z]*, /etc/init.d/thin [a-z]*

# User privilege specification
root    ALL=(ALL) ALL
web     ALL=NOPASSWD: WEB

Saurabh Bhatia commented Wed Sep 09 09:42:27 UTC 2009:

This article is amazing However I had to do things a bit differently for my Hardy Slice:

  1. SCM was git so:

set :scm, :git set :repository, "clone url of the repository" set :branch, "master" set :deploy_via, :copy set :runner, 'admin'

  1. Write Domain instead of Application:

role :app, domain role :web, domain role :db, domain, :primary => true

Andrew Blake commented Thu Jul 07 18:28:09 UTC 2011:

@Eric B and Daniel Waite:

No: as stated in the text, the reload_nginx task is not necessary. It was just a random example.

"Keep in mind that this is just an example task - there is no need to reload Nginx after the deployment. It is simply here to demonstrate the process."

The final deploy.rb does not contain the reload_nginx task because niginx does not need to be started after a deploy; there's no need to do this in real life.

It is not part of the cleanup task either.

The cleanup task is a good idea to do in real life.

Want to comment?


(not made public)

(optional)

(use plain text or Markdown syntax)