A Shining Ruby in Production Environments

Next, verify that Capistrano is installed correctly, and see the many useful tasks it comes with:

$ cap -T
cap deploy                # Deploys your project.
cap deploy:check          # Tests deployment dependencies.
cap deploy:cleanup        # Cleans up old releases.
cap deploy:cold           # Deploys and starts a 'cold' application.
cap deploy:create_symlink # Updates the symlink to the most recently
                          # deployed...
cap deploy:migrations     # Deploys and runs pending migrations.
cap deploy:pending        # Displays the commits since your last 
                          # deploy.
cap deploy:pending:diff   # Displays the 'diff' since your last 
                          # deploy.
cap deploy:rollback       # Rolls back to a previous version and 
                          # restarts.
cap deploy:rollback:code  # Rolls back to the previously deployed 
                          # version.
cap deploy:setup          # Prepares one or more servers for 
                          # deployment.
cap deploy:symlink        # Deprecated API.
cap deploy:update         # Copies your project and updates the 
                          # symlink.
cap deploy:update_code    # Copies your project to the remote 
                          # servers.
cap deploy:upload         # Copies files to the currently deployed 
                          # version.
cap invoke                # Invokes a single command on the remote 
                          # servers.
cap link_shared           # Link cake, configuration, themes, upload, 
                          # tool
cap shell                 # Begins an interactive Capistrano session.

The user that will deploy the application will need valid SSH access to the server (in order to perform remote commands with Capistrano) and write permissions to the directory where the project will be deployed. The directory structure created on the server in this directory allows you to maintain software releases. In the project's document root, Capistrano keeps two directories, one that contains the released software (releases/, by default it keeps the latest ten releases), and another that contains shared or static data (shared/). Moreover, Capistrano manages a symbolic link named current that always points to the most recent successfully deployed release.

In practice, each time Capistrano is invoked to deploy an application, it connects via SSH, creates a temporary release directory named with the current timestamp (for example, releases/20140115120050), and runs the process (pull, bundle, migrate and so on). If it finishes with no errors, as final step, Capistrano links the symlink "current" to releases/20140115120050. Otherwise, it keeps "current" symlinked with the latest directory where the deploy was successful.

So with Capistrano, the system administrator will set the virtual server DocumentRoot directive to the current directory of the released application version:

DocumentRoot /srv/www/kolobok/current/public

The Anatomy of a deploy.rb File

A deploy.rb file is virtually made of two parts: one that defines the standard configurations, like the repository server or the path to deploy files physically, and another that includes custom tasks defined by the developer responsible for deploying the application.

Let's deploy the Kolobok application. Open the kolobok/config/deploy.rb file with your favourite editor, delete the example configuration and begin to code it from scratch. A deploy.rb file is programmed in Ruby, so you can use Ruby constructs in your tasks, beyond the Capistrano "keywords".

Let's start by requiring a library:

require "bundler/capistrano"

This statement orders Capistrano to do the gem bundle each time it's necessary. Good gem files separate required dependency gems in this way:

group :test do
  gem 'rspec-rails'
  gem 'capybara'
  gem 'factory_girl_rails'

group :production do    
  gem 'execjs'
  gem 'therubyracer'  
  gem 'coffee-rails', '~> 3.1.1'

Only the gems common to all environments and included in the :production group are bundled. Gems belonging to :development and :test environments are not. And the first time you deploy your application, a bundle install is executed to bundle all the requirements as specified. The next time you deploy the software, gems are downloaded, compiled or removed only if the Gemfile and the Gemfile.lock have changed. The complete bundle is installed in shared/ and soft-linked into the current instance. By following this approach, less disk space is required.

Then, from Rails 3.1, it's common to release applications with the assets pipeline. The pipeline is active if in config/environments/production.rb the following variable is set to true:

config.assets.compile = true

If your application will use the pipeline, you need to precompile it. The Rake task to precompile assets is bundle exec rake assets:precompile. To insert this task into your work flow and keep the generated assets pipeline in shared/ and linked into the current release, load the standard assets functionality:

