Deploying Ruby applications

Overview

This document is a hands-on guide to deploying a simple Ruby application in tsuru. The example application will be a very simple Rails project associated to a MySQL service.

Creating the app

To create an app, you use the command app create:

$ tsuru app create <app-name> <app-platform>

For Ruby, the app platform is ruby! Let’s be over creative and develop a never-developed tutorial-app: a blog, and its name will also be very creative, let’s call it “blog”:

$ tsuru app create blog ruby

To list all available platforms, use the command platform list.

You can see all your applications using the command app list:

$ tsuru app list
+-------------+-------------------------+-------------+
| Application | Units State Summary     | Address     |
+-------------+-------------------------+-------------+
| blog        | 0 of 0 units in-service |             |
+-------------+-------------------------+-------------+

Application code

This document will not focus on how to write a blog with Rails, you can clone the entire source direct from GitHub: https://github.com/tsuru/tsuru-ruby-sample. Here is what we did for the project:

  1. Create the project (rails new blog)
  2. Generate the scaffold for Post (rails generate scaffold Post title:string body:text)

Deployment

You can just run tsuru app deploy command and your project will be deployed:

$ tsuru app deploy -a blog .
 /home/application/current /
 Fetching gem metadata from https://rubygems.org/.........
 Fetching gem metadata from https://rubygems.org/..
  ---> App will be restarted, please check its log for more details...

Now you can check your deployed app running tsuru app info

$ tsuru app info
Application: blog
Platform: ruby
Teams: admin
Address: blog.192.168.50.4.nip.io
Owner: admin@example.com
Team owner: admin
Deploys: 0
Pool: theonepool
Units: 1
+------------------------+---------+
| Unit                   | State   |
+------------------------+---------+
| blog-eab5151eff-151eff | started |
+------------------------+---------+

App Plan:
+---------------+--------+-----------+---------+
| Name          | Memory | Cpu Share | Default |
+---------------+--------+-----------+---------+
| autogenerated | 0 MB   | 100       | false   |
+---------------+--------+-----------+---------+

Listing dependencies

In the last section we omitted the dependencies step of deploy. In tsuru, an application can have two kinds of dependencies:

  • Operating system dependencies, represented by packages in the package manager of the underlying operating system (e.g.: yum and apt-get);
  • Platform dependencies, represented by packages in the package manager of the platform/language (in Ruby, bundler).

All apt-get dependencies must be specified in a requirements.apt file, located in the root of your application, and ruby dependencies must be located in a file called Gemfile, also in the root of the application. Since we will use MySQL with Rails, we need to install mysql package using gem, and this package depends on an apt-get package: libmysqlclient-dev, so here is how requirements.apt looks like:

libmysqlclient-dev

And here is Gemfile:

source 'https://rubygems.org'

gem 'rails', '3.2.13'
gem 'mysql'
gem 'sass-rails',   '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
gem 'therubyracer', platforms: 'ruby'
gem 'uglifier', '>= 1.0.3'
gem 'jquery-rails'

You can see the complete output of installing these dependencies below:

