Capistrano recipes for Rails
We deploy a lot of Rails applications and our developers have to solve similar problems each time during the deployment: how to run workers, how to generate crontab, how to precompile assets faster and so on. This collection of recipes helps us to solve them.
Recipe use:
-
foreman
+foreman_export_runitu
to generate runit scripts with the Procfile -
whenever
to generate crontab -
nginx
as proxy server
It also consider that you use rbenv on the server.
##Installation
gem 'capistrano_rails_recipes', :require => false
Capfile example:
require "capistrano_rails_recipes/capistrano"
set :repository, "git@github.com:..."
set :application, "my_application"
set :user, 'deploy'
set :branch, 'master'
set :deploy_to, "/path/to/app"
set :use_sudo, false
server 'web.example.com', :web, :app, :worker, :crontab
role :db, 'web.example.com', primary: true
OR last two line can be expressed like
task :production do
role :web, "web.example.com"
role :app, "app.example.com"
role :crontab, "app.example.com"
role, :db, "db.example.com", :primary => true
role, :worker, "workers.example.com"
end
task :staging do
server "stage.example.com", :web, :app, :crontab, :db, :worker
end
As you can see, we use use roles to bind the tasks, and there are some additions to roles and additional roles:
deploy:setup creates all necessary folders and symlinks to files:
config/nginx.conf -> /opt/nginx/conf/sites-enabled/my_application
creates deploy_to/shared/config folder
creates deploy_to/services folder
copies config/database.example.yml -> shared/config/database.yml
Run deploy:setup before your first deploy. See below how to configure services.
web compiles assets if content of app/assets
was changed since last deploy (add FORCE=1 to force the assets compilation)
app all files from shared/config
is being symlinked to current/config
like:
shared/config/database.yml -> current/config/database.yml
shared/config/settings/production.yml -> current/config/settings/production.yml
crontab generates crontab with whenever
gem, only if the content of config/schedule.rb
was changed (add FORCE=1 to force the crontab generation)
db run migrations only if db/migrate
was changed (add FORCE=1 to force migrations or SKIP_MIGRATION=1 to skip them)
worker Procfile exports runit configs to deploy_to/application/services
On deploy:restart runit workers is being restarted.
You can use some extra cap
tasks:
-
rails:console
to launchrails console
on remote server -
rails:dbconsole
to launchrails dbconsole
on remote server -
login
to open SSH session under userdeploy
and switch catalog to Capistrano'scurrent_path
Important
To run succesfully together with system wide rbenv, all you tasks in Procfile must be started with rbenv exec
##Configuring services
To run services just create Procfile in root of your app.
Procfile example:
sidekiq: rbenv exec bundle exec sidekiq -L sidekiq.log
web: rbenv exec bundle exec unicorn -c config/unicorn.rb -E production
Also you need to configure runit
to monitor deploy_to/services
folder.
##Nginx config file example for Unicorn
upstream unicorn_my_application {
server unix:/tmp/unicorn.my_application.sock fail_timeout=0;
}
server {
listen 80;
server_name web.example.com;
root deploy_to/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
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://unicorn_my_application;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
Change deploy_to
and my_application
to your actual values.
##Unicorn config file example
root = "deploy_to/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.my_application.sock"
worker_processes 5
timeout 30
Change deploy_to
and my_application
to your actual values.
##Capistrano
Default variables:
logger.level = Capistrano::Logger::DEBUG
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
set :bundle_cmd, "rbenv exec bundle"
set :bundle_flags, "--deployment --quiet --binstubs --shebang ruby-local-exec"
set :rake, -> { "#{bundle_cmd} exec rake" }
set :keep_releases, 7
set :scm, "git"
set :user, "deploy"
set :deploy_via, :unshared_remote_cache
set :copy_exclude, [".git"]
set :repository_cache, -> { "#{deploy_to}/shared/#{application}.git" }
set :normalize_asset_timestamps, false
##Bonus track
To enable silent mode, add ENV['CAP_SILENT_MODE']
before the require 'capistrano_evrone_recipes/capistrano'
in your Capfile