Deploying Rails Apps using Capistrano (Part 3)

Prerequisite: Deploying Rails Apps using Capistrano (Part 2)

Table of Contents

 

4 The Rails App

For the sake of the tutorial, I created a public repository on my Github account that contains the Rails app I’ll be deploying to the server we’ve been working on. This sample app uses Rails 4.1, with MySQL as its database. To clone the repository, use the following command.

git clone https://github.com/RaMin0/v.ps.git

Please note that you’ll need Git to be installed on both your machine and the server before continuing. To install Git on the server, run the following command.

sudo aptitude install git-core

Also, for Rails assets pipeline to work properly, a JavaScript runtime has to be installed. There are 2 ways to achieve that, either by installing NodeJS or by including therubyracer gem in the application’s Gemfile. I prefer the former, because it’s done once on the server, in case it’ll be used to serve multiple Rails applications. To install NodeJS, run the following command.

sudo aptitude install nodejs

PS: I’m assuming a MySQL Server is already installed, and permissions are granted to a user called deployer for a schema called vps_production. The steps to install a MySQL Server on a fresh VPS and grant the required permissions will be discussed later in the Bonus part of this tutorial.

 

4.1 Capify!

Start by adding the following line to your Gemfile, then run bundle install to install capistrano.

gem 'capistrano-rvm',   group: :development
gem 'capistrano-rails', group: :development

After your bundle is updated, a new exectuable cap will be available. Run the following command to configure your app to use Capistrano.

bundle exec cap install

The following directories and files will be created for you.

# The main configuration file for Capistrano
config/deploy.rb

# Environment specific configurations
config/deploy
config/deploy/staging.rb
config/deploy/production.rb

# A directory for including custom Capistrano tasks
lib/capistrano/tasks

# A file containing the necessary calls to load Capistrano's libraries
Capfile

To include the default Capistrano tasks for Rails included by the gem capistrano-rails, make sure to add/uncomment the following lines in your Capfile.

require 'capistrano/rvm'              # since we're using RVM on the server
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

# Ensure nested .rake files are loaded as well
Dir.glob('lib/capistrano/tasks/**/*.rake').each { |r| import r }

Now open up config/deploy.rb in your favorite text editor (Mine is Sublime Text 2). Make the necessary changes to match the following. (Diff: deploy.rb)

set :application, 'vps'
set :repo_url, 'https://github.com/RaMin0/v.ps.git'

set :deploy_to, '/home/deployer/public_html/v.ps'

set :linked_files, %w{config/database.yml config/secrets.yml .env}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

# Configure: capistrano/rvm
set :rvm_ruby_version, '2.1.0'

namespace :deploy do
  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      # Your restart mechanism here, for example:
      execute :touch, release_path.join('tmp/restart.txt')
    end
  end

  after :publishing, :restart
end

Now open up config/deploy/production.rb and replace its contents with the following.

server 'v.ps', roles: %w{web app db}
set :ssh_options, user: 'deployer', port: 6311

Let’s make sure the server is ready to receive our first deployment. Capistrano creates a directory structure that makes it able to handle multiple releases and easily switch between them. Also, it handles common directories and files that are shared between the releases, things like logs, assets, configuration files, …etc. Run the following command.

bundle exec cap production deploy:check:directories deploy:check:linked_dirs deploy:check:make_linked_dirs

This will create the following tree under /home/deployer/public_html/v.ps.

.
├── releases           # contains the different releases/deployment
└── shared             # contains shared directories and files
    ├── bin
    ├── config         # contains shared configurations, such as database.yml, ...etc.
    ├── log            # contains shared log files
    ├── public
    │   └── system     # contains files uploaded using (e.g. Carrierwave)
    ├── tmp            # contains temporary files
    │   ├── cache
    │   ├── pids
    │   └── sockets
    └── vendor
        └── bundle     # contains the bundled gems created by bundler

Capistrano totally automates the deployment process and makes it a client-side process, but the only manual thing that has to be done on the server itself is creating the shared configuration files, such as database.yml, the first time an application is deployed. I usually keep an example of these files in the repository (by prefixing their names with an underscore), and add the original files to .gitignore. Now SSH into the server, then cd to /home/deployer/public_html/v.ps/shared/config and create the file database.yml with the following contents. This file will be automatically symlinked to the application’s config directory during each deployment, which will eliminate the need to create it each and every time you want to deploy your application.

# MySQL.  Versions 5.0+ are recommended.
#
# Install the MYSQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.0/en/old-client.html
#
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  # username: root
  # password:
  # socket: /tmp/mysql.sock

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV["DATABASE_URL"] %>
#
production:
  <<: *default
  url: <%= ENV['VPS_PRODUCTION_DATABASE_URL'] %>

While we’re at it, create the file secrets.yml with the following contents.

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["VPS_PRODUCTION_SECRET_KEY_BASE"] %> # Generate one using `rake secret`.

Since Rails 4.1, secret stuff such as database credentials and secret hashes are expected to be set as environment variables, to keep them “safe”. As you might have noticed in the production section of the database.yml we just created, the database/url is expected to be the value of a preset environment variable called VPS_PRODUCTION_DATABASE_URL. Same goes for the secret_key_base in secrets.yml which requires the existence of an environment variable called VPS_PRODUCTION_SECRET_KEY_BASE.

Handling environments variable can be pretty tiresome, but thankfully, there is a gem that makes it easy to set/get them. Add the following line to your Gemfile and run bundle install to install dotenv-deployment.

gem 'dotenv'
gem 'dotenv-deployment', require: 'dotenv/deployment'

dotenv loads entries from a .env file present in the root of the application into the ENV variable that Rails uses to access environment variables. Maintaining this file is much easier that dealing directly with the environment variables.

Since environment variables are not expected to change between deployments, let’s add our .env file to the shared directory on the server with the following contents.

VPS_PRODUCTION_DATABASE_URL: mysql2://deployer@localhost/vps_production
VPS_PRODUCTION_SECRET_KEY_BASE: ce8c364048650a65006d5ec4ce344b66df642ca729a53e9720a3ae61674c9819198a3bdcb3b4b586481d762a8cfc80f9686a7216d4619d0ec7cc2a6047999440

3 .. 2 .. 1 ….. Deploy!

bundle exec cap production deploy

 

References

Ramy Aboul Naga (@RaMin0)

A senior web apps developer with 6+ years of experience, who seeks to enrich the aspects of the web, to make it a more friendly environment for those who experience its advantages and nourishing knowledgebase.

3 thoughts to “Deploying Rails Apps using Capistrano (Part 3)”

  1. Thank you to your blog send. Manley and I materialize to be discount in support of only a pristine e book resting on this topic and your blog post has complete every of us to save heaps of money. Your ideas really resolved every one our questions. In fact, better than what we had established prematurely of the time we discovered your first-rate blog. My relate and that i straight away not have doubts and in addition a worried opinion because you have really attended to apiece of our needs by this article. Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *