Skip to content

Instantly share code, notes, and snippets.

@jhjguxin
Last active June 5, 2020 06:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jhjguxin/5849887 to your computer and use it in GitHub Desktop.
Save jhjguxin/5849887 to your computer and use it in GitHub Desktop.
like unix better,deploy rails application with unicorn + nginx <br/> the tour base on guanxi_cms

Unicorn: Rack HTTP server for fast clients and Unix

Unicorn is an HTTP server for Rack applications designed to only serve fast clients on low-latency, high-bandwidth connections and take advantage of features in Unix/Unix-like kernels. Slow clients should only be served by placing a reverse proxy capable of fully buffering both the the request and response in between Unicorn and slow clients.

Features

  • Designed for Rack, Unix, fast clients, and ease-of-debugging. We cut out everything that is better supported by the operating system, nginx or Rack.
  • Compatible with both Ruby 1.8 and 1.9. Rubinius support is in-progress.
  • Process management: Unicorn will reap and restart workers that die from broken apps. There is no need to manage multiple processes or ports yourself. Unicorn can spawn and manage any number of worker processes you choose to scale to your backend.
  • Load balancing is done entirely by the operating system kernel. Requests never pile up behind a busy worker process.
  • Does not care if your application is thread-safe or not, workers all run within their own isolated address space and only serve one client at a time for maximum robustness.
  • Supports all Rack applications, along with pre-Rack versions of Ruby on Rails via a Rack wrapper.
  • Builtin reopening of all log files in your application via USR1 signal. This allows logrotate to rotate files atomically and quickly via rename instead of the racy and slow copytruncate method. Unicorn also takes steps to ensure multi-line log entries from one request all stay within the same file.
  • nginx-style binary upgrades without losing connections. You can upgrade Unicorn, your entire application, libraries and even your Ruby interpreter without dropping clients.
  • before_fork and after_fork hooks in case your application has special needs when dealing with forked processes. These should not be needed when the “preload_app” directive is false (the default).
  • Can be used with copy-on-write-friendly memory management to save memory (by setting “preload_app” to true).
  • Able to listen on multiple interfaces including UNIX sockets, each worker process can also bind to a private port via the after_fork hook for easy debugging.
  • Simple and easy Ruby DSL for configuration.
  • Decodes chunked transfers on-the-fly, thus allowing upload progress notification to be implemented as well as being able to tunnel arbitrary stream-based protocols over HTTP.

Usage

non-Rails Rack applications

In APP_ROOT, run:

unicorn

for Rails applications (should work for all 1.2 or later versions) In RAILS_ROOT, run:

unicorn_rails

Unicorn will bind to all interfaces on TCP port 8080 by default. You may use the --listen/-l switch to bind to a different address:port or a UNIX socket.

configuration unicorn on rails

# Sample verbose configuration file for Unicorn (not Rack)
#
# This configuration file documents many features of Unicorn
# that may not be needed for some applications. See
# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
# for a much simpler configuration file.
#
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.

# Use at least one worker per core if you're on a dedicated server,
# more will usually help for _short_ waits on databases/caches.
worker_processes 15

# Since Unicorn is never exposed to outside clients, it does not need to
# run on the standard HTTP port (80), there is no reason to start Unicorn
# as root unless it's from system init scripts.
# If running the master process as root and the workers as an unprivileged
# user, do this to switch euid/egid in the workers (also chowns logs):
# user "unprivileged_user", "unprivileged_group"

module Rails
  class << self
    def root
      File.expand_path("../..", __FILE__)
    end
  end
end

# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
# working_directory "/path/to/app/current" # available in 0.94.0+
working_directory Rails.root

# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
# listen "/path/to/.unicorn.sock", :backlog => 64
listen "/tmp/unicorn.guanxi_cms.sock"
listen 8000, :tcp_nopush => true

# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30

# feel free to point this anywhere accessible on the filesystem
# pid "/path/to/app/shared/pids/unicorn.pid"
pid "#{Rails.root}/tmp/pids/unicorn.pid"

# By default, the Unicorn logger will write to stderr.
# Additionally, ome applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
# stderr_path "/path/to/app/shared/log/unicorn.stderr.log"
# stdout_path "/path/to/app/shared/log/unicorn.stdout.log"
stderr_path "#{Rails.root}/log/unicorn.log"
stdout_path "#{Rails.root}/log/unicorn.log"

# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
# http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-preload_app
# preload_app true

GC.respond_to?(:copy_on_write_friendly=) and
  GC.copy_on_write_friendly = true

