Torquebox 3, Rails 4 - zero downtime deployments on Ubuntu 12.04

Torquebox is JRuby application server based on JBoss. What’s an application server? It’s a bundle of web server, messaging queue, JRuby runtime and websocket infrastructure. It is pretty cool and we use it in AmberBit to deploy a few hight-traffic web applications we developed for our clients.

As nice as it is, having it’s roots in Java world, and being sponsored by Red Hat (who we send lots of love doing that, BTW), Torquebox is also quite hard to set up and get running on Ubuntu 12.04, unless you know exactly what you are doing.

This is a step-by-step guide on setting up Torquebox 3.0.1 on Ubuntu 12.04. I am using DigitalOcean VPS with 2GB RAM, but anything else will also do the job. Just give it some RAM, remember: JRuby and Java are memory-hungry!

Dependencies

We’ll need a few tools, most notably git, JavaScript runtime and JRE.

apt-get install git unzip nodejs openjdk-7-jre-headless wget

Torquebox installation

wget http://torquebox.org/release/org/torquebox/torquebox-dist/3.0.1/torquebox-dist-3.0.1-bin.zip
unzip torquebox-dist-3.0.1-bin.zip
mv torquebox-3.0.1/ /opt/torquebox

We want to create “torquebox” user in the system and make him owner of Torquebox installation with:

adduser torquebox
chown torquebox.torquebox -R /opt/torquebox/

To set up environment variables, we create /etc/profile.d/torquebox.sh with following contents:

export TORQUEBOX_HOME=/opt/torquebox
export JBOSS_HOME=$TORQUEBOX_HOME/jboss
export JRUBY_HOME=$TORQUEBOX_HOME/jruby
export PATH=$JRUBY_HOME/bin:$PATH

Do not forget to source that file or sign out and log into your server again so the environment variables should pick up. We can verify that our JRuby/Torquebox installation is working with:

jruby -S irb

If it brings up IRB prompt, our Torquebox/JRuby installation is probably good to go.

One last bit is to set up init scripts, you can do it with:

cd /opt/torquebox
rake torquebox:upstart:install

This should create the /etc/init/torquebox.conf. Unfortunately the file did not autostart Torquebox for me, as it depends on non-existing service - I think it’s created for different version of Ubuntu. I had to slightly change it to work on our company’s VPS:

/etc/init/torquebox.conf:

description "This is an upstart job file for TorqueBox"

pre-start script
bash << "EOF"
  mkdir -p /var/log/torquebox
  chown -R torquebox /var/log/torquebox
EOF
end script

start on started network-interface
stop on stopped network-interface
respawn

limit nofile 4096 4096

script
bash << "EOF"
  su - torquebox
  /opt/torquebox/jboss/bin/standalone.sh >> /var/log/torquebox/torquebox.log 2>&1
EOF
end script

We can now start our Torquebox application server with:

service torquebox start

Torquebox will also be started automatically on server restarts. This means our Ruby on Rails web applications will start itself up even when you need to do a hard restart. Pretty sweet.

Torquebox will listen on http://localhost:8080 and happily serve blank white 404 page for you. It will not listen on any public interface by default. If you, however, want it to - change all occurences of 127.0.0.1 to 0.0.0.0 in file /opt/torquebox/jboss/standalone/configuration/standalone.xml.

Nginx as a proxy

You don’t need Nginx to do the proxy web traffic for you. I just like it. You can use iptables rule to forward your public IP port 80 to port 8080, use rinetd, run Torquebox as root (sic!) and bind directly to port 80 etc. Nginx, however, gives you some nice options like setting maximum body size, logging, more control over HTTP headers.

apt-get install nginx

/etc/nginx/sites-enabled/default:

