Развёртывание RoR приложения

Накидал пока в виде заметки. Возможно позже попробую оформить в статью.

Настройка

Подключим Capistrano к проекту

group :development do
  ...
  gem 'capistrano', '~> 3.3.0', require: false
  gem 'capistrano-rbenv', require: false
  gem 'capistrano-bundler', require: false
  gem 'capistrano-rails', require: false
  gem 'capistrano-sidekiq', require: false
  gem 'capistrano3-unicorn', require: false
end

Раскоментируем гем unicorn.

bundle
cap install

Capfile

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails'
require 'capistrano3/unicorn'

# require 'capistrano/rails/assets'
# require 'capistrano/rails/migrations'

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


deploy.rb

lock '3.3.5'

set :application, 'kalibr'
set :repo_url, 'git@bitbucket.org:J-Son/kalibr.git'

# RBENV
set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.2.0'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default value

set :deploy_to, '/home/deployer/kalibr'
set :deploy_user, 'deployer'

set :linked_files, %w{config/database.yml .env}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/uploads}
set :keep_releases, 5

namespace :deploy do
  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      invoke 'unicorn:restart'
    end
  end
  after :publishing, :restart
end

production.rb

role :app, %w{deployer@xxx.xxx.xxx.xxx}
role :web, %w{deployer@xxx.xxx.xxx.xxx}
role :db,  %w{deployer@xxx.xxx.xxx.xxx}

set :rails_env, :production
set :stage, :production

server 'xxx.xxx.xxx.xxx', user: 'deployer', roles: %w{web app db}, primary: true

set :ssh_options, {
                    keys: %w(/Users/yuri/.ssh/id_rsa /home/yuri/.ssh/id_rsa),
                    forward_agent: true,
                    auth_methods: %w(publickey password),
                    port: 38
                }

Посмотреть все достуаные задачи можно так:

cap -T

Проверка конфигурации:

cap production deploy:check

Генерирование токена:

rake secret

Настройка NGINX

nginx.conf:

user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_prefer_server_ciphers on;
    #ssl_stapling on;
    resolver 8.8.8.8;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    gzip on;
    gzip_disable "msie6";
    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

kalibr:

upstream kalibr {
  server unix:/tmp/unicorn.kalibr.sock fail_timeout=0;
}

server {
    server_name  www.ltpcaliber.ru;
    rewrite ^(.*) http://ltpcaliber.ru$1 permanent;
}

server {
  listen 80;
  server_name ltpcaliber.ru;
  root /home/deployer/kalibr/current/public;
  client_max_body_size 30M;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

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

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

Пробный деплой

cap production deploy

Настраиваем Unicorn

unicorn/production.rb:

# paths
app_path = "/home/deployer/kalibr"
working_directory "#{app_path}/current"
pid               "#{app_path}/current/tmp/pids/unicorn.pid"

# listen
listen "/tmp/unicorn.kalibr.sock", backlog: 64

# logging
stderr_path "log/unicorn.stderr.log"
stdout_path "log/unicorn.stdout.log"

# workers
worker_processes 2

# use correct Gemfile on restarts
before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = "#{app_path}/current/Gemfile"
end

# preload
preload_app true

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

  # Before forking, kill the master process that belongs to the .oldbin PID.
  # This enables 0 downtime deploys.
  old_pid = "#{server.config[: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
end

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end
end

Init.d скрипт unicorn_kalibr:

#! /bin/sh
# cd /home/kalibr/site/current &&
# ( RBENV_ROOT=~/.rbenv RBENV_VERSION=2.1.3
#   RAILS_ENV=production RBENV_ROOT=~/.rbenv
#   RBENV_VERSION=2.1.3
#   ~/.rbenv/bin/rbenv exec bundle exec unicorn
#   -c /home/kalibr/site/shared/config/unicorn.rb
#   -E deployment -D
### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $all
# Required-Stop:     $network $local_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the unicorn web server
# Description:       starts unicorn
### END INIT INFO

# Change these to match your app:
USER=deployer
APP_NAME=kalibr
APP_PATH=/home/deployer
ENV=production
# ENV=deployment
RBENV_RUBY_VERSION=2.2.0

# env
APP_ROOT="$APP_PATH/$APP_NAME/current"
RBENV_ROOT="/home/$USER/.rbenv"
PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH"
SET_PATH="cd $APP_ROOT; rbenv rehash; rbenv local $RBENV_RUBY_VERSION"
DAEMON="bundle exec unicorn_rails"
DAEMON_OPTS="-c $APP_PATH/$APP_NAME/shared/config/unicorn.rb -E $ENV -D"
CMD="$SET_PATH; $DAEMON $DAEMON_OPTS"
NAME=unicorn
DESC="Unicorn app for $APP_NAME"
PID="$APP_PATH/$APP_NAME/shared/run/unicorn.pid"
OLD_PID="$PID.oldbin"

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-help} in
start)
        sig 0 && echo >&2 "Already running" && exit 0
        su - $USER -c "$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"
        su - $USER -c "$CMD"
        ;;
upgrade)
        sig USR2 && exit 0
        echo >&2 "Couldn't upgrade, starting '$CMD' instead"
        su - $USER -c "$CMD"
        ;;
rotate)
        sig USR1 && echo rotated logs OK && exit 0
        echo >&2 "Couldn't rotate logs" && exit 1
        ;;
*)
        echo >&2 "Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
        exit 1
        ;;
esac

exit 0

Автозапуск:

sudo chmod 755 /etc/init.d/unicorn_kalibr
sudo update-rc.d unicorn_kalibr defaults

Аварийный запуск Unicorn

#!/bin/bash

cd /home/deployer/keys/current
bundle exec unicorn -c /home/deployer/keys/current/config/unicorn/production.rb -E production -D
Поделиться Комментарии