Skip to content

Instantly share code, notes, and snippets.

@heygrady
Forked from mpapis/README.md
Last active November 19, 2023 04:43
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save heygrady/5551597 to your computer and use it in GitHub Desktop.
Example setup for running Unicorn on Ubuntu to manage Sinatra applications.
2.0.0@mygemset

You have your Apps with specific Gemsets in RVM.

The files in the Gist go in the following locations:

  • /path/to/your/app/
  • .ruby-version
  • Gemfile
  • config.ru
  • app.rb
  • /etc/unicorn/
  • sites-available/
  • myapp.conf
  • sites-enabled
  • myapp.conf -> /etc/unicorn/sites-available/mysite
  • unicorn.rb
  • /etc/init.d/
  • unicorn

Initial Setup

RVM

Install RVM for Ubuntu

Install your Gemset

cd /path/to/your/app/
# this should set your RVM environment to 2.0.0@mygemset
bundle install
bundle update

Prepare tmp and log folders

mkdir tmp
mkdir tmp/pids
mkdir tmp/sockets
mkdir log

Wrap your Unicorn

The following commands creates a wrapped unicorn_rails bin. Be sure to replace the variables and that you have unicorn in your bundle.

rvmsudo rvm wrapper [RUBY VERSION]@[GEMSET] [GEMSET] unicorn_rails

Example:

rvmsudo rvm wrapper ruby-2.0.0@mygemset mygemset unicorn

You can locate this file with which bootup_unicorn

Create init.d and rc#.d scripts

Create the other files now, especially /etc/init.d/unicorn

