Created
November 4, 2011 04:58
-
-
Save ahoward/1338691 to your computer and use it in GitHub Desktop.
how dojo4 is running apache -> unicorn | file ./script/unicorn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
# file: RAILS_ROOT/script/unicorn | |
# | |
# this script has a runs a rails app under a specific rails_env and ruby | |
# version based on a local cap configuration. it has a few goals: | |
# | |
# - a single script is used to run servers for all of | |
# - local development | |
# - init.id on the remote server | |
# - remote cap start/restart | |
# | |
# | |
# - the script ensures | |
# - the right ruby version is being run | |
# - the right user:group is running the app | |
# - rolling (0 downtime) deploys | |
# | |
# | |
# - this script can load a Rails.root/config/unicorn.yml and fold in any | |
# environment settings. it is in this way that a specific rails_env, ruby | |
# version, or unix user/group can be specified for both init.d *and* cap | |
# deployments. | |
# | |
# | |
# - the script has a bit if ruby/unix magic in it | |
# - it can alternately be loaded as both a script or the script's config file | |
# - it will reexec with the correct ruby if the wrong one is run | |
# | |
# | |
# - the built-in unicorn configuration handles re-opening sockets for common | |
# O(RD)Ms and also ensures running the app as the configured user:group in the | |
# case of an init.d start | |
# | |
# it is probably worth noting that the unicorn cluster is fronted via apache | |
# or nginx. here is an apache config which says: proxy everything to unicorn | |
# *except* in the case of a static asset which may be accelerated. | |
# | |
# <VirtualHost *:80> | |
# PassengerEnabled off | |
# ServerName rails_app.dojo4.com | |
# | |
# DocumentRoot /ebs/apps/rails_app/current/public | |
# | |
# <Directory /ebs/apps/rails_app/current/public/> | |
# AllowOverride all | |
# Options -MultiViews | |
# </Directory> | |
# | |
# RewriteEngine On | |
# | |
# <Proxy balancer://unicornservers> | |
# BalancerMember http://0.0.0.0:4202 retry=0 | |
# </Proxy> | |
# | |
# <Proxy *> | |
# Order deny,allow | |
# Allow from all | |
# </Proxy> | |
# | |
# RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f | |
# RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L] | |
# | |
# ProxyPreserveHost on | |
# | |
# ErrorDocument 503 "Oh noes! Teh app is blown!" | |
# | |
# XSendFile On | |
# XSendFileAllowAbove on | |
# </VirtualHost> | |
# | |
# | |
INTERPRET_THIS_FILE_AS_A_SCRIPT = $0 == __FILE__ | |
INTERPRET_THIS_FILE_AS_A_CONFIG = !INTERPRET_THIS_FILE_AS_A_SCRIPT | |
if INTERPRET_THIS_FILE_AS_A_SCRIPT | |
# setup | |
# | |
require 'fileutils' | |
require 'rbconfig' | |
require 'yaml' | |
require 'ostruct' | |
Unicorn = OpenStruct.new | |
THIS_SCRIPT = File.expand_path(__FILE__) | |
SCRIPT_DIR = File.dirname(THIS_SCRIPT) | |
RAILS_ROOT = File.dirname(SCRIPT_DIR) | |
# fold any settings from ./config/unicorn.yml into the environment. we can | |
# use these to force a particular RAILS_ENV setting, a particular ruby | |
# version, or a particular user for the application to run as. these | |
# settings will apply whether this script is called from init.d or via a cap | |
# deploy - this file would normally *not* be in revision control but | |
# symlink'd in via cap... | |
# | |
# example ./config/unicorn.yml | |
# | |
# RAILS_ENV : production | |
# RAILS_STAGE : staging | |
# UNICORN_RUBY : /usr/local/rbenv/versions/1.9.3-rc1/bin/ruby | |
# UNICORN_USER : dojo4 | |
# UNICORN_GROUP : dojo4 | |
# | |
require 'yaml' unless defined?(YAML) | |
config_yml = File.expand_path('../../config/unicorn.yml', __FILE__) | |
if test(?s, config_yml) | |
config = YAML.load(IO.read(config_yml)) | |
if config.is_a?(Hash) | |
config.each do |key, val| | |
key, val = key.to_s, val.to_s | |
ENV[key] ||= val unless val.strip.empty? | |
end | |
end | |
end | |
# setup RAILS_ENV and RAILS_STAGE | |
# | |
# many of our applications accept compound RAILS_ENV settings such as | |
# | |
# production:staging | |
# | |
# meaning "run in production mode" but, for instance, do not process credit | |
# cards... | |
# | |
# we handle this here, and also bring RAILS_ENV and RAILS_STAGE into the | |
# codez | |
# | |
ENV['RAILS_ENV'] ||= 'development' | |
parts = ENV['RAILS_ENV'].to_s.split(%r/[._:-]+/) | |
if parts.size > 1 | |
rails_env = parts.shift | |
rails_stage = parts.shift | |
ENV['RAILS_ENV'] = rails_env | |
ENV['RAILS_STAGE'] ||= rails_stage | |
abort('conflicting RAILS_STAGE setting') unless ENV['RAILS_STAGE'] == rails_stage | |
else | |
ENV['RAILS_STAGE'] ||= ENV['RAILS_ENV'] | |
end | |
RAILS_ENV = ENV['RAILS_ENV'] | |
RAILS_STAGE = ENV['RAILS_STAGE'] | |
# we'll be using knowledge of the currently executing ruby for some | |
# cleverness below: mainly re-exec'ing if the wrong one is running... | |
# | |
rb = rb_config = ::RbConfig::CONFIG | |
THIS_RUBY = File.join(rb['bindir'], rb['ruby_install_name']) << rb['EXEEXT'] | |
# configure teh unicorn via command line and potentially folded environment | |
# | |
mode = ARGV.first || 'restart' | |
daemon = ARGV.include?('-d') || ARGV.include?('--daemon') || (STDIN.tty? ? false : true) | |
Unicorn.mode = mode | |
Unicorn.daemon = daemon | |
Unicorn.rails_env = RAILS_ENV | |
Unicorn.rails_stage = RAILS_STAGE | |
Unicorn.rails_root = RAILS_ROOT | |
Unicorn.config_file = THIS_SCRIPT | |
Unicorn.pid_file = File.join(RAILS_ROOT, "tmp/pids/unicorn.pid") | |
Unicorn.port = (ENV['UNICORN_PORT'] ||= '3000') | |
Unicorn.ruby = (ENV['UNICORN_RUBY'] ||= THIS_RUBY) | |
Unicorn.user = (ENV['UNICORN_USER'] ||= Etc.getpwuid.name) | |
Unicorn.group = (ENV['UNICORN_GROUP'] ||= Etc.getgrgid.name) | |
# iff a particular ruby has been specified, and we are *not* running it, | |
# re-exec using the configured ruby to remedy the situation | |
# | |
if THIS_RUBY != Unicorn.ruby | |
warn("replacing sucky ruby #{ THIS_RUBY } with #{ Unicorn.ruby } ...") | |
argv = [Unicorn.ruby, script, *ARGV].join(' ') | |
exec(argv) | |
end | |
# ensure that ruby's bin path is inherited by the unicorn application for | |
# app 'system' and backtick hygiene. | |
# | |
bindir = rb_config['bindir'] | |
path = ENV['PATH'] | |
ENV['PATH'] = "#{ bindir }:#{ path }" | |
# let's run outta rails root - just for sanity | |
# | |
Dir.chdir(RAILS_ROOT) | |
# so. this is what we gonna do... | |
# | |
stop_command = "kill -QUIT `cat #{ Unicorn.pid_file }`" | |
start_command = "unicorn_rails -E #{ Unicorn.rails_env }:#{ Unicorn.rails_stage } -c #{ Unicorn.config_file } -p #{ Unicorn.port }" | |
restart_command = "kill -USR2 `cat #{ Unicorn.pid_file }`" | |
start_command << " -D" | |
#puts stop_command | |
#puts start_command | |
#puts restart_command | |
# dump pid iff asked | |
# | |
if Unicorn.mode == 'pid' | |
begin | |
puts IO.read(Unicorn.pid_file) | |
rescue | |
exit(1) | |
end | |
end | |
# clean up old unicorn iff needed | |
# | |
if Unicorn.mode == 'stop' | |
exec(stop_command) | |
end | |
# start a new one | |
# | |
if Unicorn.mode == 'start' | |
system(stop_command) if test(?s, Unicorn.pid_file) | |
FileUtils.touch(File.join(RAILS_ROOT, 'tmp', 'restart.txt')) | |
exec(start_command) | |
end | |
# (re)start | |
# | |
if Unicorn.mode =='restart' | |
FileUtils.touch(File.join(RAILS_ROOT, 'tmp', 'restart.txt')) | |
if test(?s, Unicorn.pid_file) | |
exec(restart_command) | |
else | |
exec(start_command) | |
end | |
end | |
end ### interpret_this_file_as_a_script | |
if INTERPRET_THIS_FILE_AS_A_CONFIG | |
rails_env = ENV['RAILS_ENV'] || 'development' | |
rails_root = File.dirname(File.dirname(__FILE__)) | |
worker_processes (rails_env == 'production' ? 8 : 2) | |
preload_app true | |
timeout 30 | |
#listen '/data/github/current/tmp/sockets/unicorn.sock', :backlog => 2048 | |
if GC.respond_to?(:copy_on_write_friendly=) | |
GC.copy_on_write_friendly = true | |
end | |
unless STDIN.tty? | |
stderr_path rails_root + "/log/unicorn.stderr.log" | |
stdout_path rails_root + "/log/unicorn.stdout.log" | |
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 | |
# someone else did our job for us | |
end | |
end | |
end | |
after_fork do |server, worker| | |
Mongoid.reconnect! if defined?(Mongoid) | |
ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base) | |
# unicorn master might be started as root, which is fine, but let's drop the | |
# workers to the app's configured user/group | |
# | |
begin | |
uid, gid = Process.euid, Process.egid | |
user, group = ENV['UNICORN_USER'], ENV['UNICORN_GROUP'] | |
if user and group | |
target_uid = Etc.getpwnam(user).uid | |
target_gid = Etc.getgrnam(group).gid | |
worker.tmp.chown(target_uid, target_gid) | |
if uid != target_uid || gid != target_gid | |
Process.initgroups(user, target_gid) | |
Process::GID.change_privilege(target_gid) | |
Process::UID.change_privilege(target_uid) | |
end | |
end | |
rescue Object => e | |
#if rails_env == 'development' | |
STDERR.puts "couldn't change user, oh well. shit might blow up l8r..." | |
#else | |
#raise e | |
#end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment