Deployment

Hosting

FastCGI

Mongrel

Typical Mongrel Architecture

Example Deployment Stack

Mongrel Alternative: Passenger (mod_rails)


sudo gem install passenger
sudo passenger-install-apache2-module
# Copy three lines into your Apache config and setup virtual host
sudo apachectl graceful # restart Apache

Mongrel Alternative: LiteSpeed

Capistrano

Capistrano – Installation and Configuration


gem install capistrano; cd path_to_rails_app; capify .
# Edit deploy.rb
set :application, "community" 
set :deploy_to, "/var/www/apps/#{application}" 
set :domain, "community.marklunds.com" 
set :user, "deploy" 
set :repository, "svn+ssh://#{user}@#{domain}#/var/www/apps/marklunds/repos/community" 
role :web, domain
role :app, domain
role :db,  domain, :primary => true
role :scm, domain

Capistrano – Bootstrap and Deploy


# Setup directory structure on server
cap setup
# First deploy. Here we assume database is created and mongrel cluster config is set up.
cap cold_deploy

Capistrano Tasks


cap -T # Show tasks
deploy:update # update code and symlink
deploy # Update and restart
deploy:migrate # run migrations
deploy:migrations # deploy and run migrations
deploy:cleanup # cleanup old releases
deploy:web:disable
deploy:web:enable
deploy:rollback # rollback code and restart

Custom Capistrano Tasks


namespace :deploy do
  def mongrel_cluster(command)
    "mongrel_rails cluster::#{command} -C #{current_path}/config/mongrel_cluster.yml" 
  end
  
  %w(restart stop start).each do |command|
    task command.to_sym, :roles => :app do
      run mongrel_cluster(command)
    end
  end
end

Custom Capistrano Tasks


namespace :deploy do
  desc "Run the full tests on the deployed app." 
  task :run_tests do
    run "cd #{release_path} && RAILS_ENV=production rake && cat /dev/null > log/test.log" 
  end
  desc "Clear out old sessions from the database" 
  task :clear_sessions, :roles => :db, :only => { :primary => true } do
    delete_sql = "DELETE FROM sessions WHERE updated_at < now() - 48*3600" 
    run <<-CMD
      cd #{current_path} && ./script/runner 'ActiveRecord::Base.connection.delete("#{delete_sql}")'
    CMD
  end
end

end

Capistrano Callbacks


namespace :deploy do
  desc "Run pre-symlink tasks" 
  task :before_symlink, :roles => :web do
    copy_shared
    backup_db
    run_tests
  end
end

MySQL and Charsets


# Abort if database doesn't have right encoding configured.
# You can also use the mysql_requirement plugin for this
%w(character_set_database character_set_client character_set_connection).each do |v|
  ActiveRecord::Base.connection.execute("SHOW VARIABLES LIKE '#{v}'").each do |f|
    unless f[1] == "utf8" 
      puts "ERROR: MySQL database isn't properly encoded" 
      puts "Kindly set your #{f[0]} variable to utf8" 
      RAILS_DEFAULT_LOGGER.error("MySQL database isn't properly encoded")
      exit 1
    end
  end
end

Deployment Checklist

Security

Performance

Performance

Deploy Early

Release early in the project and release often. Just like your application, your deployment doesn’t need to be perfect from day one. You can start simple and grow into more sophisticated deployment strategies over time.

- paraphrased from James Duncan Davidson

JRuby

Ruby 1.9