# Enable this flag to have unicorn test client connections by writing the
# beginning of the HTTP headers before calling the application.  This
# prevents calling the application for connections that have disconnected
# while queued.  This is only guaranteed to detect clients on the same
# host unicorn runs on, and unlikely to detect disconnects even on a
# fast LAN.
check_client_connection false

module Rails
  class << self
    def root
      File.expand_path("../..", __FILE__)
    end
  end
end

rails_env = ENV["RAILS_ENV"] || "production"

before_exec do |server|
  ENV["BUNDLE_GEMFILE"] = "#{Rails.root}/Gemfile"
end

before_fork do |server, worker|
  old_pid = "#{Rails.root}/tmp/pids/unicorn.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
      puts "Send 'QUIT' signal to unicorn error!"
    end
  end
end


after_fork do |server, worker|
  # per-process listener ports for debugging/admin/migrations
  # addr = "127.0.0.1:#{9293 + worker.nr}"
  # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)

  # the following is *required* for Rails + "preload_app true",
  # defined?(ActiveRecord::Base) and
  #   ActiveRecord::Base.establish_connection

  # if preload_app is true, then you may also want to check and
  # restart any other shared sockets/descriptors such as Memcached,
  # and Redis.  TokyoCabinet file handles are safe to reuse
  # between any number of forked children (assuming your kernel
  # correctly implements pread()/pwrite() system calls)
end

deploy with capistrano

# unicorn.conf.rb path
set :unicorn_conf, "#{deploy_to}/current/config/unicorn.conf.rb"
set :unicorn_pid, "#{deploy_to}/current/tmp/pids/unicorn.pid"

namespace :deploy do
  task :start, :roles => :app do
    run "cd #{deploy_to}/current/; RAILS_ENV=production bundle exec unicorn_rails -c #{unicorn_conf} -D"
  end

  task :stop, :roles => :app do
    run "kill -QUIT `cat #{unicorn_pid}`"
  end

  desc "Restart Application"
  task :restart, :roles => :app do
    #run "kill -USR2 `cat #{deploy_to}/current/tmp/pids/unicorn.pid`"
    run "if [ -f #{unicorn_pid} ] ; then kill -USR2     \`cat #{unicorn_pid}\`; else cd #{deploy_to}/current && bundle exec unicorn -c #{unicorn_conf} -E #{rails_env} -D; fi"
  end
end

the better

http://my.oschina.net/cfanlds/blog/61890

configration the unicorn

#config/nginx.conf
# Sample verbose configuration file for Unicorn (not Rack)
#
# This configuration file documents many features of Unicorn
# that may not be needed for some applications. See
# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
# for a much simpler configuration file.
#
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.

# Use at least one worker per core if you're on a dedicated server,
# more will usually help for _short_ waits on databases/caches.
worker_processes 15

# Since Unicorn is never exposed to outside clients, it does not need to
# run on the standard HTTP port (80), there is no reason to start Unicorn
# as root unless it's from system init scripts.
# If running the master process as root and the workers as an unprivileged
# user, do this to switch euid/egid in the workers (also chowns logs):
# user "unprivileged_user", "unprivileged_group"

module Rails
  class << self
    def root
      File.expand_path("../..", __FILE__)
    end
  end
end

# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
# working_directory "/path/to/app/current" # available in 0.94.0+
working_directory Rails.root

# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
# listen "/path/to/.unicorn.sock", :backlog => 64
listen "/tmp/unicorn.guanxi_cms.sock"
listen 3000, :tcp_nopush => true

# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30

# feel free to point this anywhere accessible on the filesystem
# pid "/path/to/app/shared/pids/unicorn.pid"
pid "#{Rails.root}/tmp/pids/unicorn.pid"

# By default, the Unicorn logger will write to stderr.
# Additionally, ome applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
# stderr_path "/path/to/app/shared/log/unicorn.stderr.log"
# stdout_path "/path/to/app/shared/log/unicorn.stdout.log"
stderr_path "#{Rails.root}/log/unicorn.log"
stdout_path "#{Rails.root}/log/unicorn.log"

# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
preload_app true

GC.respond_to?(:copy_on_write_friendly=) and
  GC.copy_on_write_friendly = true

# Enable this flag to have unicorn test client connections by writing the
# beginning of the HTTP headers before calling the application.  This
# prevents calling the application for connections that have disconnected
# while queued.  This is only guaranteed to detect clients on the same
# host unicorn runs on, and unlikely to detect disconnects even on a
# fast LAN.
check_client_connection false

module Rails
  class << self
    def root
      File.expand_path("../..", __FILE__)
    end
  end
end

rails_env = ENV["RAILS_ENV"] || "production"

