Skip to content

Instantly share code, notes, and snippets.

@Godoy
Last active June 20, 2017 21:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Godoy/7a04acf42a382bd03ed1 to your computer and use it in GitHub Desktop.
Save Godoy/7a04acf42a382bd03ed1 to your computer and use it in GitHub Desktop.
deploy ruby on rails 4 with capistrano nginx unicorn - ubuntu 14.04

Steps

These steps were made with Ubuntu 14.04 e ruby 2.3.1. To enjoy the ease of step-by-step, I suggest not change folders and application name - just change the server ip and git

Create user to deploy

# Server console
sudo adduser deployer
sudo gpasswd -a deployer sudo

Config ssh access

# Server console
sudo nano /etc/ssh/sshd_config
# change to yes:
PasswordAuthentication yes

This point forward use the user "deployer"

Install libraries

# Server console
sudo apt-get update
sudo apt-get install curl
sudo apt-get install git-core
sudo apt-get install imagemagick
sudo apt-get install nodejs
# Mysql
sudo apt-get install libmysqlclient-dev mysql-server
# Postgresql
# sudo apt-get install postgresql postgresql-contrib libpq-dev 
sudo apt-get install nginx
sudo apt-get install libgmp-dev (http://www.nokogiri.org/tutorials/installing_nokogiri.html#ubuntu___debian)

Install rvm, ruby and bundler

Change the ruby version if necessary

# Server console
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
\curl -sSL https://get.rvm.io | bash
source ~/.rvm/scripts/rvm

rvm requirements
rvm install 2.3.1
rvm use 2.3.1 --default
gem install bundler

Start and configure Nginx

# Server console
sudo service nginx start

# Edit nginx default site
sudo nano /etc/nginx/sites-enabled/default
# file: /etc/nginx/sites-enabled/default 

  upstream unicorn {
    server unix:/tmp/unicorn.rails.sock fail_timeout=0;
  }
  
  server {
    listen 80 default deferred;
    # server_name example.com;
    root /home/deployer/railsapp/current/public;
  
    location ^~ /assets/ {
      gzip_static on;
      expires max;
      add_header Cache-Control public;
    }
  
    location ~ ^/(robots.txt|sitemap.xml.gz)/ {
      root /home/deployer/railsapp/current/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://unicorn;
    }
  
    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
  }

Config unicorn and capistrano in project

# Gemfile:
  gem 'unicorn'
  group :development do
    gem 'capistrano-rails'
    gem 'capistrano-rvm'
    gem 'capistrano3-unicorn'
  end
# Development console in rails folder

bundle install
cap install
sudo nano config/unicorn/production.rb  # Create if not exists
# file: config/unicorn/production.rb

root = "/home/deployer/railsapp/current"
working_directory root

pid "#{root}/tmp/pids/unicorn.pid"

stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"

worker_processes 2
timeout 30
preload_app true

listen '/tmp/unicorn.rails.sock', backlog: 64

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
  ENV['BUNDLE_GEMFILE'] = File.join(root, 'Gemfile')
end

Edit Capfile and replace all the content

# file: Capfile 
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'

# Load custom tasks from `lib/capistrano/tasks' if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Edit config/deploy.rb and changes git@git.teste.git with your repo

# file: config/deploy.rb

lock '3.4.0'

set :application, 'railsapp'
set :repo_url, 'git@git.teste.git'

ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call

set :use_sudo, false
set :bundle_binstubs, nil
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

after 'deploy:publishing', 'deploy:restart'

namespace :deploy do
  task :restart do
    invoke 'unicorn:legacy_restart'
    on roles(:app) do
      within current_path do
        with rails_env: fetch(:rails_env, 'production') do
          execute :bundle, 'exec', :rake, 'tmp:cache:clear'
        end
      end
    end
  end
end

Edit config/deploy/production.rb and change the server ip 122.125.125.123

# file: config/deploy/production.rb

set :port, 22
set :user, 'deployer'
set :deploy_via, :remote_cache
set :use_sudo, false

server '122.125.125.123',
  roles: [:web, :app, :db],
  port: fetch(:port),
  user: fetch(:user),
  primary: true

set :deploy_to, "/home/deployer/railsapp"

set :ssh_options, {
  forward_agent: true,
  auth_methods: %w(publickey),
  user: 'deployer',
}
#or ask for password
#set :ssh_options, {
#   config: false
#}

set :rails_env, :production
set :conditionally_migrate, true

Add SSH Key to Server

Only if you don't want be asked for pass

$ cat ~/.ssh/id_rsa.pub | ssh -p 22 username@123.123.123.123 'cat >> ~/.ssh/authorized_keys'

Init unicorn on ubuntu

Edit or create file config/unicorn/unicorn_service.sh

#!/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/railsapp/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn/production.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

Reference unicorn_service starter on init ubuntu

# Server console
sudo  nano /etc/init/unicorn.conf
# Set init ubuntu
description     "Start Unicorn"
start on startup
task
exec sh /home/deployer/railsapp/current/config/unicorn/unicorn_service.sh start

Commit the repo changes, restart the server and be happy with cap production deploy

PS: remember to set production secrets in config/initializers/devise.rb and config/secrets.yml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment