18 Mar 2015 · Software Engineering · 9 min read

    How to Deploy Sinatra Applications with Capistrano

    Contents

    Introduction

    Rails is great, but often it is not the best tool for creating smaller applications. For smaller size apps, especially those that are mainly focused around pure JSON APIs, Sinatra is often a far better option. Unfortunately, the majority of learning resources in the Ruby ecosystem are focused on Rails, and leave out the details for other frameworks. One of the essential web application development processes is application deployment.

    This article will try to teach you about Capistrano— a remote server automation tool that lets you execute arbitrarily tasks over SSH. It is very useful for automating your application’s deployment and setting up common repetitive tasks for remote machines.

    Prerequisites

    This article will assume that you are using the following tools to develop and serve your applications:

    Installing Capistrano

    The first step we need to take is to add Capistrano as our applications dependency. To achieve that we will add the following line to our Gemfile:

    gem "capistrano"

    Install the above gem by executing bundle install in your shell.

    To get started with Capistrano we need to “capify” our Sinatra project. In other words we need to let Capistrano create the configuration files and the file structure that it needs. To do this, run the following command in your application’s root directory:

    bundle exec cap install

    The above will append the following file structure to your application:

    ├── Capfile
    ├── config
    │   ├── deploy
    │   │   ├── production.rb
    │   │   └── staging.rb
    │   └── deploy.rb
    └── lib
        └── capistrano
                └── tasks

    With this we are ready to use Capistrano with your Sinatra app.

    Installing Addition Dependencies

    We will install two addition dependencies for easier deployment management, the capistrano-bundler and the capistrano-passenger gems. These gems will help us to install our Bundler-defined dependencies on the remote server and to start/restart our Phusion Passenger based application.

    Let’s add them to the Gemfile:

    gem "capistrano-bundler"
    gem "capistrano-passenger"

    Install them by executing bundle install.

    At this point we need to inform Capistrano to include those two dependencies. We will achieve this by editing the Capfile in our project’s root directory.

    The Capfile is the first file that Capistrano loads and it is responsible for loading all the dependencies that your application needs for deployment. By default it includes deployment commands and loads all the tasks from the lib/tasks directory.

    Luckily, these gems are so common that the Capfile already includes them, but comments them out by default. Uncomment those two lines, to require the gems. Those lines should look like this:

    require "capistrano/bundler"
    require "capistrano/passenger"

    The next time you execute a Capistrano task, these gems will be available in your Capistrano scripts.

    Defining Servers and Roles

    By default, Capistrano will create two types of server environments for our application: production and staging. The configuration file for each of those environments can be found in the config/deploy/ directory.

    In these files you can define the server you are going to deploy to. For example, we can define a server for our staging environment if we add the following line to the config/deploy/staging.rb file:

    server "staging.sinatra-app.com", :user => "deployer", :roles => %{web app}

    In the above command we have defined a server that lives under the staging.sinatra-app.com domain. We could have used the IP of the server instead, like in the following example:

    server "111.222.333.444", :user => "deployer", :roles => %{web app}

    The next thing we want to notice is the user parameter. That option tells Capistrano to use the deployer Unix user on the remote server. Of course this can also be changed to match the user on your specific deployment machine.

    In case we have several machines where we want to deploy simultaneously, we can simply repeat the server command to add additional servers:

    server "staging1.sinatra-app.com", :user => "deployer", :roles => %{web app}
    server "staging2.sinatra-app.com", :user => "deployer", :roles => %{web app}
    server "staging3.sinatra-app.com", :user => "deployer", :roles => %{web app}

    A common option when defining the servers for the production environment is to hardcode the branch to master, and thus make sure that we only deploy the latest stable build. We can define the branch like this:

    server "staging.sinatra-app.com", :user => "deployer", :roles => %{web app}
    
    set :branch, "master"

    The last argument that you can see defined in the above examples is the roles option. With it we can limit the tasks that are run on some of the servers. For example, if we keep our database on a separate machine, we can define that server like this:

    server "staging.sinatra-app.com", :user => "deployer", :roles => %{web db}

    In the above line we specified that on this server we want to execute only the database specific tasks.

    Configuring the Deployment

    Capistrano’s main configuration file is the config/deploy.rb. This is the file where we define the name of our application and a URL to its repository.

    Let’s say that our application is called test-application and is hosted on this Git server git@github.com/tester/test-application.git. We then need to set the application and repo_url options in our config/deploy.rb file:

    set :application, "test-application"
    set :repo_url, "git@github.com/tester/test-application.git"

    When Capistrano deploys our application, it is actually executing git pull over an SSH connection. That is why we need to provide the URL to our project’s repository.

    The default deploy path is inside the /var/www directory, but often we want to deploy our applications to the home directory of the remote user. For example, if our user on the remote server is joe we would set this path to be:

    set :deploy_to, "/home/joe/test-application"

    The above line will tell Capistrano to deploy the project under the test-application directory, in the home of the joe user.

    When Capistrano deploys your application, it will create a releases, current and shared directory in the deploy_to path. The current path will be a symlink to the latest release in the releases directory, while the shared directory contains files that are symlinked into every release.

    Managing Secret Files

    Often, our application depends on external APIs or a database connection. Both of them usually require a secret password for access. Because we should not keep those passwords checked in to our repository, we will use the linked files mechanism provided with Capistrano.

    First we need to define a path of our secret file, relative to our project’s root directory. For example, we could decide to use config/secrets.yml with the following content:

    API_KEY: xyz

    With that in place, first we will add that path to our .gitignore file to prevent it from checking in to our repository:

    # .gitignore
    config/secrets.yml
    

    Secondly, we need to SSH into our server and store the content of the file in the shared directory to the config/secrets.yml path so it will be linked into each release. The full path of our remote secrets file should be test-application/shared/config/secrets.yml if test-application is the name of our application.

    The next step is to tell Capistrano that this file is linked into each release with the following option in the config/deploy.rb file:

    set :linked_files, ["config/secrets.yml"]

    The above line tells Capistrano to include the secret files from the shared directory into the project’s config directory on each release.

    Setting up SSH Keys

    Capistrano needs two SSH key pairs to deploy to your servers.

    The first SSH key pair will make sure we are able to enter the remote machine where we want to deploy. It is always a good practice to check your connection before deployment by manually entering the server. For example, if the name of the remote Unix user you are using for deployment is joe we could try to SSH into the remote server with:

    ssh joe@1.2.3.4

    Where 1.2.3.4 is the IP of the remote server.

    The second SSH key pair is concerned with pulling from our git repository. Each time we deploy, Capistrano will pull a fresh copy of our repository into the deploy path on the remote machine. For this to succeed we need to set up a private key on the remote machine that has access to our repository.

    Capistrano also offers an alternative way to pull from our repository. It can use our local keys, forward them in our SSH session to the remote server, and use them to when pulling the project. This way we don’t need to store our SSH keys on the remote server and tighten our security a little bit.

    To achieve this, we need to enable the following option in our config/deploy.rb file:

    set :ssh_options, { :forward_agent => true }

    Before we deploy, we also need to make sure that our SSH agent is running. To run the agent and put your keys into the agent type the following in your command line:

    eval ssh-agent
    ssh-add ~/.ssh/id_rsa

    To test if we have all the necessary keys we could SSH into the remote machine and try to pull from our repository.

    igor@localhost ~ $ ssh joe@1.2.3.4
    
    joe@1.2.3.4 ~ $ git pull git@github.com/tester/test-application.git

    If everything works without issues, we are ready to deploy.

    Deployment

    With the above steps in place we can deploy our application with a simple shell command:

    bundle exec cap production deploy

    That line will do several things on the remote server:

    • It will create the project’s directory and the releases, shared and current directories.
    • Install all bundle dependencies with capistrano-bundle.
    • Run/restart Passenger with the capistrano-passenger task.

    To deploy to a staging server environment, simply:

    bundle exec cap staging deploy

    Conclusion

    Capistrano is an excellent tool for automating your application deployment. There is of course much more to Capistrano than what we covered in this tutorial. Some great resources are:

    P.S. Would you like to learn how to build sustainable Rails apps and ship more often? We’ve recently published an ebook covering just that — “Rails Testing Handbook”. Learn more and download a free copy.

    mm
    Writen by:
    Chief Architect at Semaphore. A decade of experience in dev productivity, helping close to 50,000 organizations with operational excellence.

    Leave a Reply

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

    Star us on GitHub