before_exec do |server|
  ENV["BUNDLE_GEMFILE"] = "#{Rails.root}/Gemfile"
end

before_fork do |server, worker|
  old_pid = "#{Rails.root}/tmp/pids/unicorn.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
      puts "Send 'QUIT' signal to unicorn error!"
    end
  end
end


after_fork do |server, worker|
  # per-process listener ports for debugging/admin/migrations
  # addr = "127.0.0.1:#{9293 + worker.nr}"
  # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)

  # the following is *required* for Rails + "preload_app true",
  # defined?(ActiveRecord::Base) and
  #   ActiveRecord::Base.establish_connection

  # if preload_app is true, then you may also want to check and
  # restart any other shared sockets/descriptors such as Memcached,
  # and Redis.  TokyoCabinet file handles are safe to reuse
  # between any number of forked children (assuming your kernel
  # correctly implements pread()/pwrite() system calls)
end

OobGC and unicorn-worker-killer

demo config.ru

# This file is used by Rack-based servers to start the application.
if defined?(ENV) and ENV["RAILS_ENV"].eql?("production")
#if self.class.const_defined?("Unicorn") and self.class.const_defined?("Unicorn::WorkerKiller")
  require 'unicorn/oob_gc'
  require 'unicorn/worker_killer'

  # 每10次请求,才执行一次GC
  use Unicorn::OobGC, 10
  # 设定最大请求次数后自杀,避免禁止GC带来的内存泄漏(1000~1800之间随机,避免同时多个进程同时自杀,可以和下面的设定任选)
  use Unicorn::WorkerKiller::MaxRequests, 1000, 1800
  # 设定达到最大内存后自杀,避免禁止GC带来的内存泄漏(192~256MB之间随机,避免同时多个进程同时自杀)
  use Unicorn::WorkerKiller::Oom, (192*(1024**2)), (256*(1024**2))
end

require ::File.expand_path('../config/environment',  __FILE__)
run GuanxiCms::Application

add nginx config file

#config/nginx.conf
upstream unicorn {
  server unix:/tmp/unicorn.guanxi_cms.sock fail_timeout=0;
}

server {
    listen 8000 default deferred;
    server_name cms.guanxi.me;

  root /home/guanxi/deployment/guanxi_cms/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;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
}
# sudo cp config/nginx.conf /etc/nginx/sites-enabled/

build init script

#!/bin/sh
# sudo cp config/unicorn_init.rh
### 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/guanxi/deployment/guanxi_cms/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; RAILS_ENV=production bundle exec unicorn -D -c $APP_ROOT/config/unicorn.conf.rb -E production"
AS_USER=guanxi
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
# sudo ln -nfs /data/guanxi/gxservice/config/unicorn_init.sh /etc/init.d/unicorn_gxserver
# chmod a+x /etc/init.d/unicorn_gxserver
# /etc/init.d/unicorn_gxserver restart

deploy with capistrano

namespace :deploy do
  %w[start stop restart].each do |command|
    desc "#{command} unicorn server"
    task command, roles: :app, except: {no_release: true} do
      run "chmod a+x /etc/init.d/unicorn_#{application}"
      run "/etc/init.d/unicorn_#{application} #{command}" # Using unicorn as the app server
    end
  end

  task :init_path, :roles => :web do
    #run "mkdir -p #{deploy_to}/shared/log"
    #run "mkdir -p #{deploy_to}/shared/pids"
    #run "mkdir -p #{deploy_to}/shared/assets"
  end

  task :setup_config, roles: :app do
    #run "sudo mkdir -p /etc/nginx/sites-enabled/"

    #sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
    sudo "ln -nfs #{current_path}/config/nginx.conf /opt/nginx/sites-enabled/#{application}"

    sudo "rm /etc/init.d/unicorn_#{application}"
    sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}.conf"
    run "chmod a+x /etc/init.d/unicorn_#{application}"
    #run "mkdir -p #{shared_path}/config"
    #put File.read("config/database.yml"), "#{shared_path}/config/database.yml"
    puts "Now edit the config files in #{shared_path}."
  end
end

执行下面的命令部署

cap deploy:setup_config
#cap deploy:cold

about signal

HUP - reloads config file and gracefully restart all workers. If the “preload_app” directive is false (the default), then workers will also pick up any application code changes when restarted. If “preload_app” is true, then application code changes will have no effect; USR2 + QUIT (see below) must be used to load newer code in this case. When reloading the application, Gem.refresh will be called so updated code for your application can pick up newly installed RubyGems. It is not recommended that you uninstall libraries your application depends on while Unicorn is running, as respawned workers may enter a spawn loop when they fail to load an uninstalled dependency.

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