load "deploy/assets"

After loading the main requirements, specify the application name, the path on the server where it will be deployed, and the user allowed to SSH:

set :application, "kolobok"
set :deploy_to, "/srv/www/kolobok" 
et :user, "myuser"

With Rails > 3, it's recommended to invoke Rake (it's used to do the database migrations and to precompile the assets pipeline) with the correct bundled Rake version in the bundle. So, specify the exact rake command:

set :rake, 'bundle exec rake'

Now it's time to configure the repository from which to pull the project source code:

set :scm, :git
set :branch, "master"
set :repository, "git://github.com/myusername/kolobok.git"

Finally, set the server names:

role :web, "kolobok.example.com"
role :app, "kolobok.example.com"
role :db,  "mydb.example.com", :primary => true 

web is the address of the responding Web server, and app is where the application will be deployed. These roles are the same if the application runs on only one host rather than on a cluster. db is the address of the database, and primary => true means that migrations will be run there.

Now you have a well-defined deploy.rb and the right server configurations. Begin by creating the structure tree (releases/ and static/) on the server, from the desktop host:

$ cap deploy:setup

Releasing Software

After having set up the project directory on the server, run the first deploy:

$ cap deploy:cold

The actions performed by Capistrano follow the standard pattern: git checkout, bundle, execute migrations, assets precompile. If everything is fine, your application is finally published as a reliable versioned release, with a current symlink.

Normal deploys (skipping the first Rails app configuration, such as creating the database) will be done in the future by invoking:

$ cap deploy

If you notice that some errors occurred with the current application in production, you immediately can roll back to the previous release by calling Capistrano like this:

$ cap deploy:rollback

Easy, reliable and smart, isn't it?

Custom Tasks

When you deploy a more complex application, you'll normally be handling more complex recipes than the standard Capistrano procedure. For example, if you want to publish an application on GitHub and release it open source, you won't put configurations there (like credentials to access databases or session secret tokens). Rather, it's preferable to copy them in shared/ on the server and link them on the fly before modifying the database or performing your tasks.

In Capistrano, you can define hooks to actions to force the tool to execute required actions before or after other actions. It might be useful, for instance, to link a directory where users of kolobok have uploaded files. If you move the current directory to another release path, you might discover that those files are no longer available to users. So, you can define a final task that, after having deployed code, links the shared/uploads into your current release in public/uploads directory. Notice how this can be managed with ease by exploiting the presence of the shared_path and release_path paths variables:

desc "Link uploaded directory"
task :link_uploads do
  run "ln -nfs #{shared_path}/uploads 

Finally, another common task to perform is to restart the application instance into the server container. In case of Passenger, it's enough to touch the tmp/restart.txt file. So, you can write:

desc "Restart Passenger" 
task :restart do
  run "cd #{current_path} && touch tmp/restart.txt" 

You execute these two tasks automatically by hooking them at the end of the deploy flow. So add this extra line just before the tasks definitions:

after "deploy:update_code", :link_uploads, :restart

Performance Issues?

People often complain of Rails' performance in production environments. This is a tricky topic. Tuning servers and application responsiveness are rather hard tasks that cannot be discussed briefly, so I don't cover them here. To make your application faster, you should involve several technologies and engineering patterns, like setting intermediate caching services, serving static and dynamic content with different server containers and monitoring the application with tools like New Relic to find bottlenecks. After having set up the right environment to host the application, this is the next challenge—optimizing. Happy deploys!


Rack: http://rack.github.com

RVM: https://rvm.io

Phusion Passenger: https://www.phusionpassenger.com

Capistrano: https://github.com/capistrano/capistrano


Fabrizio Soppelsa works as a sysadmin for an Italian provider. His areas of interest include Ruby hosting, automated Ruby deployments, Ruby application performance in production environments, Platform as a Service, and scalable and event-driven Web pat