server {
  listen 80;
  server_name example.com; # do change me!
    location / {
      access_log off;
      proxy_pass http://127.0.0.1:8080;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Restart your Nginx and you’ll server should now also serve you a white blank web page, directly from Torquebox:

service nginx restart

Ruby on Rails application and Capistrano (v2) recipe

Capistrano is one of the simplest ways to deploy Ruby on Rails web applications. We will use version 2 here, since Torquebox has integration for this version at the moment. The recipe, however, should work with some modifications on Capistrano 3 as well.

Add these two lines to your Gemfile:

gem 'capistrano', '~> 2.15.4'
gem 'torquebox'
gem 'torquebox-capistrano-support'

and run “bundle install”.

You also want to add this Capistrano recipe to your Ruby on Rails application (config/deploy.rb):

require 'torquebox-capistrano-support'
require 'bundler/capistrano'

set :application, "myapp"

set :torquebox_home,    '/opt/torquebox'
set :jboss_home,        "#{torquebox_home}/jboss"
set :jruby_home,        "#{torquebox_home}/jruby"
set :bundle_dir, "~/shared"

set :default_environment, { 
  "TORQUEBOX_HOME"=>"/opt/torquebox",
  "JBOSS_HOME"=>"/opt/torquebox/jboss",
  "JRUBY_HOME"=>"/opt/torquebox/jruby",
  "PATH"=>"/opt/torquebox/jruby/bin:$PATH"
}

role :web, "XXX.XXX.XXX.XXX"                       # Your IP here
role :app, "XXX.XXX.XXX.XXX" 
role :db,  "XXX.XXX.XXX.XXX", :primary => true

default_run_options[:pty] = true
ssh_options[:forward_agent] = true

set :user, -> { "torquebox" }
set :group, -> { "torquebox" }
set :use_sudo, false

set :scm, :git
set :stage, "production"
set :repository,  "git://github.com/yourcompany/yourwebapp.git" # Address of your Git repository goes here

set :deploy_to, -> { "/home/#{user}" } # Change this if you want to deploy to other directory
set :deploy_via, :remote_cache
set :deploy_env, -> { "production" }
set :rails_env, -> { "production" }
set :app_context,       "/"

namespace :deploy do
  desc "Symlink shared configs and folders on each release."
  task :finalize_update do
    # If this task fails, please make sure you put config/database.yml and config/app_config.yml in the shared directory
    run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
  end

  desc "Start the server"
  task :start do
    run "cd #{current_path} && RAILS_ENV=production bundle exec torquebox deploy --name=myapp" # change name
  end

  task :stop do
    run "cd #{current_path} && RAILS_ENV=production bundle exec torquebox undeploy --name=myapp" # change name
  end

  desc "Hot-restart the server"
  task :restart do
    run "cd #{current_path} && touch tmp/restart.txt" # hot deployment made easy!
  end
end

Don’t forget to customize it to your needs, mind the comments above. There are some important bits here:

  • deploy:start and deploy:stop tasks - you should always specify name of your app/knob file here. Otherwise it will try to deploy the app multiple times under the same context with different names and it will fail badly except first deployment.
  • Torquebox 3 will do zero downtime deployments only if you overwrite restart task or ‘touch tmp/restart.txt’ file manually. This is consistent with behavior of Phusion Passenger. The default Torquebox’s restart task will shutdown the old application before booting new one, which can save you some RAM but users can notice the app being down for a few seconds.

Now set up your deployment files structure:

bundle exec cap deploy:setup

SSH to the server and place /home/torquebox/shared/config/database.yml file:

production:
  adapter: jdbcpostgresql
  encoding: unicode
  database: myapp-production
  pool: 5
  username: torquebox
  password: mypassword
  host: localhost
  port: 5432

You need to amend the config file above to match your PostgreSQL settings. Of course you can also use MySQL if you really want, we won’t judge you ;).

Now you can do first deployment:

bundle exec cap deploy:cold

And all the following deployments, you simply need to run:

bundle exec cap deploy

Summary

We just set up and deployed an Ruby on Rails app to Torquebox running as a service on Ubuntu 12.04. The web application will be deployed with virtually zero downtime and we can use full potential of Torquebox, including messaging, services, scheduler and clustering. If you are not using JRuby and Torquebox yet in production for your company - consider it as an option.

More info

  • http://torquebox.org/

by Hubert Łępicki, twitter: @hubertlepicki

comments powered by Disqus