Posts Tagged Nginx

Setting up Rails 3 on Rackspace Cloud Servers

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
  • memcached
  • MySQL

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

#I found it necessary to change the user in order to have the correct permissions.
user  app_user;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
error_log  logs/error.log  info;

events {
  worker_connections  1024;
}

http {
  #Note: rubygems installs gems in the 1.9.1 dir even though I am using 1.9.2... not sure why??
  passenger_root /usr/local/lib/ruby/gems/1.9.1/gems/passenger-2.2.15;
  passenger_ruby /usr/local/bin/ruby;
  # passenger recommends 30 max pool size if you have a 2GB box that is dedicated to app server and
  # less than that if you have a DB server running on it
  passenger_max_pool_size 30;

  include       mime.types;
  default_type  application/octet-stream;
  sendfile        on;
  #tcp_nopush     on;
  keepalive_timeout  65;
  tcp_nodelay        on;
  gzip  on;
  gzip_min_length  1100;
  gzip_buffers     4 8k;
  gzip_types       text/plain;
  gzip_comp_level 2;
  gzip_types      text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

  server {
    listen       80;
    server_name  localhost; #putting in localhost just for testing.  You should set this to your domain name: yourdomain.com
    root /home/app_user/rails_apps/myapp/public;
    passenger_enabled on;  #MAKE SURE YOU HAVE THIS LINE OR IT WILL NOT FORWARD TO PASSENGER!
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
       root   html;
    }
  }
}
12. Start Nginx, which will also start Passenger
sudo /opt/nginx/sbin/nginx
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 "youraddress@email.com"
#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 git@github.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:

Capfile

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }

load 'config/deploy' # remove this line to skip loading any of the default tasks

deploy.rb – most of this config came from the passenger capistrano recipe.

set :application, "myapp"

#github stuff
set :repository,  "git@github.com:your_github_id/myapp.git"
set :scm, :git
set :scm_username, "your_github_id"
set :scm_passphrase, "your_github_passwd"

set :use_sudo,    false
set :deploy_to,   "/rails_apps/#{application}"

#server login
set :user, "app_user"
set :password, "server_password_here"

ssh_options[:forward_agent] = true

# will be different entries for app, web, db if you host them on different servers
server "123.456.12.34", :app, :web, :db, :primary => true

namespace :deploy do
  task :start, :roles => :app do
    run "touch #{current_release}/tmp/restart.txt"
  end

  task :stop, :roles => :app do
    # Do nothing.
  end

  desc "Restart Application"
  task :restart, :roles => :app do
    run "touch #{current_release}/tmp/restart.txt"
  end
end

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):

  1. cap deploy:check   #this will check for dependencies
  2. cap deploy:setup  #this will create your shared directories.. log, pids, system
  3. 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:

config.cache_store = :mem_cache_store

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

#Gemfile located at myapp/Gemfile. on your dev box
group :production do
gem “memcache-client”
end

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:

config.action_view.cache_template_loading = true

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.

Tags: , , , , ,

Installing Nginx on CentOS 5

I recently installed Nginx on another server and forgot how difficult it was the last time I did it.  You need to make sure you have all the dependencies before you can configure the source.  Here are the steps to get a successful install on CentOS 5 using Nginx 0.7.61.

> yum install pcre
> yum install pcre-devel
> yum install zlib
> yum install zlib-devel
> yum install openssl-devel
> yum install openssl
> wget http://sysoev.ru/nginx/nginx-0.7.61.tar.gz
> tar -xvf nginx-0.7.61.tar.gz
> ./configure
> make
> make install

Nginx should be installed after that.   Then you can edit your config in /usr/local/nginx/conf and start the web server using:

> /usr/local/nginx/sbin/nginx

Tags: ,