A Shining Ruby in Production Environments
Even the most beautiful Rails application can lose its elegance if not deployed correctly. Like other Ruby frameworks or languages, such as Sinatra, Rails is based on the Rack interface. This article provides a basic introduction to Rack hosting and Rack-based application deployments.
When Rails first was released in 2005, developers exulted. Finally, a comprehensive open-source framework for Web applications was available, packed with a set of tools making Web development fast, productive and fun. Rails has the reputation of being a "heaven for developers", but despite the many facilities it provides for avoiding typical and repetitive tasks, there is still a weak spot: deployment. Deploying a Rails application is not a smooth matter. Everyone knows that Rails applications will be published on-line one day, but not precisely how.
Platform as a Service (PaaS)
Developers often choose to purchase hosting space as Platform as a Service (for example, Heroku, OpenShift or EngineYard). PaaS is marvelous as it provides a ready-to-use environment containing a full stack of software dependencies. Publishing on a PaaS platform is, as a rule, easy, fast and everything tends to work (almost) immediately. But there are at least two cases when PaaS won't fit your needs: when applications must be kept in the customer's private infrastructure or when applications have superior hardware or software requirements—for instance, when you need a specific software service not supported by your PaaS provider.
In such situations, you must implement custom virtual server configurations and custom deployment procedures. You can deploy Rails applications on servers or on virtual machines. The availability of entire cloud services like Amazon Web Services (AWS), which allow you to create complex infrastructures made of several Web servers, database servers and front-end balancing machines, is hugely growing in popularity. This approach is very flexible, although you must access, install and manage the operating system and the distribution packages, configure the network, activate the services, and so on and so forth. In this article, I describe the Rack-based hosting software requirements and some basic example configurations to implement automated Ruby hosting on a GNU/Linux server.
First, if you want to host Ruby software, you must install the Ruby platform. You can install Ruby and gems with apt-get or yum. It's easy, but when your application requires specific gem versions or specific interpreter versions, you will face a common problem. How can you satisfy these requests if your GNU/Linux distribution doesn't package those specific versions? Furthermore, how can you maintain multiple Ruby versions in a clean and repeatable manner?
You may think you can just download the Ruby platform and compile it manually. It's guaranteed that you can install the interpreter versions and the gem versions you need. Unfortunately, this is totally inconvenient. This kind of software management makes your configuration hard to update.
There are several solutions for overcoming these common issues. The one I find more reliable for server environments is named Ruby enVironment Manager (RVM). RVM comes packed with a set of scripts that helps you install and update the Ruby ecosystem.
Download RVM by issuing the following command as root:
# \curl -L https://get.rvm.io | bash -s stable
Despite the fact that it's recommended that you work with RVM using security
facilities as sudo, the
rvm executable must be available in your root
$PATH environment, so install it as root. For a multiuser RVM
installation, typical for servers, the software is kept by default in the
/usr/local/rvm directory, so you can remove the whole distribution safely
rm -fr /usr/local/rvm command.
Before proceeding with the Ruby
installation, make sure your system is ready to compile Ruby. Check
that you have the
rvm command available in your PATH (if not, log out and
log in again or reload your shell with
bash -s), and
$ sudo rvm requirements
RVM will install, through yum or apt-get, the required packages to compile the Ruby distribution. In this article, I use the stable official Ruby distribution called MRI, Matz Ruby Interpreter (derived from the name of Ruby's creator, Yukihiro Matsumoto).
Now, you'll likely need to add to your future Rubies some basic libraries
typically needed by some complex gems or software. Setting up such
libraries immediately will guarantee that the Ruby software will never
complain that the system libraries are old or incompatible, generating
annoying errors. Previously, you would have installed these extra packages via the
rvm pkg install <pkg> command, but now RVM
deprecates this. Instead, simply enable
autolibs to delegate to RVM the responsibility to build coherent and
$ sudo rvm autolibs enable
You finally are ready to provide your environment a full Ruby distribution. For example, let's install the latest stable version of the official MRI interpreter, the 2.0.0 version:
$ sudo rvm install 2.0.0
If everything goes well, the distribution is available for root and the system users. If not, it's commonly a $PATH problem, so adjust it in the /etc/profile.d, and also to avoid deployment pitfalls, verify that the $GEM_HOME variable is exported to the correct gem path. In practice, if something is not working properly, set the following variables like this:
if [ -d "/usr/local/rvm/bin" ] ; then PATH="/usr/local/rvm/gems/ruby-2.0.0-p353@global/bin: ↪/usr/local/rvm/bin:$PATH" GEM_HOME="/usr/local/rvm/gems/ruby-2.0.0-p353@global" fi
You can list the available Ruby versions with this command:
$ rvm list known
On a system running multiple Rubies, users and system processes may load other environment versions with a command like this:
$ rvm use jruby-1.7.1
And set the default system distribution in this way:
$ rvm --default use 2.0
The Web Server
Ruby on Rails, like Sinatra and many other popular Ruby frameworks or Domain Specific Languages, is based on an interface named Rack. Rack provides the minimal abstraction possible between Web servers supporting Ruby and Ruby frameworks. Rack is responsible for invoking the main instance of your application as specified in the startup file, config.ru.
So, a Web server hosting Ruby Web applications will have to understand how Rack talks. With a stable and clean Ruby environment, you're ready to build your Web server that is capable of speaking Rack.
With Ruby, you can choose between many Web servers. You may have heard of Mongrel, Unicorn, Thin, Reel or Goliath. For typical Rails deployments, Passenger is one of the most popular choices. It integrates well with Apache and Nginx, so in this example, let's set up an Apache + Passenger configuration.
Passenger, developed by Phusion, also formerly known as mod_rails or mod_rack, is a module that allows you to publish Ruby applications in the popular Web server containers Apache or Nginx. Passenger is available as a "community" free edition and as an enterprise release, which includes commercial support and advanced features.
If you chose to install Ruby through packages, Passenger is conveniently available through RPM or DEB repositories, and yum or apt-get will install all the required software.
On an RVM-customized system, to install the free version of Passenger, you need to add the gem through Ruby gems:
$ sudo gem install passenger
Now you can install the server module (the latest version at the time of this writing is 4.0.33) by executing a script provided by the gem:
Let's select Ruby only, and let's skip Python, Node.js and Meteor support. If your system misses software requirements, the script will give you a tip to the exact command line for yum or apt-get to meet those dependencies.
After some compile time, you will be introduced to Passenger configuration with useful and self-explanatory output. Specifically, copy to the directives that load Passenger into Apache in your main Apache configuration file (apache2.conf or httpd.conf):
LoadModule passenger_module /usr/local/rvm/gems/ruby-2.0.0-p353/gems/passenger-4.0.33/ ↪buildout/apache2/mod_passenger.so PassengerRoot /usr/local/rvm/gems/ruby-2.0.0-p353/gems/ ↪passenger-4.0.33 PassengerDefaultRuby /usr/local/rvm/wrappers/ruby-2.0.0-p353/ruby
Finally, restart Apache. Et voilà, now you can host Ruby Web applications.
If your goal is to host one or more Ruby applications on the same server, you should activate each instance as a virtual host. The most significant directive with Ruby hosting is the DocumentRoot. It's mandatory that it points to the public/ directory in the application's root project directory. The public/ directory is the default public path of a Rails application. So let's say you have a Kolobok application made in Rails, and you have to deploy it to the DNS zone kolobok.example.com on the kolobok.example.com server. Here is an example VirtualHost:
<VirtualHost *:80> ServerName kolobok.example.com DocumentRoot /srv/www/kolobok/public <Directory /srv/www/kolobok/public> # This relaxes Apache security settings. AllowOverride all # MultiViews must be turned off. Options -MultiViews </Directory> </VirtualHost> ]]> </code></pre>
Now, if you have put your application in /srv/www/kolobok, and it's well configured (configured and binded to the database and so on), enable the virtual host, reload Apache, and your application is published.
Automating Software Deployments
Ages ago, it was common to deploy Web applications by doing a bulk copy of files via FTP, from the developer's desktop to the server hosting space, or by downloading through Subversion or Git. Although this approach still works for simpler PHP applications, it won't fit more complex projects made using more complex frameworks, such as Rails.
The Ruby community provides several ways to accomplish the whole deploy task, and one very popular method uses Capistrano. Capistrano lets you write a set of "recipes" that will "cook" your application in the production environment. Common tasks executed by Capistrano are: 1) pulling the source code from a git or svn repository; 2) putting it in the right location; 3) checking if a bundle is needed and, if yes, bundling your gems; 4) checking if migrations are required and, if yes, running them; 5) checking if assets precompile is required and, if yes, precompiling; and 6) checking other Rake tasks you have defined and running them in order. If the whole recipe fails, Capistrano will keep the current software release in production; otherwise, it will substitute the latest release with the one you've just deployed. Capistrano is a largely tested and very reliable tool. You definitely can trust it.
To use Capistrano, you just need to install it through Ruby gems on the system where the deploy will be done (not on the server):
$ gem install capistrano
When Capistrano is available, you'll have two new binaries in your
PATH: capify and cap. With capify, you build your deploy skeleton.
cd to the
project directory and type:
$ capify .
This command creates a file named Capfile and a config/deploy.rb file. Capfile tells Capistrano where the right deploy.rb configuration file is. This is the file that includes your recipes, and typically it's kept in the project's config/ directory.
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
Practical books for the most technical people on the planet. Newly available books include:
- Agile Product Development by Ted Schmidt
- Improve Business Processes with an Enterprise Job Scheduler by Mike Diehl
- Finding Your Way: Mapping Your Network to Improve Manageability by Bill Childers
- DIY Commerce Site by Reven Lerner
Plus many more.
- Building a Multisourced Infrastructure Using OpenVPN
- Happy GPL Birthday VLC!
- Unikernels, Docker, and Why You Should Care
- diff -u: What's New in Kernel Development
- What's New in 3D Printing, Part III: the Software
- Giving Silos Their Due
- Controversy at the Linux Foundation
- Don't Burn Your Android Yet
- Non-Linux FOSS: Snk
- Firefox OS