QUICK UPDATE 03-14-2011 – PLEASE READ – This tutorial was written when Rails 3 was in beta. Some of this info may be outdated. This tutorial will be updated soon, but here are a few notes until I get to it: For steps 6 & 7, use RVM and install 1.9.2 using RVM. This should install rubygems, so you can skip #7. For step #8, install without the –pre flag… it will install the current official release (3.0.5 or higher). I am not using Passenger at this time, so I can’t comment on how well that will work. You may need to include the bundler Capistrano recipes with your deploy.rb. This will bundle install with the –deployment flag.
I could not wait patiently for the official release of Rails 3, so I am working on a Rails 3 upgrade now… and part of the process is moving my site from co-location hosting over to Rackspace Cloud. I am a big fan of the Rackspace Cloud and while most of my websites are hosted there now, I was waiting for my co-location contract to run up before I moved over my highest traffic website. Here are the steps I used to set up my server. I will show you how to install all the software and create a blank Rails 3 app. Then I will show how to pull an existing Rails 3 app from github and deploy it with Capistrano.
Note: I am not doing this, but you may want to go the route of installing RVM if you will be running multiple applications or ruby versions on your production server.
My Rails stack looks like this:
- CentOS 5.5
- Ruby 1.9.2 preview 3
- Rubygems 1.3.7
- Rails 3 beta 4
- Passenger 2.2.15 with Nginx
Here are the steps I used. Please leave a comment if you have any questions or if something needs to be changed.
1. Login or signup for Rackspace Cloud Servers (not Cloud Sites)
2. Build a new server – Hosting -> Cloud Servers -> Add Server -> Create a CentOS 5.5 server
(YMMV with other linux flavors using my steps)
3. Login and change your root password
4. Create a new user and add them to the sudoers file.
It is important that you create a new user instead of doing everything as root. You will most likely run into permission problems with Passenger and Bundler if you do this as root. Passenger will run as ‘nobody’ if ‘root’ owns the rails app root directory. It will not have access to root’s local gem bundle. Also, go ahead and disable root login from sshd
useradd app_user passwd app_user vi /etc/sudoers #open sudoers in your favorite text edit # add the following line below "root ALL=(ALL) ALL" : # app_user ALL=(ALL) ALL # save file and exit #disable root login from ssh, so nobody is able to brute force a root login vi /etc/ssh/sshd_config #uncomment "PermitRootLogin yes" and change it to "PermitRootLogin no" /etc/init.d/sshd restart #logout and login or su to your new user su app_user cd ~
5. Update EPEL and install linux software dependencies
sudo rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm sudo yum -y install gcc gcc-c++ make zlib zlib-devel openssl openssl-devel git expect pcre pcre-devel readline-devel mysql mysql-devel libxml2 libxml2-devel libxslt libxslt-devel
6. Install Ruby 1.9.2 preview 3 (compatible with Rails 3 beta 4)
wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-preview3.tar.gz tar -xvf ruby-1.9.2-preview3.tar.gz cd ruby-1.9.2-preview3 ./configure make sudo make install ruby -v #just checking to make sure it installed.. output = ruby 1.9.2dev (2010-05-31 revision 28117) [x86_64-linux] cd ..
7. Install Rubygems 1.3.7
wget http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz tar -xvf rubygems-1.3.7.tgz cd rubygems-1.3.7 sudo ruby setup.rb cd ..
8. Install Ruby on Rails 3 Beta 4
If the official release is out you can remove the –pre. This should install a bunch of gems related to rails 3 beta 4 (23 gems on my system)
sudo gem install rails --pre --no-ri --no-rdoc
9. Create a new directory for your rails applications and create a blank rails app
In rails 3 you need to use the new parameter after the rails command in order to create a new app
mkdir rails_apps cd rails_apps rails new myapp
10. Install passenger with Nginx
sudo gem install passenger sudo passenger-install-nginx-module # This will guide you thru nginx/passenger install.. hit enter to check dependencies # choose option 1. Yes: download, compile and install Nginx for me. (recommended) # I installed in default /opt/nginx.. just hit enter to install default # hit enter at the end and you should be finished and back to the command line prompt
11. Update your Nginx configuration file so you can run myapp with passenger.
Example Nginx config file: /opt/nginx/conf/nginx.conf
12. Start Nginx, which will also start Passenger
13. Open up iptables for port 80 and restart iptables.
Also, you might want to look into enable SELinux for some added security on your system.
sudo vi /etc/sysconfig/iptables # Add the following line towards the bottom below the port 22 ACCEPT # -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT # save and close # restart iptables sudo /etc/init.d/iptables restart
You should be able to get to your Rails app now and see the default rails index.html -> Welcome aboard. You’re riding Ruby on Rails! ……
This is great and all, but I am more interested in running my existing app in production, rather than creating a new one. I am guessing you will not be developing on the server, so we need to get things set up with github & capistrano and deploy our app that way. This assumes that you already have a project in github and you have some git knowledge. This also assumes that you already have a database server setup and that your database.yml is pointing to that server. I will not cover database setup here.
Here is what you do:
14. Generate a new ssh key on the server to use with github
ssh-keygen -t rsa -C "firstname.lastname@example.org" #use the default dir and enter a solid password
15. Add the key to github
Full instructions about creating a key and adding to github here: http://help.github.com/linux-key-setup/
16. Add entry into known_hosts file for github
This is necessary to pull with capistrano. I think the easiest way to do this is to clone your github project. It will create the known_hosts file and add the entry for you. Then you can just delete the project right after you clone it. No need to keep it, since you will be using the Capistrano setup.
git clone email@example.com:your_username/your_project.git # type yes to accept the key entry # enter your passphrase # it will clone.. now delete it rm -Rf your_project/
OPTIONAL: Out of habit I have all my rails apps in the same place on my servers.
I have a dir called /rails_apps. I am going to create that now, but you can use what you already have set up if you like. I also need to change the dir to be owned by “app_user”
sudo mkdir /rails_apps sudo chown app_user:app_user /rails_apps/
17. Go ahead and create your project and release directory for Capistrano to use.
mkdir /rails_apps/myapp mkdir /rails_apps/myapp/releases
IMPORTANT! It is necessary to update your nginx configuration if you are using Capistrano. Since cap uses the “current” directory and links to a release directory, your nginx config should change the “root” entry in the server block
open /opt/nginx/conf/nginx.conf and change the “root” entry to “root /rails_app/myapp/current/public;” Then you can restart nginx like this:
sudo killall -9 nginx sudo /opt/nginx/sbin/nginx
18. Capistrano Setup
Now you are ready to move on to Capistrano setup. I do all development locally on my Mac, commit the code with git and push it to my repository at github. In order to use Capistrano to deploy, you will need to “gem install capistrano” on your Mac (or windows box.. say it ain’t so!) and add 2 files to your Rails project. /myapp/Capfile and /myapp/config/deploy.rb. Here are my files:
deploy.rb – most of this config came from the passenger capistrano recipe.
Now you are ready to deploy. The whole point of using Capistrano is so you don’t have to login to your servers to push code, do restarts, etc.. So on your mac, you will run the “cap” commands for deployment.
This is a 3 step process. Run these from the command line on your development box while in your rails app root directory (dir that contains Capfile):
- cap deploy:check #this will check for dependencies
- cap deploy:setup #this will create your shared directories.. log, pids, system
- cap deploy #this will pull your code from github and put it in a new release directory, create a link to “/rails_apps/myapp/current” directory for that release and restart your passenger server
Now, take a look at your app on the server. If you are lucky it will be running as expected, but I highly doubt it if you have gems that need to be installed. Lets use bundler to get the necessary gems installed. If you are Rails 3 savvy, you know that all your gem dependencies should be in /myapp/Gemfile, and that you can use Bundler (which is awesome BTW.. thnx wycats) to install them easily.
Passenger gives me a real pretty screen that says: “Could not find gem ‘mysql (= 2.8.1, runtime)’ in the gems available on this machine. (Bundler::GemNotFound)”
My Gemfile contains that gem, but it is not on my server. So to get Bundler to install it, you will need to navigate over to your rails app root directory on the server. I mentioned earlier that you should not set up your rails app with the ‘root’ account on your linux box, and this is the problem that I ran into. When I installed with bundler, it put the bundled gems in /root/.bundle/…. and the web server could not access the gems, because passenger runs as ‘nobody’. If you run the server as ‘app_user’ or whatever you account is, then you should bundle install with the same account, so you dont have permission problems.
cd /rails_apps/myapp/current bundle install #no need for sudo here. You want them to install in your user's home dir
19. Memcached Setup
Next on my list was getting memcached working. I use memcached as my cache store. In /myapp/config/environments/production.rb, I have the following line:
So passenger was complaining about not having memcached -> no such file to load — memcache
First I need to install memcached on the server. You can use a seperate server for memcached but you need to update the line in production.rb, to point to that server. It uses localhost by default.
In order to get memcached to work, you have to install libevent first
cd wget http://www.monkey.org/~provos/libevent-1.4.13-stable.tar.gz tar -xvf libevent-1.4.13-stable.tar.gz cd libevent-1.4.13-stable ./configure && make sudo make install cd .. wget http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz tar -xvf memcached-1.4.5.tar.gz cd memcached-1.4.5 ./configure && make sudo make install #you will get errors if you do not do the next 2 steps. Create libevent config file, add a line to it, and then run ldconfig sudo vi /etc/ld.so.conf.d/libevent-i386.conf #create this file and add one line -> # /usr/local/lib/ sudo /sbin/ldconfig #start your memcached server up sudo /usr/local/bin/memcached -u nobody & #you should add this last command to /etc/rc.local so the memcached server starts if there is a reboot
Now your memcached server is running, but you still have to install the gem. Let’s add it to our Gemfile, git commit, & git push on our Mac, run “cap deploy” and then bundle install again on the server
One note of caution here.. if you are already in /rails_apps/myapp/current, you need to cd again. If I remember correctly I ran into the problem of being in an old release directory. I guess when the link was update, my shell did not catch the update and was pointing current to the previous release directory.
cd /rails_apps/myapp/current bundle install
You should have a working Rails app with memcached now. I did run into one gotcha when I was doing this. I had a legacy Rails app that I was upgrading and the following line was in environments/production.rb:
This line needs to be removed if you get the following error -> “undefined method `cache_template_loading=’ for ActionView::Base:Class”
Now open up your app in your browser and feel all the Rails 3 goodness! You are a rock star!
Hopefully YMDNV Everything seems to be working so far on Rails 3 and I have a fairly complex app with quite a few plugins. I think the hardest part about adopting Rails 3 is the fact that plugins are not ready. I spent a good bit of my time troubleshooting and patching (yeah.. some monkey stuff) plugins. You can read more about my upgrade experiences in this post.