Skip to content

Instantly share code, notes, and snippets.

@eoy
Created July 20, 2012 22:49
Show Gist options
  • Save eoy/3153735 to your computer and use it in GitHub Desktop.
Save eoy/3153735 to your computer and use it in GitHub Desktop.
Eoy's Server/Deploy Setup
  • Replace all instances of app_name with the name of your application in lower letters
require "bundler/capistrano"
set :whenever_command, "bundle exec whenever"
require 'whenever/capistrano'
server "123.456.789.10", :web, :app, :db, primary: true
set :application, "app_name"
set :user, "deployer"
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false
set :scm, "git"
set :repository, "git@github.com:eoy/#{application}.git"
set :branch, "master"
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
after "deploy", "deploy:uploads:symlink" # keep only the last 5 releases
after "deploy:uploads:symlink", "deploy:cleanup"
namespace :deploy do
# OLD STUFF FOR NON 0-DOWNTIME DEPLOYS ### DON'T DELETE
# %w[start stop restart].each do |command|
# desc "#{command} unicorn server"
# task command, roles: :app, except: {no_release: true} do
# run "/etc/init.d/unicorn_#{application} #{command}"
# end
# end
desc "Zero-downtime restart of Unicorn"
task :restart, :except => { :no_release => true } do
run "kill -s USR2 `cat /tmp/unicorn.app_name.pid`"
end
desc "Start unicorn"
task :start, :except => { :no_release => true } do
run "/etc/init.d/unicorn_#{application} start"
end
desc "Stop unicorn"
task :stop, :except => { :no_release => true } do
run "kill -s QUIT `cat /tmp/unicorn.app_name.pid`"
end
desc "Upgrade code and restart unicorn server"
task :upgrade, :roles => :app, :except => { :no_release => true } do
run "/etc/init.d/unicorn_#{application} upgrade"
end
desc "Remote console"
task :console, :roles => :app do
env = "production"
server = find_servers(:roles => [:app]).first
run_with_tty server, %W( ./script/rails console #{env} )
end
def run_with_tty(server, cmd)
# looks like total pizdets
command = []
command += %W( ssh -t #{gateway} -l #{self[:gateway_user] || self[:user]} ) if self[:gateway]
command += %W( ssh -t )
command += %W( -p #{server.port}) if server.port
command += %W( -l #{user} #{server.host} )
command += %W( cd #{current_path} )
# have to escape this once if running via double ssh
command += [self[:gateway] ? '\&\&' : '&&']
command += Array(cmd)
system *command
end
task :setup_config, roles: :app do
sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
run "mkdir -p #{shared_path}/config"
put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml"
puts "Now edit the config files in #{shared_path}."
end
after "deploy:setup", "deploy:setup_config"
task :symlink_config, roles: :app do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
end
after "deploy:finalize_update", "deploy:symlink_config"
desc "Make sure local git is in sync with remote."
task :check_revision, roles: :web do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
namespace :uploads do
desc "Link uploads from shared to current"
task :symlink do
run "cd #{current_path}/public; rm -rf uploads; ln -s #{shared_path}/uploads ."
end
end
namespace :ts do
%w[index start stop restart reindex rebuild].each do |command|
desc "#{command} Thinking Sphinx server"
task command do
run "cd #{current_path}; RAILS_ENV=production rake ts:#{command}"
end
end
end
namespace :bundle do
%w[install update clean outdated].each do |command|
desc "Sending << #{command} >> through bundler."
task command do
run "cd #{current_path}; RAILS_ENV=production bundle #{command}"
end
end
end
namespace :db do
task :seed do
run "cd #{current_path}; RAILS_ENV=production rake db:seed"
end
task :migrate do
run "cd #{current_path}; RAILS_ENV=production rake db:migrate"
end
end
# Delayed Job commands
namespace :dj do
%w[start stop].each do |command|
desc "#{command} Delayed_job worker"
task command, roles: :app do
run "cd #{current_path}; RAILS_ENV=production script/delayed_job #{command}"
end
end
end
namespace :assets do
task :precompile, :roles => :web, :except => { :no_release => true } do
from = source.next_revision(current_revision)
if capture("cd #{latest_release} && #{source.local.log(from)} vendor/assets/ app/assets/ | wc -l").to_i > 0
run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
else
logger.info "Skipping asset pre-compilation because there were no asset changes"
end
end
end
#before "deploy", "deploy:check_revision"
end
namespace :log do
desc "A pinch of tail"
task :tailf, :roles => :app do
run "tail -n 10000 -f #{shared_path}/log/#{rails_env}.log" do |channel, stream, data|
puts "#{data}"
break if stream == :err
end
end
end
upstream app_name {
server unix:/tmp/unicorn.app_name.sock fail_timeout=0;
}
server {
listen 80 default deferred;
server_name _;
root /home/deployer/apps/app_name/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
# Favicon
location ~* \.ico$ {
expires 1w;
access_log off;
add_header Cache-Control "public";
}
# Media: images, video, audio, HTC, WebFonts
location ~* \.(?:jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|eot|mp4|ogg|ogv|webm)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
try_files $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_name;
#if ($request_uri ~* "\.(ico|css|js|gif|jpe?g|png)\?[0-9]+$") {
# expires max;
# break;
#}
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
root = "/home/deployer/apps/app_name/current"
working_directory root
pid "/tmp/unicorn.app_name.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.app_name.sock"
worker_processes 4
timeout 20
preload_app true
before_exec do |server|
ENV["BUNDLE_GEMFILE"] = "#{root}/Gemfile"
end
before_fork do |server, worker|
# the following is highly recomended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
if defined?(ActiveRecord::Base)
ActiveRecord::Base.connection.disconnect!
end
if defined?(Resque)
Resque.redis.quit
end
# Before forking, kill the master process that belongs to the .oldbin PID.
# This enables 0 downtime deploys.
old_pid = "/tmp/unicorn.app_name.pid.oldbin"
if File.exists?(old_pid) && server.pid != old_pid
begin
Process.kill("QUIT", File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
# someone else did our job for us
end
end
sleep 1
end
after_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.establish_connection
end
if defined?(Resque)
Resque.redis = 'localhost:6379'
end
end
#!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Manage unicorn server
# Description: Start, stop, restart unicorn server for a specific application.
### END INIT INFO
set -e
# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/home/deployer/apps/app_name/current
PID=/tmp/unicorn.app_name.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=deployer
set -u
OLD_PIN="$PID.oldbin"
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
}
run () {
if [ "$(id -un)" = "$AS_USER" ]; then
eval $1
else
su -c "$1" - $AS_USER
fi
}
case "$1" in
start)
sig 0 && echo >&2 "Already running" && exit 0
run "$CMD"
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig HUP && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
run "$CMD"
;;
upgrade)
if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
then
n=$TIMEOUT
while test -s $OLD_PIN && test $n -ge 0
do
printf '.' && sleep 1 && n=$(( $n - 1 ))
done
echo
if test $n -lt 0 && test -s $OLD_PIN
then
echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
exit 1
fi
exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
run "$CMD"
;;
reopen-logs)
sig USR1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
exit 1
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment