Now that we have a Rackspace Cloud server with a useful rails stack running. It is about time for us to show you how to easily, and consistantly deploy your code. Although it is possible so use simple tools such as scp, or even ftp to simply transfer the working files to production, and trigger a reload of the environment, it is not good practice. First of all we are all human, and we make mistakes. If we can automate something simply, it will prevent simple or stupid mistakes from occuring. With more complex installations, a deployment could easily be across multiple machines, and be more then just source code changes. This article will describe one of the many methods and workflows used to deploy, manage, and even rollback production Rails environemnts.
This article assumes a running server as described in Part 1, and a local development similar to the Ruby Environment described in the the previous article. Once deployment is setup, it is fairly rock solid, and will ease future releasing, and hopefully unessessary rollbacks.
For this article, we will start by creating a simple local Rails application, and configure it to deploy to our remote server.
First we will need to install rails and bundler. First lets make sure we are using the ruby we expect, this can be checked simply by typing
ruby -v
// to see the version
or
which -a ruby
// to see the path
You should see ree as the current ruby, if use rvm to switch to the correct ruby and then set it as the default ruby.
rvm use ree --default
Now that we are using the correct ruby, we need to install some dependencies.
gem install rails bundler capistrano
and lets generate a dummy rails application
rails new blog
cd blog
now we should make sure all the gem requirements have been filled.
bundle install
next, we woant all commands to use the bundled gem environment, to accomplish this we prefix each command with
bundle exec <regular command>
this ensures the correct gems are loaded.
To generate our first rails scaffold we simple
bundle exec rails generate scaffold post name:string body:text
Run the database migrations, which will setup our posts table.
bundle exec rake db:migrate
bundle exec rails server
Lets checkout your super simple, and trivial blog, by surfing over to:
http://127.0.0.1:3000/posts
We will be using ssh-keys for authentication... blurp. If you already have a key-pair feel free to skip this section.
ssh-keygen -t rsa -C "<your@email.com"
Now your public key ( you can share this with others) exists at.
~/.ssh/id_rsa.pub
And, your private key ( do not share this with others) exists at.
~/.ssh/id_rsa
*http://help.github.com/mac-key-setup/
Git is a source control management system, it is a beautiful simple but incredibly powerfull tool. A highly recommended read is the Git from the bottom up paper, which nicely explains and describes whats going on internally.
For deployment, and collaboration (as mentioned in the Distributed Workflow) article) using a centralized git location is very useful. For this we will use Github. Github offers free plans, which allow unlimited public repositories, and paid plans, which offer a set number of private repositories, and also unlimited public repositores.
If you dont already have a github account, head over to Github.com, signup. Once you have signed up, its time to create a new repository on Github. It is not required that you setup a github repository first, you can always push any repository up to a new github account, if down the road you decide to. Now that you have a new github repository lets push your new simple blog to it, simply follow the instructions on empty github repository page to accomplish this.
At the time of this writing those steps where:
// inside the root of your blog application
git init
git commit -m 'first commit'
git remote add origin git@github.com:stefanpenner/blog.git
git push origin master
Now if you refresh your blogs github repository page, the page should show the full directory structure and all the source files of your rails application.
http://help.github.com/ The Git Book http://git-scm.org Git from the bottom up
Capistrano is a deployment tool for written in ruby, initial use to deploy ruby applications, such as rails. But can be used for quite literaly anything, even remote server admin. By configuring Capistrano simple deploy.rb DSL, it will do all the heavy lifting for you. Such as, deployments, rollbacks, database migrations, starting and stoping daemons, and configuration environments.
capify .
This generates the following files.
./Capfile
./config/deploy.rb
The first ./Capfile we can simply ignore, the second confg/deploy.rb is where we configure the magic.
The first part is straight forward, lets give you application a name, specify the remote repository, and set the scm to git.
set :application, "set your application name here"
set :repository, "git@github.com:<username>/<repo_name>.git"
set :scm, "git"
Since we have our permissions setup correctly on our server, we should not need to use sudo when deploying
set :use_sudo, false
This is where capistrano truely shines, multiple server deployments. By specificying seperate servers or arrays of servers for :app, :web, :db, we can let capistrano handle deployment to N servers, database migrations to N servers, unfortunately our simple blog application does not (yet) need this scale of archicture, so we can simply bunch, :app, :web, :db together.
server "<ip of your rackspace server>", :app, :web, :db, :primary => true
Since we are using RVM on our server, we will have to enable the rvm/capistrano helper. This automatically sets the correct environment variables to use the rvm Ruby associated with your passenger installtion.
$:.unshift(File.expand_path('./lib', ENV['rvm_path'])) # Add RVM's lib directory to the load path.
require "rvm/capistrano"
set :rvm_ruby_string, "ree@passenger" # Or whatever env you want it to run in.
set :rvm_type, :user
In the past, or under other system setups rails stack deployments required restarting various services, since we are using passenger as our webserver. We only need to touch a single file in your applications tmp directory, and on the next request passenger will automatically reboot our application.
namespace :deploy do
# passenger stuff
task :start, :roles => :app do
run "touch #{release_path}/tmp/restart.txt"
end
task :stop, :roles => :app do
run "touch #{current_path}/tmp/restart.txt"
end
# end passenger stuff
desc "Restart Application"
task :restart, :roles => :app do
run "touch #{current_path}/tmp/restart.txt"
end
In order to run migrations with Rails using bundler, we need to make capistrano aware that it should use bundler.
# helpers to migrate with bundler
desc "Run the migrate rake task."
task :migrate, :roles => :app do
run "cd #{current_path} && bundle exec rake db:migrate RAILS_ENV=#{rails_env}"
end
end
Now that all the hard work is done, we simply issue the following command in the local blog directory, and our application is deploy, and remote database configured. cap deploy:setup deploy deploy:migrate