Thanks to Ruby on Rails I can quickly help my customers. The speed of development with Rails and agile practices make my work really fast. The only time when I slow down a bit is when I create and deploy a new project. If I saved the time somehow I could spend this time solving business problems instead.
I decided to start with a documentation of my deployment/development process. Then, I automated and simplified all I could and had time to. The result is a semi-automatic process which helps me with creating applications. This process is specialised for my needs and it uses the tools that I found useful. It doesn't have to be good for your needs.
All the setup was focused on agility. I love to check-in frequently and be able to see the changes immediately on the production server. That's why I use tools like Capistrano or Vlad.
A quick description of the tools I use:
- Rails
- I don't think I have to explain why :)
- most of the time I use edge Rails
- Mongrel
- A standard way of deploying in the Rails world.
- fast and stable
- Easy to configure
- nginx
- an http server that is responsible for load balancing
- easy to configure (even easier if you can read Russian blogs)
- easy to handle many Rails applications.
- Capistrano
- A standard tool for easy deployment.
- I use Vlad for some of my applications, both are good.
- Piston
- Easy plugins management
- Subversion
- mysql
Let's assume that you want to create and deploy a new Rails applications. The whole process can be described in 5 general steps.
- Create an SVN repository, create the app and import it to the repository.
- Use capistrano to make the remote work easier.
- Create the production database.
- Prepare mongrel_cluster to work with the new application.
- Configure nginx and bind the domain.
Ok, let's go into details.
- Remote: Create a repository for the application.
- svnadmin create /var/repos/app
- I prefer to have a separate repo for each of my applications.
- It's good to have one directory for all repositories
- I use svn+ssh protocol, in my case it's easier to have just one source of users (Linux users)
- Checkout the application to the /tmp/app directory
- svn co $REMOTE_SVN/app /tmp/app
- I recommend having environment variables for the svn paths, works nicely with shell autocompletion.
- Use edge rails to create a rails app.
- svn up ~/tmp/rails_edge
- update it first
- my rails_edge lives there, you can always create it with 'svn co $RAILS_DEV_TRUNK'
- ruby ~/tmp/rails_edge/railities/bin/rails -dmysql /tmp/app/trunk
- '-dmysql' because Rails has recently changed its default db to sqlite
- svn ci -m "initial import"
- Live on Rails edge
- piston import $RAILS_DEV_TRUNK vendor/rails
- svn ci -m "piston imported edge rails"
- capify
- capify .
- It creates two files.
- modify config/deploy.rb:
set :application, "app"
my_server = "12.34.56.7"
set :repository, "svn+ssh://#{my_server}/var/repos/app/trunk"
set :deploy_to, "/var/www/#{application}"
role :app, my_server
role :web, my_server
role :db, my_server, :primary => true
- Commit.
- svn ci -m "capistrano setup"
- cap deploy:setup
- it creates all the directories on the remote server
- cap deploy:cold
- it checkouts the code, creates a 'current' symlink
- logs are in the 'shared' directory
- it fails on db:migrate task which is fine for now.
- Create the production database
- Remote: Go to /var/www/app/current
- Remote: rake RAILS_ENV=production db:create
- Local: cap deploy:cold
- Again.
- It should pass the db:migrate now
- But it fails on missing script/spin file
- Create script/spin with the following content:
- mongrel_cluster_ctl restart
- Yes, you need mongrel_cluster on the remote server
- Add this file to svn and commit
- cap deploy:cold
- This time it worked!
- But... we didn't setup mongrel_cluster to restart our app.
- Mongrel_cluster
- Create config/mongrel_cluster.yml
---
cwd: /var/www/app/current
log_file: log/mongrel.log
port: 5000
environment: production
group: www-data
user: your-username
address: 127.0.0.1
pid_file: tmp/pids/mongrel.pid
servers: 3
- Commit this file
- cap deploy:cold
- still doesn't work
- Mongrel_cluster doesn't know that it should use this file
- Remote: Go to /etc/mongrel_cluster/
- Make a symbolic link to the yml file
- sudo ln -s /var/www/app/current/config/mongrel_cluster.yml app.yml
- cap deploy:cold
- And it works!
- you should now be able to connect to localhost:5000 from the remote server.
- Now we will bind the domain to this mongrel_cluster.
- We need to configure nginx.
- I have setup nginx so that for every application I create a single file that is automatically loaded on nginx startup.
- The file just needs to live in the /etc/nginx/vhosts/ directory.
- It works because I have the following line in my /etc/nginx/nginx.conf file:
- include /etc/nginx/vhosts/*.conf;
- Create /etc/nginx/vhosts/app.conf
upstream app {
server 127.0.0.1:5000;
server 127.0.0.1:5001;
server 127.0.0.1:5002;
}
server {
listen 80;
client_max_body_size 50M;
server_name app.com www.app.com;
root /var/www/app/current/public;
access_log /var/www/app/current/log/nginx.log;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html last;
break;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;
proxy_max_temp_file_size 0;
if (-f $request_filename) {
break;
}
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://app;
break;
}
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/app/current/public;
}
}
- Restart nginx
- sudo /etc/init.d/nginx stop
- sudo /etc/init.d/nginx start
- That's it :)
- From now on you just write the code, commit and call capistrano to deploy.
I hope you found this guide useful. There is still a lot to improve and a lot of duplicates (like defining mongrel ports). Let me know if you think that I could do something in a better way.
Here is a list of articles that I found useful when I was experimenting with Rails deployment.
Installing and Configuring Nginx and Mongrel for Rails
Install Ruby Rails on Gutsy Gibbon (Nginx Version)
Hosting Rails apps using nginx and Mongrel Cluster
A clean slate, Edge Rails recipe