One app, one user, one ruby
… and check why 5600+ Rails engineers read also this
One app, one user, one ruby
There are many ways to install and manage ruby installation in your
infrastructure. Some people like rvm
, others prefer rbenv
, some pack
their ruby installation into deb
packages. We like nothing.
You ain’t need it in production
Yes, you read it well. Using no tool is better than using some tool if we can simply avoid it. Let me state it clearly: RVM is a great tool for development environment however we do not see much use for it in production. Even in development some of us started to separate projects on higher level using Vagrant or LXC containers but that is another story.
A little background: Our customers usually host their applications using our infrastructure or their own. These solution are based on LXC, XEN, or KVM. Every project has its own container/VM .
Simplest thing that can possibly work
We do not have a globally installed ruby except for ruby coming from system package that is used mostly by littlechef to setup the virtual machine according to our conventions and application requirements. For every application that is a part of bigger project separate user is created and separate ruby is installed in its home directory. If we end up having five users using same ruby version but different ruby installation then fine. Storage is cheap. Easy upgrades are more important.
Running Ruby
Bash
You might wonder how to execute your scripts and run applications with that ruby.
We just add the ruby bin path to user $PATH
via .bashrc
and voilà.
Whenever you run something inside bash it just works. And gem binaries are
installed into the same directory so they also work properly.
Not Bash?
If your software is not directly executed inside bash you have two options:
- run it in bash anyway.
- just use the full path to your ruby.
Example 1
Here is an example. Runit by default executes
supervised processes as root
. We use su
to switch to user with no
special abilities. The -c
switch allows you to execute a command that will
be invoked in a shell. You can use -s, --shell SHELL
specify which shell
is going to be used. Thanks to such behavior .bashrc
is used and $PATH
variable is set up properly.
Note: We use exec
twice here so that runit ends up monitoring our
application-name
binary instead of monitoring su
or bash
shell.
exec su - application-name -c "cd /var/lib/application-name/current/ \
&& exec bundle exec ruby -Ilib ./bin/application-name"
Example 2
And the second mentioned solution is to use full ruby binary (or gem) path in a script:
/var/lib/application-name/1.9.3-p286/bin/rackup path/to/the/app
Cookbook
You can see our open-sourced cookbook for ruby installation on github arkency/ruby-build-cookbook. This is first thing that we open sourced as a company (we do have lot of open source experience as individuals) and we hope that in the future you can expect even more from us.
And if you are not that much into cooking (with chef) you can see for yourself how simple the whole ruby installation process just is.
Benefits
- No need to use rvm wrappers in every possible place such as cron, capistrano recipes etc.
- No need to switch ruby versions because you just log in as proper user associated with the application and there is just the right ruby that you should be using with this app.
TL;DR
- Every application has its own user
- Every user has its own ruby