sudo update-rc.d unicorn defaults 30
require 'rubygems'
require 'sinatra'
class App < Sinatra::Base
get '/' do
"Sinatra App"
end
end
# bundler
require 'rubygems'
require 'bundler'
Bundler.setup
# sinatra
require './app' # assumes app.rb
use Rack::ShowExceptions
run App.new
source 'https://rubygems.org'
# for sintatra
gem 'rvm'
gem 'unicorn'
gem 'sinatra'
gem 'sinatra-contrib'
# other gems my app needs
# gem 'somegem'
# This is the actual config for your app
# Save this to /etc/unicorn/sites-enabled/<APPNAME>.conf
APP_NAME=app-name
APP_ROOT=/path/to/app/
UNICORN=/path/to/bootup_unicorn
# Optional Settings
# APP_ENV=production
# UNICORN_CONFIG=/etc/unicorn/unicorn.rb
# UNICORN_WORKERS=10
# UNICORN_PORT= 4567
# UNICORN_USER=www-data
# UNICORN_GROUP=www-data
# 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.
@dir = ENV["APP_ROOT"] || File.dirname(__FILE__) + '/../'
@env = ENV["APP_ENV"] || "development"
# See https://rvm.io/integration/passenger/
if ENV['MY_RUBY_HOME'] && ENV['MY_RUBY_HOME'].include?('rvm')
begin
gems_path = ENV['MY_RUBY_HOME'].split(/@/)[0].sub(/rubies/,'gems')
ENV['GEM_PATH'] = "#{gems_path}:#{gems_path}@global"
require 'rvm'
RVM.use_from_path! File.dirname(File.dirname(__FILE__))
rescue LoadError
raise "RVM gem is currently unavailable."
end
end
# If you're not using Bundler at all, remove lines bellow
ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', File.dirname(__FILE__))
require 'bundler/setup'
# 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 worker_processes ENV["UNICORN_WORKERS"].to_i || 10
# 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"
if ENV["UNICORN_USER"]
if ENV["UNICORN_GROUP"]
user ENV["UNICORN_USER"], ENV["UNICORN_GROUP"]
else
user ENV["UNICORN_USER"]
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 @dir
# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
#listen "/tmp/.sock", :backlog => 64
listen ENV["UNICORN_PORT"].to_i || 4567, :tcp_nopush => true
listen "#{@dir}tmp/sockets/unicorn.sock", :backlog => 64
# 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 "#{@dir}tmp/pids/unicorn.pid"
# By default, the Unicorn logger will write to stderr.
# Additionally, some applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
# stderr_path "./log/unicorn.stderr.log"
# stdout_path "./log/unicorn.stdout.log"
stderr_path "#{@dir}log/unicorn.stderr.log"
stdout_path "#{@dir}log/unicorn.stdout.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
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
# defined?(ActiveRecord::Base) and
# ActiveRecord::Base.connection.disconnect!
# This allows a new master process to incrementally
# phase out the old master process with SIGTTOU to avoid a
# thundering herd (especially in the "preload_app false" case)
# when doing a transparent upgrade. The last worker spawned
# will then kill off the old master process with a SIGQUIT.
old_pid = "#{@dir}tmp/pids/unicorn.pid.oldbin"
if old_pid != server.pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
# Throttle the master from forking too quickly by sleeping. Due
# to the implementation of standard Unix signal handlers, this
# helps (but does not completely) prevent identical, repeated signals
# from being lost when the receiving process is busy.
# sleep 1
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
#!/bin/sh
### 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: Start the unicorns at boot
# Description: Enable at boot time.
### END INIT INFO
# This is /etc/init.d/unicorn_init (without .sh)
# init.d script for single or multiple unicorn installations. Expects at least one .conf
# file in /etc/unicorn
#
# Modified by http://github.com/killercup
# based on modified version by jay@gooby.org http://github.com/jaygooby
# which is based on http://gist.github.com/308216 by http://github.com/mguterl
#
## A sample /etc/unicorn/my_app.conf
##
## APP_ENV=production
## APP_ROOT=/var/apps/www/my_app/current
## UNICORN="/usr/local/rvm/bin/<WRAPPED_NAME>" #see rvm wrapper above
#
# This configures a unicorn master for your app at /var/apps/www/my_app/current running in
# production mode. It will read config/unicorn.rb for further set up.
#
# You should ensure different ports or sockets are set in each config/unicorn.rb if
# you are running more than one master concurrently.
#
# If you call this script without any config parameters, it will attempt to run the
# init command for all your unicorn configurations listed in /etc/unicorn/*.conf
#
# /etc/init.d/unicorn start # starts all unicorns
#
# If you specify a particular config, it will only operate on that one
#
# /etc/init.d/unicorn start my_app
set -e
set -u
sig () {
test -s "$PID" && kill -$1 `cat "$PID"`
}
oldsig () {
test -s "$OLD_PID" && kill -$1 `cat "$OLD_PID"`
}
cmd () {
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
$CMD
echo "Starting"
;;
stop)
sig QUIT && echo "Stopping" && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && echo "Forcing a stop" && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig USR2 && sleep 5 && oldsig QUIT && echo "Killing old master" `cat $OLD_PID` && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
sig USR2 && echo Upgraded && exit 0
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
$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
}
setup () {
echo "$APP_ROOT: "
cd $APP_ROOT || exit 1
export APP_ROOT
export PID=$APP_ROOT/tmp/pids/unicorn.pid
export OLD_PID="$PID.oldbin"
export APP_ENV=${APP_ENV-production}
export UNICORN_CONFIG=${UNICORN_CONFIG-/etc/unicorn/unicorn.rb}
export UNICORN_WORKERS
export UNICORN_PORT
export UNICORN_USER
export UNICORN_GROUP
CMD="$UNICORN -c $UNICORN_CONFIG -D"
}
start_stop () {
# either run the start/stop/reload/etc command for every config under /etc/unicorn
# or just do it for a specific one
# $1 contains the start/stop/etc command
# $2 if it exists, should be the specific config we want to act on
if [ $# -gt 1 ]; then
. /etc/unicorn/sites-enabled/$2.conf
setup
cmd $1
else
for CONFIG in /etc/unicorn/sites-enabled/*.conf; do
# import the variables
. $CONFIG
setup
# run the start/stop/etc command
cmd $1
done
fi
}
if [ $# -gt 1 ]; then
ARGS="$1 $2"
else
ARGS="$1"
fi
start_stop $ARGS
@hasmanytrees
Copy link

Love the idea of treating unicorn much like nginx with the sites-available and sites-enabled directories and incorporating that into the init.d script. Very nice.

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