diff --git a/.env.example b/.env.example index 1637654..edaa6f6 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,9 @@ RAILS_ENV= APP_SECRET= -UNICORN_WORKERS=3 +UNICORN_USER=deploy +UNICORN_GROUP=deploy +UNICORN_WORKERS=4 UNICORN_PORT=5000 UNICORN_SOCKET=/tmp/unicorn.ensl.sock diff --git a/Capfile b/Capfile index e429c17..1626739 100644 --- a/Capfile +++ b/Capfile @@ -3,5 +3,6 @@ require 'capistrano/deploy' require 'capistrano/rbenv' require 'capistrano/bundler' require 'capistrano/rails' +require 'capistrano3/unicorn' Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } diff --git a/Gemfile b/Gemfile index aca9e16..02c5619 100644 --- a/Gemfile +++ b/Gemfile @@ -4,22 +4,20 @@ ruby '2.1.1' gem 'rails', '~> 3.2.17' gem 'mysql2', '~> 0.3.15' -gem 'foreman', '~> 0.63.0' # Libraries -gem 'jquery-rails' +gem 'jquery-rails', '~> 2.0.2' gem 'sass-rails' gem 'coffee-rails' gem 'gruff' gem 'nokogiri' -gem 'carrierwave' gem 'rbbcode' -gem 'tinymce-rails' +gem 'tinymce-rails', '~> 3.5.4.1' +gem 'carrierwave', '~> 0.10.0' gem 'bluecloth', '~> 2.2.0' -gem 'bb-ruby' -gem 'therubyracer' -gem 'acts_as_indexed' -gem 'rmagick', require: false +gem 'bb-ruby', '~> 1.0.4' +gem 'therubyracer', '~> 0.12.1' +gem 'rmagick', '~> 2.13.2', require: false gem 'will_paginate', git: 'https://github.com/p7r/will_paginate.git', branch: 'rails3' gem 'newrelic_rpm', '~> 3.7.2.195' @@ -32,6 +30,7 @@ group :development do gem 'capistrano-rbenv', '~> 2.0.2' gem 'capistrano-bundler', '~> 1.1.2' gem 'capistrano-rails', '~> 1.1' + gem 'capistrano3-unicorn', '~> 0.1.1' gem 'annotate', '~> 2.6.2' end diff --git a/Gemfile.lock b/Gemfile.lock index 67264cc..43083c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,12 +35,11 @@ GEM activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) - acts_as_indexed (0.7.8) annotate (2.6.2) activerecord (>= 2.3.0) rake (>= 0.8.7) arel (3.0.3) - bb-ruby (0.9.5) + bb-ruby (1.0.4) bluecloth (2.2.0) builder (3.0.4) capistrano (3.1.0) @@ -56,15 +55,19 @@ GEM capistrano-rbenv (2.0.2) capistrano (~> 3.1) sshkit (~> 1.3) + capistrano3-unicorn (0.1.1) + capistrano (>= 3.1.0) capybara (2.2.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - carrierwave (0.6.2) + carrierwave (0.10.0) activemodel (>= 3.2.0) activesupport (>= 3.2.0) + json (>= 1.7) + mime-types (>= 1.16) cliver (0.3.2) coderay (1.1.0) coffee-rails (3.2.2) @@ -94,13 +97,6 @@ GEM factory_girl_rails (4.4.1) factory_girl (~> 4.4.0) railties (>= 3.0.0) - foreman (0.63.0) - dotenv (>= 0.7) - thor (>= 0.13.6) - foreman (0.63.0-x86-mingw32) - dotenv (>= 0.7) - thor (>= 0.13.6) - win32console (~> 1.3.0) given_core (3.5.4) sorcerer (>= 0.3.7) gruff (0.3.6) @@ -173,7 +169,7 @@ GEM rdoc (3.12.2) json (~> 1.4) ref (1.0.5) - rmagick (2.13.1) + rmagick (2.13.2) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) @@ -245,23 +241,22 @@ PLATFORMS x86-mingw32 DEPENDENCIES - acts_as_indexed annotate (~> 2.6.2) - bb-ruby + bb-ruby (~> 1.0.4) bluecloth (~> 2.2.0) capistrano (~> 3.1.0) capistrano-bundler (~> 1.1.2) capistrano-rails (~> 1.1) capistrano-rbenv (~> 2.0.2) + capistrano3-unicorn (~> 0.1.1) capybara (~> 2.2.1) - carrierwave + carrierwave (~> 0.10.0) coffee-rails dalli (~> 2.7.0) dotenv-rails (~> 0.10.0) factory_girl_rails (~> 4.4.1) - foreman (~> 0.63.0) gruff - jquery-rails + jquery-rails (~> 2.0.2) kgio (~> 2.9.2) mysql2 (~> 0.3.15) newrelic_rpm (~> 3.7.2.195) @@ -270,13 +265,13 @@ DEPENDENCIES pry-debugger (~> 0.2.2) rails (~> 3.2.17) rbbcode - rmagick + rmagick (~> 2.13.2) rspec-given (~> 3.5.4) rspec-rails (~> 2.14.1) sass-rails simplecov (~> 0.7.1) - therubyracer - tinymce-rails + therubyracer (~> 0.12.1) + tinymce-rails (~> 3.5.4.1) uglifier (~> 2.5.0) unicorn (~> 4.8.2) will_paginate! diff --git a/INSTALL.md b/INSTALL.md index c2277fb..79fcdce 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,32 +1,27 @@ -# Ubuntu 13.10 x64 +# Production Install (Ubuntu 13.10 x64 or Debian 7) ## Capistrano setup -SSH as root, and install the basics +SSH as a user with sudo access, update package lists and upgrade packages (new install only) sudo apt-get update sudo apt-get upgrade -Create a deploy user. Disable password authentication and add it to the www-data group. +Either use an existing sandboxed user account, or create a deploy user and disable password authentication. Instead, authenticate with a keypair. sudo adduser deploy sudo passwd -l deploy - sudo usermod -a -G www-data deploy -Create a new upstart config and set permissions +## Install Apache - touch /etc/init/ensl.conf - chown deploy /etc/init/ensl.conf + sudo apt-get install apache2 apache2-mpm-prefork -Add the following to `/etc/sudoers` to allow the `deploy` user to manage nginx, rbenv and upstart commands via sudo without a password - - # /etc/sudoers - deploy ALL=NOPASSWD:/etc/init.d/nginx - deploy ALL=NOPASSWD:/home/deploy/.rbenv/bin/rbenv - deploy ALL=NOPASSWD:/usr/sbin/service ensl start, /usr/sbin/service ensl stop, /usr/sbin/service ensl restart +Now create the required directories, e.g. `/var/www/virtual/ensl.org/deploy` ## Install MySQL & Memcached +You may need to re-configure MySQL. Use `sudo dpkg-reconfigure mysql-server-5.5` + sudo apt-get install mysql-server mysql-client libmysqlclient-dev memcached Login to mysql as root, and create the database and user account: @@ -37,23 +32,20 @@ Login to mysql as root, and create the database and user account: ## Install rbenv, ruby, bundler and Image Magick -As root, install dependencies +As a user with sudo, install dependencies - sudo apt-get install nginx git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libmysql-ruby imagemagick libmagickwand-dev + sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev imagemagick libmagickwand-dev Switch user to deploy, and install rbenv su deploy cd ~ git clone git://github.com/sstephenson/rbenv.git .rbenv - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.profile echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc - echo 'eval "$(rbenv init -)"' >> ~/.profile echo 'eval "$(rbenv init -)"' >> ~/.bashrc exec $SHELL git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build - echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.profile echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc exec $SHELL @@ -63,15 +55,14 @@ Switch user to deploy, and install rbenv echo "gem: --no-ri --no-rdoc" > ~/.gemrc gem install bundler -Install the rbenv-sudo plugin - - mkdir ~/.rbenv/plugins - git clone git://github.com/dcarley/rbenv-sudo.git ~/.rbenv/plugins/rbenv-sudo - ## Install the ENSL site Create the `.env` file with the appropriate credentials. - mkdir /var/www/virtual/ensl.org/deploy - mkdir /var/www/virtual/ensl.org/deploy/shared - touch /var/www/virtual/ensl.org/deploy/shared/.env \ No newline at end of file + touch /var/www/virtual/ensl.org/deploy/shared/.env + +# Deployment + +Use capistrano to deploy: + + bundle exec cap production deploy \ No newline at end of file diff --git a/Procfile b/Procfile deleted file mode 100644 index 08b255c..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: bundle exec unicorn -c ./config/unicorn.rb \ No newline at end of file diff --git a/config/deploy.rb b/config/deploy.rb index 35824c9..898dc3d 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -3,6 +3,7 @@ lock '3.1.0' set :application, 'ensl' set :deploy_user, 'deploy' set :deploy_to, '/var/www/virtual/ensl.org/deploy' +set :deploy_via, :remote_cache set :pty, true set :scm, :git @@ -11,14 +12,18 @@ set :keep_releases, 10 set :rbenv_type, :user set :rbenv_ruby, '2.1.1' -set :rbenv_sudo, "sudo /home/#{fetch(:deploy_user)}/.rbenv/bin/rbenv sudo" +set :unicorn_config_path, "config/unicorn.rb" +set :unicorn_rack_env, fetch(:rails_env) + +set :writable_dirs, %w{public tmp} set :linked_files, %w{.env} set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system public/local public/uploads} -set :writable_dirs, %w{public tmp} -set :normalize_asset_timestamps, %{public/images public/javascripts public/stylesheets} +set :normalize_asset_timestamps, %{public/images + public/javascripts + public/stylesheets} namespace :deploy do namespace :check do @@ -38,43 +43,9 @@ namespace :deploy do end end - desc 'Restart application' task :restart do - invoke 'foreman:export' - invoke 'foreman:restart' + invoke 'unicorn:restart' end after :publishing, :restart -end - -namespace :foreman do - desc "Export the Procfile to Ubuntu's upstart scripts" - task :export do - on roles(:app) do |host| - within release_path do - execute fetch(:rbenv_sudo), "bundle exec foreman export upstart /etc/init -a #{fetch(:application)} -u #{fetch(:deploy_user)} -l #{fetch(:deploy_to)}/shared/log" - end - end - end - - desc "Start the application services" - task :start do - on roles(:app) do |host| - execute "sudo service #{fetch(:application)} start" - end - end - - desc "Stop the application services" - task :stop do - on roles(:app) do |host| - execute "sudo service #{fetch(:application)} stop" - end - end - - desc "Restart the application services" - task :restart do - on roles(:app) do |host| - execute "sudo service #{fetch(:application)} start || #{sudo} service #{fetch(:application)} restart" - end - end end \ No newline at end of file diff --git a/config/unicorn.rb b/config/unicorn.rb index 0e07129..75b6eea 100644 --- a/config/unicorn.rb +++ b/config/unicorn.rb @@ -1,25 +1,33 @@ -worker_processes Integer(ENV['UNICORN_WORKERS'] || 3) +base_path = "/var/www/virtual/ensl.org/deploy" +current_path = "#{base_path}/current" +shared_path = "#{base_path}/shared" + +working_directory current_path +worker_processes Integer(ENV['UNICORN_WORKERS'] || 4) timeout 30 preload_app true -listen ENV['UNICORN_PORT'] -listen ENV['UNICORN_SOCKET'] - +user ENV['UNICORN_USER'], ENV['UNICORN_GROUP'] +listen Integer(ENV['UNICORN_PORT'] || 4000), :tcp_nopush => true +listen ENV['UNICORN_SOCKET'], :backlog => 64 + +stderr_path "#{shared_path}/log/unicorn.stderr.log" +stdout_path "#{shared_path}/log/unicorn.stdout.log" +pid "#{shared_path}/tmp/pids/unicorn.pid" + before_fork do |server, worker| - Signal.trap 'TERM' do - puts 'Unicorn master intercepting TERM and sending myself QUIT instead' - Process.kill 'QUIT', Process.pid + ActiveRecord::Base.connection.disconnect! + + old_pid = "#{server.config[: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 + end end - - defined?(ActiveRecord::Base) and - ActiveRecord::Base.connection.disconnect! end - + after_fork do |server, worker| - Signal.trap 'TERM' do - puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' - end - - defined?(ActiveRecord::Base) and - ActiveRecord::Base.establish_connection -end + ActiveRecord::Base.establish_connection +end \ No newline at end of file