I’m learning a lot about our new test server each day!
Today I set up RVM on our apache web server, so that it can serve Rails applications with multiple gem installations. There is a lot of tutorials and write-ups on this topic, but everybody encounters different problems, so why not document my own experiences?
Installation Strategy
My goal was to be able to create virtual hosts that can serve rails apps, depending on unique gemsets.
In order to do this, we need to:
- be able to create a gemset for every project that we deploy on the server
We will need to:
- prepare the rail projects for RVM and Capistrano
- set up apache with passenger and RVM
- prepare gemsets for projects on the server
Existing setup
The apache web server is run by the user www-data on the server, but I have previously set up a user, www-publisher with ssh, a homedir and as a member of the www-data group so I can update web content without sudo. This user will also need to be the user that installs RVM in order to access the rvm-gemsets with no problems.
Currently, when apache is running mod_passenger with RVM, you have some restrictions that you will need to work with.
First, you can only use 1 ruby, but with this ruby you can use different gemsets. This I believe, is because mod_passenger will be installed as a gem under RVM, and apache needs to serve rails apps with gemsets containing the passenger gem.
Existing software
The Ubuntu server was already set up to serve the rails app Redmine, which is an amazing piece of project managing software. More on that in a future post.
Unfortunately it was set up with mod_passenger installed with apt-get, so the first thing was to remove that module.
$ sudo a2dismod passenger # disable the passenger module
$ sudo apt-get remove libapache2-mod-passenger # uninstall passenger
The reason for uninstalling the apt-get installed passenger, is that Apache will need to have passenger in the RVM-controlled ruby that we’re using.
Afaik this means that we can only use 1 RVM-ruby. But we can still use as many gemsets as we’d like to.
Git was already set up on the server with gitosis serving repositories on the local network. I will describe setting up Git later.
Installing
After removing the unwanted software, I had the following list of software to install:
- rvm
- gems:
- passenger
- rails
- capistrano
Plus some additional setup of existing software:
Here’s what happened:
Installing RVM
In order to install in the correct location as a single-user, we need to install as www-publisher and we need www-publisher to be able to sudo. We also need to revoke sudo from www-publisher when we’re done so www-publisher won’t wreck unnecessary havoc later on. And we need to install the passenger gem so apache can serve rails pages.
$ groups www-publisher #list all groups that www-publisher is a member of
$ sudo usermod -G www-publisher,www-data,rvm,sudo www-publisher # add www-publisher to the list of sudoers
Once we’re done we can revoke sudo from www-publisher by
$ sudo usermod -G www-publisher,www-data,rvm www-publisher
Notice that www-publisher was already a member of rvm. I believe that was due to a previous (failed) rvm installation attempt 🙂
Installing in www-publishers home dir
$ su www-publisher #switch to www-publisher
$ cd ~ # not really necessary but now we're in www-publishers home dir
$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm) # installing rvm
$ echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"' >> ~/.bashrc # adding the rvm function to the file .bashrc in www-publishers home dir
$ source ~/.rvm/scripts/rvm # initiating the rvm function
Testing the installation:
$ type rvm | head -1 # should display 'rvm is a function'
When the time comes, we can log in as, or switch to the user www-publisher in order to create gemsets and install some gems, but for now we really only need to install the ruby that we want to use and passenger gem:
$ rvm install 1.8.7 # installs ruby1.8.7
$ rvm use 1.8.7 # switch to ruby1.8.7
$ gem install passenger # install the passenger ruby API
$ rvmsudo passenger-install-apache2-module # run the install script for passenger on apache
The last line will output some lines that you can use later to configure the passenger module. Copy those lines and save them for later.
Setting up Apache and Passenger
So now we can switch back to our normal administrator user by typing
$ exit # quit being www-publisher
and set up apache.
First we need to set up the passenger module by adding the lines that was output when we ran the passenger-install-apache2-module script
$ sudo nano /etc/apache2/mods-available/passenger.load # edit /etc/apache2/mods-available/passenger.load with a simple editor
$ # in file /etc/apache2/mods-available/passenger.load
LoadModule passenger_module /home/www-publisher/.rvm/gems/ruby-1.8.7-p334/gems/passenger-3.0.7/ext/apache2/mod_passenger.so
$ sudo nano /etc/apache2/mods-available/passenger.conf # edit /etc/apache2/mods-available/passenger.conf with a simple editor
$ # in file /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
PassengerRoot /home/www-publisher/.rvm/gems/ruby-1.8.7-p334/gems/passenger-3.0.7
PassengerRuby /home/www-publisher/.rvm/wrappers/ruby-1.8.7-p334/ruby
</IfModule>
We also need to make apache trust the rvm settings that we decide to do later in our rails applications:
$ sudo echo "<code>rvm_trust_rvmrcs_flag=1</code>" >> /home/www-publisher/.rvmrc # set a flag that will make the www-publisher always trust .rvmrc files in capistrano deployed rails apps
Now we can set up multiple virtual hosts for our rails applications
Setting up for a Rails application
We need a RVM gemset
$ su www-publisher # switch to the www-publisher user so we can create gemsets and install required gems
$ rvm use 1.8.7 # selecting the ruby that we will install our new gemset in
$ rvm create gemset myprojectname # creating a gemset with the name 'myprojectname'
and a Virtual Host
$ sudo nano /etc/apache2/sites-available/myprojectname.domain # edit and create the file myprojectname.domain
# in file myprojectname.domain
<VirtualHost *:80>
ServerAdmin admin@domain.com
ServerName myprojectname.domain.com
DocumentRoot /home/www-publisher/myprojectname/www/current/public
RailsEnv development
RailsBaseURI /
ErrorLog /home/www-publisher/myprojectname/logs/error.log
CustomLog /home/www-publisher/myprojectname/logs/access.log combined
<Directory /home/www-publisher/myprojectname/www/current/public>
AllowOverride all
Options -MultiViews
</Directory>
</VirtualHost>
Above virtual host assumes that you wants to serve your rails app from the folder /home/www-publisher/myprojectname/www, while apache is logging to log files in /home/www-publisher/myprojectname/log
Preparing a Rails application for deployment with Capistrano to a server with RVM
We need to configure our Rails app so the passenger loaded apache server can select gems from the correct gemset.
Add a .rvmrc file in your Rails dir
# in file .rvmrc
rvm 1.8.7@myprojectname
This will make the pasenger loaded apache swap to the gemset with the name ‘myprojectname’
Add a setup_load_paths.rb file in the config folder in your Rails dir
# in file .rvmrc
if ENV['MY_RUBY_HOME'] && ENV['MY_RUBY_HOME'].include?('rvm')
begin
rvm_path = File.dirname(File.dirname(ENV['MY_RUBY_HOME']))
rvm_lib_path = File.join(rvm_path, 'lib')
$LOAD_PATH.unshift rvm_lib_path
require 'rvm'
RVM.use_from_path! File.dirname(File.dirname(__FILE__))
rescue LoadError
# RVM is unavailable at this point.
raise "RVM ruby lib is currently unavailable."
end
end
# If we're using a Bundler 1.0 beta
ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', File.dirname(__FILE__))
require 'bundler/setup'
This post will be proofread very soon – there may still be some commands missing