Little notes about multistaging nginx + unicorn
I made this task for quite some time, so I can not reproduce the process in order, so I will describe just how it should looks in the end.
Why Unicorn? It was a rails app, which didn't have mobile interface so all clients are fast. In plans it shouldn't be really highloaded (as I can see now less than 1000 requests per day) so fast and light unicorn is best choise, I think.
Also we should have 2 rails app running on one physical server (production and staging). So, how I made this:
/etc/nginx/nginx.conf
user www-data; worker_processes 4; pid /var/run/nginx.pid; events { worker_connections 512; } http { ## # Basic Settings ## sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048;
include /etc/nginx/mime.types; default_type application/octet-stream; ## # Logging Settings ## access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; ## # Gzip Settings ## gzip on; gzip_disable "msie6"; ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; }
Two files in sites-enabled directory:
/etc/nginx/sites-enabled/staging
server { # We will take access to staging by that port listen 4321; server_name plsnr.ru plsnr.com *.plsnr.ru; access_log /home/user/var/www/staging/shared/logs/access.log; error_log /home/user/var/www/staging/shared/logs/error.log; # Максимальный размер тела запроса клиента client_max_body_size 101M; location ~ /.svn/ { deny all; } location ~ /.git/ { deny all; } location ~ /\.ht { deny all; } location / { root /home/user/var/www/staging/current/public; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://127.0.0.1:3001; break; } } error_page 500 502 503 504 /500.html; location = /500.html { root /home/user/var/www/staging/current/public; } }
/etc/nginx/sites-enabled/plsnr.ru
server { # listen default http port listen 80; server_name plsnr.ru plsnr.com *.plsnr.ru; access_log /home/user/var/www/production/shared/logs/access.log; error_log /home/user/var/www/production/shared/logs/error.log; # Максимальный размер тела запроса клиента client_max_body_size 101M; location ~ /.svn/ { deny all; } location ~ /.git/ { deny all; } location ~ /\.ht { deny all; } location / { root /home/user/var/www/production/current/public; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://127.0.0.1:3000; break; } } error_page 500 502 503 504 /500.html; location = /500.html { root /home/user/var/www/production/current/public; } }
We have no any specific unicorn config file and identical RAILS_APP/config/environments/production.rb and RAILS_APP/config/environments/staging.rb
Plsnr::Application.configure do config.cache_classes = true config.consider_all_requests_local = false config.action_controller.perform_caching = true config.action_dispatch.x_sendfile_header = "X-Sendfile" config.serve_static_assets = false config.action_mailer.raise_delivery_errors = false config.action_mailer.default_url_options = { :host => 'plsnr.ru' } config.action_mailer.delivery_method = :ses config.i18n.fallbacks = true config.active_support.deprecation = :notify Paperclip.options[:command_path] = "/usr/bin" end
It's all about nginx+unicorn configuring. Next step is deployment. Certanly, capistrano.
RAILS_APP/config/deploy.rb
require 'bundler/capistrano' require 'capistrano/ext/multistage' set :stages, %w(production staging) set :default_stage, "staging" set :domain, "[email protected]" set :backup_dir, "/home/ouruser/var/www/production/backup" set :use_sudo, false set :application, "plsnr" set :scm, :git set :repository, "[email protected]/plsnr.git" set :deploy_via, :remote_cache role :web, domain role :app, domain role :db, domain, :primary => true after 'deploy:update_code', :roles => :app do run "ln -s #{deploy_to}/shared/public/uploads #{current_release}/public/uploads" backup end # backup db task :backup, :roles => :db, :only => { :primary => true } do filename = "#{backup_dir}/#{application}.dump.#{Time.now.to_f}.sql.bz2" text = capture "cat #{deploy_to}/current/config/database.yml" yaml = YAML::load(text) on_rollback { run "rm #{filename}" } run "mysqldump -u #{yaml['production']['username']} -p #{yaml['production']['database']} | bzip2 -c > #{filename}" do |ch, stream, out| ch.send_data "#{yaml['production']['password']}\n" if out =~ /^Enter password:/ end end namespace :deploy do task :start do run "cd #{deploy_to}/current && bundle exec unicorn_rails -E #{rails_env} -p #{unicorn_port} -D" end task :stop do run "kill `cat #{unicorn_pid}`" end task :graceful_stop do run "kill -s QUIT `cat #{unicorn_pid}`" end task :reload do run "kill -s USR2 `cat #{unicorn_pid}`" end task :restart do stop start end end
RAILS_APP/config/deploy/production.rb
set :branch, "master" set :rails_env, "production" set :unicorn_port, "3000" set :deploy_to, "/home/user/var/www/production" set :unicorn_pid, "#{deploy_to}/shared/tmp/pids/unicorn.pid"
RAILS_APP/config/deploy/staging.rb
set :branch, "staging" set :rails_env, "staging" set :unicorn_port, "3001" set :deploy_to, "/home/user/var/www/staging" set :unicorn_pid, "#{deploy_to}/shared/tmp/pids/unicorn.pid"
Thats all! If I haven't forgotten anythink %)
So production we have on
http://plsnr.ru/
and staging on
http://plsnr.ru:4321
P.S. Yes we needed one domain name %)
P.P.S. Site name isn't real
Good post about Unicorn (if you want to customize it) https://github.com/blog/517-unicorn