$ tsuru app deploy -a blog .
#####################################
#                OMIT               #
#####################################
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  libmysqlclient18 mysql-common
The following NEW packages will be installed:
  libmysqlclient-dev libmysqlclient18 mysql-common
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 2360 kB of archives.
After this operation, 9289 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ quantal/main mysql-common all 5.5.27-0ubuntu2 [13.7 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ quantal/main libmysqlclient18 amd64 5.5.27-0ubuntu2 [949 kB]
Get:3 http://archive.ubuntu.com/ubuntu/ quantal/main libmysqlclient-dev amd64 5.5.27-0ubuntu2 [1398 kB]
Fetched 2360 kB in 2s (1112 kB/s)
Selecting previously unselected package mysql-common.
(Reading database ... 41063 files and directories currently installed.)
Unpacking mysql-common (from .../mysql-common_5.5.27-0ubuntu2_all.deb) ...
Selecting previously unselected package libmysqlclient18:amd64.
Unpacking libmysqlclient18:amd64 (from .../libmysqlclient18_5.5.27-0ubuntu2_amd64.deb) ...
Selecting previously unselected package libmysqlclient-dev.
Unpacking libmysqlclient-dev (from .../libmysqlclient-dev_5.5.27-0ubuntu2_amd64.deb) ...
Setting up mysql-common (5.5.27-0ubuntu2) ...
Setting up libmysqlclient18:amd64 (5.5.27-0ubuntu2) ...
Setting up libmysqlclient-dev (5.5.27-0ubuntu2) ...
Processing triggers for libc-bin ...
ldconfig deferred processing now taking place
/home/application/current /
Fetching gem metadata from https://rubygems.org/..........
Fetching gem metadata from https://rubygems.org/..
Using rake (10.1.0)
Using i18n (0.6.1)
Using multi_json (1.7.8)
Using activesupport (3.2.13)
Using builder (3.0.4)
Using activemodel (3.2.13)
Using erubis (2.7.0)
Using journey (1.0.4)
Using rack (1.4.5)
Using rack-cache (1.2)
Using rack-test (0.6.2)
Using hike (1.2.3)
Using tilt (1.4.1)
Using sprockets (2.2.2)
Using actionpack (3.2.13)
Using mime-types (1.23)
Using polyglot (0.3.3)
Using treetop (1.4.14)
Using mail (2.5.4)
Using actionmailer (3.2.13)
Using arel (3.0.2)
Using tzinfo (0.3.37)
Using activerecord (3.2.13)
Using activeresource (3.2.13)
Using coffee-script-source (1.6.3)
Using execjs (1.4.0)
Using coffee-script (2.2.0)
Using rack-ssl (1.3.3)
Using json (1.8.0)
Using rdoc (3.12.2)
Using thor (0.18.1)
Using railties (3.2.13)
Using coffee-rails (3.2.2)
Using jquery-rails (3.0.4)
Installing libv8 (3.11.8.17)
Installing mysql (2.9.1)
Using bundler (1.3.5)
Using rails (3.2.13)
Installing ref (1.0.5)
Using sass (3.2.10)
Using sass-rails (3.2.6)
Installing therubyracer (0.11.4)
Installing uglifier (2.1.2)
Your bundle is complete!
Gems in the groups test and development were not installed.
It was installed into ./vendor/bundle
#####################################
#                OMIT               #
#####################################

Running the application

As you can see, in the deploy output there is a step described as “Restarting your app”. In this step, tsuru will restart your app if it’s running, or start it if it’s not. But how does tsuru start an application? That’s very simple, it uses a Procfile (a concept stolen from Foreman). In this Procfile, you describe how your application should be started. Here is how the Procfile should look like:

web: bundle exec rails server -p $PORT -e production

Now running another deploy:

$ tsuru app deploy -a blog .

Now that the app is deployed, you can access it from your browser, getting the IP or host listed in app list and opening it. For example, in the list below:

$ tsuru app list
+-------------+-------------------------+---------------------+
| Application | Units State Summary     | Address             |
+-------------+-------------------------+---------------------+
| blog        | 1 of 1 units in-service | blog.cloud.tsuru.io |
+-------------+-------------------------+---------------------+

Deployment hooks

It would be boring to manually run rake db:migrate after every deployment. So we can configure an automatic hook to always run before or after the app restarts.

tsuru parses a file called tsuru.yaml and runs restart hooks. As the extension suggests, this is a YAML file, that contains a list of commands that should run before and after the restart. Here is our example of tsuru.yaml:

hooks:
  restart:
    before:
      - RAILS_ENV=production bundle exec rake db:migrate

For more details, check the hooks documentation.

tsuru will look for the file in the root of the project. Let’s commit and deploy it:

$ tsuru app deploy -a blog .

It is necessary to compile de assets before the app restart. To do it we can use the rake assets:precompile command. Then let’s add the command to compile the assets in tsuru.yaml:

hooks:
  build:
    - RAILS_ENV=production bundle exec rake assets:precompile
$ tsuru app deploy -a blog .

It’s done! Now we have a Rails project deployed on tsuru.

Now we can access your blog app in the URL returned in app info.

Going further

For more information, you can dig into the tsuru docs, or read the complete instructions on how to use the tsuru command.