Chef: RVM + Ruby Enterprise Edition as Default Ruby

by on September 7, 2010 · 15 comments

The opscode chef bootstrap installs Matz Ruby on the node automatically. There are cookbooks for installing ruby enterprise edition on a node, but they create a separate Ruby “universe” on your box: You will have to be very careful how you install gems to make sure they are used by either the default Ruby or by REE. As this really bothered me, I created a little cookbook which installs Ruby Enterprise Edition as the default Ruby using Ruby Version Manager (RVM) and Chef. This gives me the best of both worlds: REEs stability and speed as well as a sane way of managing gems.

Bootstrap Your Chef Node

First, you need to bootstrap your node using knife:

$ knife bootstrap www.example.com

This will install Ruby on your node and register the node at the chef server. Now you’re ready to run cookbooks on that node.

The rvm_ree_default cookbook

My cookbook consists of a default recipe and one file to be uploaded to your node. Here is the rvm_ree_default/recipes/default.rb:

#
# Cookbook Name:: rvm_ree_default
# Recipe:: default
#
# Copyright 2010, Matthias Marschall
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
# see: http://li109-47.members.linode.com/blog/
package "curl"
package "git-core"
 
include_recipe "build-essential"
 
%w(libreadline5-dev zlib1g-dev libssl-dev libxml2-dev libxslt1-dev).each do |pkg|
  package pkg
end
 
bash "install RVM" do
  user "root"
  code "bash < <( curl -L http://bit.ly/rvm-install-system-wide )"
  not_if "rvm --version"
end
cookbook_file "/etc/profile.d/rvm.sh"
 
bash "install REE in RVM" do
  user "root"
  code "rvm install ree"
  not_if "rvm list | grep ree"
end
 
bash "make REE the default ruby" do
  user "root"
  code "rvm --default ree"
end
 
gem_package "chef" # re-install the chef gem into REE to enable subsequent chef-client runs

It installs the prerequisite packages and uses the rvm-install-system-wide script to get RVM installed. To enable RVM in all shells, it puts the rvm.sh into the /etc/profile.d. Then, it uses RVM to install REE and make it the default Ruby on the box. As a last step, it installs the chef gem again as the brand new REE doesn’t yet have it. Now, REE is your default Ruby and all commands like irb, gem, etc. work as expected using REE.

Here is the rvm_ree_default/files/default/rvm.sh

[[ -s "/usr/local/lib/rvm" ]] && . "/usr/local/lib/rvm"  # This loads RVM into a shell session.

Don’t forget to $ knife cookbook upload rvm_ree_default before trying to use it!

Install RVM at Bootstrap Time

The above way helps if you already have chef nodes up and running and want to switch them to REE. If you install a brand new node, you can use a bootstrap template to install REE with RVM as default. Here you go:

bash -c '
if [ ! -f /usr/local/bin/chef-client ]; then
  apt-get update
  apt-get install -y git-core curl
  apt-get install -y build-essential binutils-doc gcc autoconf flex bison
  apt-get install -y libreadline5-dev zlib1g-dev libssl-dev libxml2-dev libxslt1-dev
  bash < <( curl -L http://bit.ly/rvm-install-system-wide )
  (
  cat <<'EOP'
[[ -s "/usr/local/lib/rvm" ]] && . "/usr/local/lib/rvm"  # This loads RVM into a shell session.
EOP
  ) > /etc/profile.d/rvm.sh
  source /etc/profile
  rvm install ree
  rvm --default ree
  gem install ohai chef --no-rdoc --no-ri --verbose <%= '--prerelease' if @config[:prerelease] %>
  ln -nfs $(which chef-client) /usr/bin/chef-client
fi
 
mkdir -p /etc/chef
 
(
cat <<'EOP'
<%= IO.read(Chef::Config[:validation_key]) %>
EOP
) > /tmp/validation.pem
awk NF /tmp/validation.pem > /etc/chef/validation.pem
rm /tmp/validation.pem
 
(
cat <<'EOP'
log_level        :info
log_location     STDOUT
chef_server_url  "<%= Chef::Config[:chef_server_url] %>"
validation_client_name "<%= Chef::Config[:validation_client_name] %>"
<% if @config[:chef_node_name] == nil %>
# Using default node name"
<% else %>
node_name "<%= @config[:chef_node_name] %>"
<% end %> 
EOP
) > /etc/chef/client.rb
 
(
cat <<'EOP'
<%= { "run_list" => @run_list }.to_json %>
EOP
) > /etc/chef/first-boot.json
 
chef-client -j /etc/chef/first-boot.json'

The upper part of the script is the interesting one here: First, it installs the required packages, then RVM and then it uses RVM to install Ruby Enterprise Edition and makes it the default ruby.

The symlink to chef-client is only necessary if you want to use sudo (without -i option) to call chef-client (sudo without -i won’t load the enviroment so RVM will not be available for your sudo commands).

To bootstrap your node using the above script, save it in your chef repo under bootstrap/ubuntu10.04-rvm-ree.erb and run

$ knife bootstrap www.example.com -t bootstrap/ubuntu10.04-rvm-ree.erb

No matter whether you want to switch an existing node to Ruby Enterprise Edition or bootstrap your node using it, the Ruby Version Manager helps you keep a clean system. And, it offers you the possibility to smoothly upgrade to another ruby version later or even use separate gem sets for different applications. Does it work for you? Please leave a comment or tweet this post.

Did you enjoy this article? Get new articles for free by email:

Comments

  1. Blake says

    I’m having some trouble with your test:

    not_if “rvm –version”

    I get error output like this:

    ERROR: bash[Install RVM] (/srv/chef/cache/cookbooks/modcloth/recipes/default.rb:38:in `from_file’) had an error:
    No such file or directory – rvm –version

  2. says

    @M yes, we’re using tynt.com to track user interaction with our blog. I’m sorry that it is annoying when copying code but there is no way to avoid that for special parts of the site. We would have to disable it completely, which is not an option right now.

  3. says

    ok, contributed the knife bootstrap scripts to install Ruby Enterprise Edition (REE) from dpkg as well as with Ruby Version Manager (RVM) and the rvm_ree_default cookbook.

  4. says

    @Dennis Great job! The separation of the cookbooks is really nice.

    Without the curl and git-core dependencies, it would not install on a minimal ubuntu install as it can be found on quite a few rented servers.

    And for me it was necessary to install the chef gem into the new default ruby.

  5. Anthony Burton says

    When I bootstrap RVM+ree using either knife ec2 server create…, or knife bootstrap… methods, and on chef-client runs where RVM+ree are installed, the process always causes either knife or chef-client to not return:


    node.amazonws.com ree-1.8.7-2010.02 – #installing

    I never get past that successfully. Any hints? Can I be the only one seeing this behavior?

Trackbacks