Showing posts with label unicorn. Show all posts
Showing posts with label unicorn. Show all posts

Thursday, November 26, 2015

Why I moved from capistrano to mina to deploy my app?


To be honest, I have just started to use deployment tools because doing the same thing over and over again was getting very tedious for me. I first started with capistrano and it was good I guess.

Why I prefer Mina over Capistrano?

While Capistrano is a really really great tool , it is terribly slow. Since time is money for every one of us, I wanted a tool that would not be so painfully slow and easy to use, you know , that get shits done. Then I met Mina and instantly fell in love with it when I browsed its homepage and read its slogan "Really fast deployer  and server automation tool" .

Before I was using any deployment tools I was manually ssh`ing into my server and then cloning the repo from the git then running bundle install , rake db:migrate and a lot of stuffs that is not very interesting to do repeatedly. If I had pushed any new updates to the repo I would have to ssh into the server and do the same thing which is time consuming as well as not very productive. So I highly reckon anyone who is not using deployment tools to use it and feel its power. But remember Great power comes with Great responsibility. :)

It is dead simple to get started with Mina. All you have to do is add a gem run the bundler, create a deploy.rb file , fill the right info in it and running mina deploy. The best part is you can write rake tasks in deploy.rb.

Either run gem install mina or add mina gem in your gemfile.
Run bundle install if you chose the later option.
run mina init in your working directory
Here is my deploy.rb file
require 'mina/bundler'
require 'mina/rails'
require 'mina/git'

# Basic settings:
#   domain       - The hostname to SSH to.
#   deploy_to    - Path to deploy into.
#   repository   - Git repo to clone from. (needed by mina/git)
#   branch       - Branch name to deploy. (needed by mina/git)

set :user, 'ubuntu'
set :domain, 'www.fuitter.com'
set :deploy_to, '/usr/share/nginx/html/fuitter'
set :repository, 'git@bitbucket.org:mc_cannibal/fuitter2.git'
set :branch, 'master'
set :forward_agent, true

# Manually create these paths in shared/ (eg: shared/config/database.yml) in your server.
# They will be linked in the 'deploy:link_shared_paths' step.
set :shared_paths, ['config/database.yml', 'config/secrets.yml', 'log']

# Optional settings:
#   set :user, 'foobar'    # Username in the server to SSH to.
#   set :port, '30000'     # SSH port number.
#   set :forward_agent, true     # SSH forward_agent.

# This task is the environment that is loaded for most commands, such as
# `mina deploy` or `mina rake`.
task :environment do
  ruby_version = File.read('.ruby-version').strip
  raise "Couldn't determine Ruby version: Do you have a file .ruby-version in your project root?" if ruby_version.empty?
 queue %{
   source /home/ubuntu/.rvm/bin/rvm
   rvm use #{ruby_version} || exit 1
 }
end

task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/log"]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]

  # Add the repository server to .ssh/known_hosts
  if repository
    repo_host = repository.split(%r{@|://}).last.split(%r{:|\/}).first
    repo_port = /:([0-9]+)/.match(repository) && /:([0-9]+)/.match(repository)[1] || '22'

    queue! %[
      if ! ssh-keygen -H  -F #{repo_host} &>/dev/null; then
        ssh-keyscan -t rsa -p #{repo_port} -H #{repo_host} >> ~/.ssh/known_hosts
      fi
    ]
  end

  # Create database.yml for Postgres if it doesn't exist
  path_database_yml = "#{deploy_to}/#{shared_path}/config/database.yml"
  database_yml = %[production:
  database: fuitter
  adapter: postgresql
  pool: 5
  timeout: 5000]
  queue! %[ test -e #{path_database_yml} || echo "#{database_yml}" > #{path_database_yml} ]

  # Create secrets.yml if it doesn't exist
  path_secrets_yml = "#{deploy_to}/#{shared_path}/config/secrets.yml"
  secret =
  secrets_yml = %[production:
  secret_key_base:
    #{`rake secret`.strip}]
  queue! %[ test -e #{path_secrets_yml} || echo "#{secrets_yml}" > #{path_secrets_yml} ]

  queue! %[chmod g+rx,u+rwx,o-rwx "#{deploy_to}/#{shared_path}/config"]

end

desc "Deploys the current version to the server."
task :deploy => :environment do
  to :before_hook do
    # Put things to run locally before ssh
  end
  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    to :launch do
      # queue "mkdir -p #{deploy_to}/#{current_path}/tmp/"
      # queue "service #{user} restart"
    end
  end
end

# For help in making your deploy script, see the Mina documentation:
#
#  - http://nadarei.co/mina
#  - http://nadarei.co/mina/tasks
#  - http://nadarei.co/mina/settings
#  - http://nadarei.co/mina/helpers

then run mina setup and finally mina deploy.
That is all there is to it.

Monday, October 5, 2015

Restarting unicorn


Every time you make any changes to your config files , you have to restart you server, at least that is what rails will notify you. It would be easy as pressing ctrl + c  if only you had run the rails server by doing rails s. But that is not the case if you are using servers such as nginx as your reverse proxy.

What you can do is, run service unicorn_appname restart in your app directory. If it works then that is fine. But if it does not and show you an error telling you to check your stderr file in the log directory, then follow on.
If you take a look at you stderr log file you will see something like this.
It is telling me that there is a process already running. So you have to kill that process and start your unicorn application server again.

Run ps aux | grep unicorn and it will list all the running processes. Since unicorn is running on 2710 i need to kill it.
pkill 2710 will kill the process and after that run service unicorn_fuitter start and now your app will be working just fine.

Sunday, October 4, 2015

Running rails app with Unicorn + Nginx


If you have come from PHP background , you will probably loose your nerve trying to get a simple hello world rails app to work with your server. Getting rails app to work with the server is a little different from any php apps where you just have to place your php app inside the /html folder(in my case).

My system has RVM, rails , ruby, nginx, postgres.

First we need to install unicorn. Unicorn is an application server. Since unicorn cant not be accessed by users directly, we will use Nginx as a reverse proxy.

Navigate to your project directory and open your Gemfile and add gem 'unicorn' gem. Then run bundle install.
Now we need to configure it.

Inside your config directory add unicorn.rb file.

unicorn.rb

# set path to application
app_dir = File.expand_path('./')
shared_dir = "#{app_dir}/tmp"
working_directory app_dir


# Set unicorn options
worker_processes 2
preload_app true
timeout 30

# Set up socket location
listen "#{shared_dir}/sockets/unicorn.sock", :backlog => 64

# Logging
stderr_path "#{shared_dir}/log/unicorn.stderr.log"
stdout_path "#{shared_dir}/log/unicorn.stdout.log"

# Set master PID location
pid "#{shared_dir}/pids/unicorn.pid"


Now lets create the required directories

mkdir -p tmp/log tmp/sockets tmp/pids 

Give those directories necessary read/write permission
Now we will create a init script that will load on boot.

sudo nano /etc/init.d/unicorn_appname
 
You can name appname whatever you want
unicorn_appname

#!/bin/sh

### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the unicorn app server
# Description:       starts unicorn using start-stop-daemon
### END INIT INFO

set -e

USAGE="Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"

# app settings
USER="sushant"
APP_NAME="appname"
APP_ROOT="/usr/share/nginx/html/app" #in my case
ENV="development"

# environment settings
PATH="/home/$USER/.rvm/shims:/home/$USER/.rvm/bin:$PATH"
CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
PID="$APP_ROOT/shared/pids/unicorn.pid"
OLD_PID="$PID.oldbin"

# make sure the app exists
cd $APP_ROOT || exit 1

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
  test -s $OLD_PID && kill -$1 `cat $OLD_PID`
}

case $1 in
  start)
    sig 0 && echo >&2 "Already running" && exit 0
    echo "Starting $APP_NAME"
    su - $USER -c "$CMD"
    ;;
  stop)
    echo "Stopping $APP_NAME"
    sig QUIT && exit 0
    echo >&2 "Not running"
    ;;
  force-stop)
    echo "Force stopping $APP_NAME"
    sig TERM && exit 0
    echo >&2 "Not running"
    ;;
  restart|reload|upgrade)
    sig USR2 && echo "reloaded $APP_NAME" && exit 0
    echo >&2 "Couldn't reload, starting '$CMD' instead"
    $CMD
    ;;
  rotate)
    sig USR1 && echo rotated logs OK && exit 0
    echo >&2 "Couldn't rotate logs" && exit 1
    ;;
  *)
    echo >&2 $USAGE
    exit 1
    ;;
esac


Update the scripts permission and enable unicorn to boot on start

sudo chmod 755 /etc/init.d/unicorn_appname
sudo update-rc.d unicorn_appname defaults
 
Now open

sudo nano /etc/nginx/sites-available/default
 
default

upstream app {
    # Path to Unicorn SOCK file, as defined previously
    server unix:/usr/share/nginx/html/app/tmp/sockets/unicorn.sock fail_timeout=0; #in my case
}

server {
    listen 80;
    server_name example.com;

    root /usr/share/nginx/html/app/public; #in my case

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}
  

Open your hosts file and add
127.0.0.1       example.com
then
sudo service unicorn_appname restart
sudo service nginx restart  If you browse to example.com your app should be running