Using rbenv to manage rubies
How I use rbenv to manage rubies and integrate with other tools
rbenv is great ¶
If you haven’t seen rbenv yet I highly recommend it. It is a small collection of shell scripts that lets you manage rubies on UNIX type machines. To date I’ve used rvm to install versions of ruby and manage gemsets. Don’t get me wrong - I’m hugely greatful to the work that has gone into the rvm project and it is still a great tool. But I prefer the UNIX philosophy of doing one thing well and rbenv embraces that. I also really like that with rbenv you just need to amend your PATH and you are done. It follows UNIX conventions making integration with other UNIX tools easier - for me at least.
rbenv basics ¶
We have a dev machine at work which runs a variety of Sinatra and Rails projects (including a Rails 1 project). As such these projects need lots of different versions of Ruby. To date I’ve used rvm to manage this and it has worked well but I always came up against issues when trying to integrate tools like Monit, puppet or init.d scripts. So I bit the bullet and switched to rbenv.
Each app has a UNIX user so rbenv is installed locally for each user when logged in as the user account
cd
git clone git://github.com/sstephenson/rbenv.git .rbenv
You need to load rbenv into the shell and you are good to go
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
If you have installed ruby-build you can then install rubies with
rbenv install
.
rbenv install 1.9.3-rc1
You can set a global ruby for the user account with
rbenv global 1.9.3-rc1
And you can also use an .rbenv-version
file to set the version. I choose to
use this in projects and check it into git.
Deployment with capistrano ¶
I like that rbenv doesn’t try and manage rubies for you - we have bundler
for that. I deploy as the same user as the app accounts so I just need to add
the following to my cap recipe to use rbenv since I’m already including
require 'bundler/capistrano'
.
set :default_environment, {
'PATH' => "/home/youruser/.rbenv/shims:/home/youruser/.rbenv/bin:$PATH"
}
This loads rbenv into the shell that capistrano uses. That’s pretty much all you need to use your rbenv installed ruby.
Following a thread on github you can also apply a clever technique to allow
you switch versions of ruby by pushing a new .rbenv-version
file with
capistrano. From version 1.1rc bundler allows you to specify a shebang for
binstubs. To use this add the following to your capistrano recipe.
set :bundle_flags, "--deployment --quiet --binstubs --shebang ruby-local-exec"
This generates executables in the bin folder of your Rails app which reference
ruby-local-exec
as the shebang. This command will execute whichever ruby is
specified in the .rbenv-version file
. In my case I have an init.d script to
manage unicorn that references bin/unicorn. If I wanted to upgrade the ruby used
by the app I would install it on the remote machine and then just update my
.rbenv-version
file commit, cap deploy and I’m done.
Integrating with other tools ¶
I like to use monit to monitor processes. Monit exexutes scripts with a very limited shell but since rbenv follows UNIX conventions we can easily create a small bash script to load rbenv into the path and then do what we want.
Here’s the script I’m using for starting a resque_worker
#!/usr/bin/env bash
USER=youruser
APP_PATH=/srv/yourapp.com/current
PATH=/home/$USER/.rbenv/bin:/home/$USER/.rbenv/shims:$PATH
RAILS_ENV=production
cd $APP_PATH
RAILS_ENV=$RAILS_ENV bin/rake environment resque:work& > $APP_PATH/log/resque_worker.log
echo $! > $APP_PATH/tmp/pids/resque_worker.pid
Then my monit task looks like this
check process yourapp_resque_worker
with pidfile /srv/yourapp.com/current/tmp/pids/resque_worker.pid
start program = "/bin/sh -c '/home/youruser/bin/start_resque_worker.sh'" as uid youruser and gid youruser
stop program = "/bin/sh -c 'cd /srv/yourapp.com/current && kill -s quit `cat tmp/pids/resque_worker.pid` && rm -f tmp/pids/resque_worker.pid; exit 0;'"
Better separation ¶
By using rbenv, bundler, capistrano and monit together we have great separation between what these tools do
- rbenv manages rubies
- bundler manages gems
- capistrano manages deployments
- monit monitors processes
This feels clean and manageable to me.
By using binstubs with the ruby-local-exec
shebang we also separate our app
from versions of rubies, init.d and monit scripts, making it super easy to
upgrade.
Thanks rvm, onwards with rbenv ¶
I’m really grateful to Wayne E Seguin and the work he has put into rvm and I hope the project continues to thrive. rbenv fits the way I work though and I’m really enjoying using it. It makes piecing tools like puppet, monit, init.d scripts, capistrano and bundler much easier, so a big thanks to Sam Stephenson for creating it.
Tags
Can you help make this article better? You can edit it here and send me a pull request.
See Also
-
Managing Unicorn Workers with Monit
A walkthrough on how to manage Unicorn worker processes using Monit -
Building a Rails server with Puppet
A short write up of my experience learning and configuring Puppet to build and manage servers to serve Rails applications. -
Instant Rails dev environments with Tmuxinator and Foreman
How to start an entire development environment with a single command