Merge pull request #46 from warpc/1-bluepill

1 bluepill

  *  Apply bluepill configuration, write capistrano recipe for it
  *  Change deploy logic to integrate with bluepill
  *  Refactor unicorn config, run it through unixsocket
  *  Cleanup depoy
  *  Refactor mount_downloads script
  *  Write autostart script. Launch from rosa user
  *  Remove old scripts
  *  Refactor platform mount/umount methods
  *  Debug
This commit is contained in:
George Vinogradov 2011-12-14 14:29:28 -08:00
commit 97c43b74e3
18 changed files with 115 additions and 70 deletions

View File

@ -40,6 +40,7 @@ gem 'unicorn', '~> 4.1.1'
group :production do
gem "airbrake", '~> 3.0.5'
# gem 'newrelic_rpm', '~> 3.1.1'
gem 'bluepill', :require => false
end
group :development do

View File

@ -41,6 +41,11 @@ GEM
activerecord (>= 2.2.2)
arel (2.0.10)
bcrypt-ruby (3.0.1)
bluepill (0.0.51)
activesupport (>= 3.0.0)
daemons (~> 1.1.0)
i18n (>= 0.5.0)
state_machine (~> 0.9.4)
builder (2.1.2)
cancan (1.6.7)
cape (1.0.1)
@ -180,6 +185,7 @@ GEM
i18n (>= 0.5.0)
sexp_processor (3.0.8)
silent-postgres (0.1.1)
state_machine (0.9.4)
thor (0.14.6)
treetop (1.4.10)
polyglot
@ -204,6 +210,7 @@ PLATFORMS
DEPENDENCIES
airbrake (~> 3.0.5)
ancestry (~> 1.2.4)
bluepill
cancan (~> 1.6.7)
cape
capistrano

View File

@ -61,7 +61,6 @@ class PlatformsController < ApplicationController
@platform.owner = @admin_id.blank? ? get_owner : User.find(@admin_id)
if @platform.save
#@platform.make_admin_relation(params[:admin_id])
flash[:notice] = I18n.t("flash.platform.saved")
redirect_to @platform
else
@ -73,15 +72,12 @@ class PlatformsController < ApplicationController
def update
@admin_id = params[:admin_id]
@admin_uname = params[:admin_uname]
#@platform.owner = @admin_id.blank? ? get_owner : User.find(@admin_id)
#if @platform.save!
if @platform.update_attributes(
:owner => @admin_id.blank? ? get_owner : User.find(@admin_id),
:description => params[:platform][:description],
:released => params[:platform][:released]
)
#@platform.make_admin_relation(@admin_id)
flash[:notice] = I18n.t("flash.platform.saved")
redirect_to @platform
else

View File

@ -63,6 +63,10 @@ class Platform < ActiveRecord::Base
build_path(name)
end
def mount_path
Rails.root.join("public", "downloads", name)
end
def hidden?
visibility == 'hidden'
end
@ -109,33 +113,22 @@ class Platform < ActiveRecord::Base
end
def mount_directory_for_rsync
#FileUtils.rm_rf "#{ Rails.root.join('tmp', 'umount', self.name) }" if File.exist? "#{ Rails.root.join('tmp', 'umount', name) }"
#FileUtils.mkdir_p "#{ Rails.root.join('tmp', 'mount', name) }"
system("sudo mkdir -p #{ Rails.root.join("public", "downloads") }/#{ name }")
system("sudo mount --bind /home/share/platforms/#{ name } #{ Rails.root.join("public", "downloads") }/#{ name }")
#system("sudo cp -f /srv/rosa_build/current/tmp/mount/#{ name }/* /home/share/platforms/#{ name }/repository/")
#system("sudo rm -Rf \"/srv/rosa_build/current/tmp/mount/#{ name }\"")
# umount_directory_for_rsync # TODO ignore errors
system("sudo mkdir -p #{mount_path}")
system("sudo mount --bind #{path} #{mount_path}")
Arch.all.each do |arch|
host = EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host]
url = "http://#{host}/downloads/#{name}/repository/"
str = "country=Russian Federation,city=Moscow,latitude=52.18,longitude=48.88,bw=1GB,version=2011,arch=#{arch.name},type=distrib,url=#{url}\n"
File.open(Rails.root.join("public", 'downloads', name, "#{name}.#{arch.name}.list"), 'w') {|f| f.write(str) }
File.open(File.join(mount_path, "#{name}.#{arch.name}.list"), 'w') {|f| f.write(str) }
end
end
def umount_directory_for_rsync
system("sudo umount #{ Rails.root.join("public", "downloads") }/#{ name }")
system("sudo rm -Rf #{ Rails.root.join("public", "downloads") }/#{ name }")
#system("rm -Rf \"/srv/rosa_build/current/tmp/umount/#{ name }\"")
#FileUtils.rm_rf "#{ Rails.root.join('tmp', 'mount', name) }" if File.exist? "#{ Rails.root.join('tmp', 'mount', name) }"
#FileUtils.mkdir_p "#{ Rails.root.join('tmp', 'umount', name) }"
system("sudo umount #{mount_path}")
system("sudo rm -Rf #{mount_path}")
end
#def make_admin_relation(user_id)
# r = self.relations.build :object_id => user_id, :object_type => 'User', :role => 'admin'
# r.save
#end
def update_owner_relation
if owner_id_was != owner_id
r = relations.where(:object_id => owner_id_was, :object_type => owner_type_was)[0]

8
bin/autostart.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
for f in `ls /srv`
do
su -l rosa -c "cd /srv/$f/current && APP_NAME=$f bundle exec bluepill --no-privileged load /srv/$f/current/config/production.pill"
done
/srv/rosa_build/current/bin/mount_downloads.sh

View File

@ -2,8 +2,9 @@
for f in `ls /srv/rosa_build/shared/downloads`
do
if [ -d "/home/share/platforms/$f" ]
if [ -d /home/share/platforms/$f ]
then
mount --bind "/home/share/platforms/$f" "/srv/rosa_build/shared/downloads/$f"
sudo umount /srv/rosa_build/shared/downloads/$f 2>&1 >> /dev/null
sudo mount --bind /home/share/platforms/$f /srv/rosa_build/shared/downloads/$f
fi
done

View File

@ -1,9 +0,0 @@
#!/bin/bash
for f in `ls /srv/rosa_build/current/tmp/mount`
do
mkdir -p "/srv/rosa_build/shared/downloads/$f"
mount --bind "/home/share/platforms/$f" "/srv/rosa_build/shared/downloads/$f"
cp -f /srv/rosa_build/current/tmp/mount/$f/* /home/share/platforms/$f/repository/
rm -Rf "/srv/rosa_build/current/tmp/mount/$f"
done

View File

@ -1,6 +0,0 @@
#!/bin/bash
mv /srv/rosa_build/shared/log/nginx.access.log /srv/rosa_build/shared/log/nginx.access.log.0
/etc/init.d/nginx reload
chown rosa /srv/rosa_build/shared/log/nginx.access.log.0
# touch /home/rosa/i_was_launched.txt

View File

@ -1,8 +0,0 @@
#!/bin/bash
for f in `ls /srv/rosa_build/current/tmp/umount`
do
umount "/srv/rosa_build/shared/downloads/$f"
rm -Rf "/srv/rosa_build/shared/downloads/$f"
rm -Rf "/srv/rosa_build/current/tmp/umount/$f"
done

View File

@ -29,6 +29,7 @@ set :scm, "git"
require 'lib/recipes/nginx'
require 'lib/recipes/unicorn'
require 'lib/recipes/bluepill'
namespace :deploy do
# task :restart, :roles => :app, :except => { :no_release => true } do
# run "touch #{current_release}/tmp/restart.txt"
@ -50,17 +51,11 @@ namespace :deploy do
run "mkdir -p #{fetch :shared_path}/downloads"
run "ln -nfs #{fetch :shared_path}/downloads/ #{fetch :release_path}/public/downloads"
run "mkdir -p #{fetch :shared_path}/tmp"
run "ln -nfs #{fetch :shared_path}/pids/ #{fetch :shared_path}/tmp/pids"
run "ln -nfs #{fetch :shared_path}/tmp/ #{fetch :release_path}/tmp"
run "mkdir -p #{fetch :release_path}/tmp/mount"
run "mkdir -p #{fetch :release_path}/tmp/umount"
end
end
after "deploy:update_code", "deploy:symlink_all", "deploy:migrate"
after "deploy:restart", "delayed_job:restart", "deploy:cleanup"
after "deploy:restart", "delayed_job:restart", "bluepill:restart", "deploy:cleanup"
require 'cape'
namespace :rake_tasks do

View File

@ -10,6 +10,6 @@ role :db, domain, :primary => true
set :application, "rosa_build_#{stage}"
set :deploy_to, "/srv/#{application}"
set :unicorn_port, 8081
# set :unicorn_port, 8081
before "deploy:restart", "deploy:stub_xml_rpc"

View File

@ -10,6 +10,6 @@ role :db, domain, :primary => true
set :application, "rosa_build_#{stage}"
set :deploy_to, "/srv/#{application}"
set :unicorn_port, 8082
# set :unicorn_port, 8082
before "deploy:restart", "deploy:stub_xml_rpc"

34
config/production.pill Normal file
View File

@ -0,0 +1,34 @@
#! /usr/bin/env ruby
app_name = ENV['APP_NAME'] || 'rosa_build'
Bluepill.application(app_name) do |app|
app.uid = app.gid = 'rosa'
app.working_dir = "/srv/#{app_name}/current"
app.process("delayed_job") do |process|
process.start_grace_time = 10.seconds
process.stop_grace_time = 10.seconds
process.restart_grace_time = 10.seconds
process.start_command = "/usr/bin/env RAILS_ENV=production script/delayed_job start"
process.stop_command = "/usr/bin/env RAILS_ENV=production script/delayed_job stop"
process.pid_file = File.join(app.working_dir, 'tmp', 'pids', 'delayed_job.pid')
end
app.process("unicorn") do |process|
process.start_grace_time = 8.seconds
process.stop_grace_time = 5.seconds
process.restart_grace_time = 13.seconds
process.start_command = "bundle exec unicorn -l /tmp/#{app_name}_unicorn.sock -E production -c config/unicorn.rb -D"
process.stop_command = "kill -QUIT {{PID}}"
process.restart_command = "kill -USR2 {{PID}}"
process.pid_file = File.join(app.working_dir, 'tmp', 'pids', 'unicorn.pid')
process.monitor_children do |child_process|
child_process.stop_command = "kill -QUIT {{PID}}"
child_process.checks :mem_usage, :every => 10.seconds, :below => 150.megabytes, :times => [3,4], :fires => :stop
child_process.checks :cpu_usage, :every => 10.seconds, :below => 20, :times => [3,4], :fires => :stop
end
end
end

View File

@ -1,11 +1,10 @@
base_path = File.expand_path(File.join File.dirname(__FILE__), '..')
rails_env = ENV['RAILS_ENV'] || 'production'
worker_processes 4
working_directory base_path # available in 0.94.0+
preload_app true
working_directory File.expand_path(File.join(File.dirname(__FILE__), "..")) # available in 0.94.0+
# listen '/tmp/rosa_build.sock', :backlog => 2048
# listen File.join(base_path, 'tmp', 'pids', 'unicorn.sock')
# listen "/tmp/.sock", :backlog => 64
# listen 8080, :tcp_nopush => true
@ -13,7 +12,7 @@ working_directory File.expand_path(File.join(File.dirname(__FILE__), "..")) # a
timeout 600
# feel free to point this anywhere accessible on the filesystem
pid File.expand_path(File.join(File.dirname(__FILE__), "..")) + '/tmp/pids/unicorn.pid'
pid File.join(base_path, 'tmp', 'pids', 'unicorn.pid')
# REE
# http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
@ -24,8 +23,8 @@ end
# 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 File.expand_path(File.join(File.dirname(__FILE__), "..")) + "/log/unicorn.stderr.log"
stdout_path File.expand_path(File.join(File.dirname(__FILE__), "..")) + "/log/unicorn.stdout.log"
stderr_path File.join(base_path, 'log', 'unicorn.stderr.log')
stdout_path File.join(base_path, 'log', 'unicorn.stdout.log')
# combine REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
@ -42,7 +41,7 @@ before_fork do |server, worker|
# we send it a QUIT.
#
# Using this method we get 0 downtime deploys.
old_pid = File.expand_path(File.join(File.dirname(__FILE__), "..")) + '/tmp/pids/unicorn.pid.oldbin'
old_pid = File.join(base_path, 'tmp', 'pids', '/unicorn.pid.oldbin')
if File.exists?(old_pid) && server.pid != old_pid
begin
Process.kill("QUIT", File.read(old_pid).to_i)

View File

@ -1,3 +1,8 @@
=== Basic bootstrap
* rake db:drop db:setup
==
=== Setup autostart
Add to /etc/rc.d/rc.local following /srv/rosa_build/current/bin/autostart.sh
==

30
lib/recipes/bluepill.rb Normal file
View File

@ -0,0 +1,30 @@
Capistrano::Configuration.instance(:must_exist).load do
namespace :bluepill do
set(:bluepill_binary) {"bundle exec bluepill --no-privileged"}
desc "Load bluepill configuration and start it"
task :start, :roles => [:app] do
run "cd #{fetch :current_path} && #{try_sudo} APP_NAME=#{fetch :application} #{bluepill_binary} load config/production.pill"
end
desc "Stop processes that bluepill is monitoring"
task :stop, :roles => [:app], :on_error => :continue do
run "cd #{fetch :current_path} && #{try_sudo} #{bluepill_binary} #{fetch :application} stop"
end
task :restart, :roles => [:app] do
# run "cd #{fetch :current_path} && #{try_sudo} #{bluepill_binary} #{fetch :application} restart"
stop; quit; start
end
desc "Stop processes that bluepill is monitoring and quit bluepill"
task :quit, :roles => [:app] do
run "cd #{fetch :current_path} && #{try_sudo} #{bluepill_binary} #{fetch :application} quit"
end
desc "Prints bluepills monitored processes statuses"
task :status, :roles => [:app] do
run "cd #{fetch :current_path} && #{try_sudo} #{bluepill_binary} #{fetch :application} status"
end
end
end

View File

@ -8,7 +8,8 @@ Capistrano::Configuration.instance(:must_exist).load do
task :generate_configuration, :roles => :web, :except => { :no_release => true } do
config = %Q{
upstream #{application}_backend {
server 127.0.0.1:#{unicorn_port};
# server 127.0.0.1:#{unicorn_port rescue 8080};
server unix:/tmp/#{fetch :application}_unicorn.sock;
}
server {

View File

@ -1,12 +1,11 @@
Capistrano::Configuration.instance(:must_exist).load do
namespace :deploy do
set :unicorn_binary, "bundle exec unicorn"
set(:unicorn_config) { "#{fetch :current_path}/config/unicorn.rb" }
set(:unicorn_pid) { "#{fetch :shared_path}/tmp/pids/unicorn.pid" }
set :unicorn_port, 8080
# set :unicorn_port, 8080
task :start, :roles => :app, :except => { :no_release => true } do
run "cd #{fetch :current_path} && #{try_sudo} #{unicorn_binary} -c #{unicorn_config} -p #{unicorn_port} -E #{rails_env} -D"
run "cd #{fetch :current_path} && #{try_sudo} #{unicorn_binary} -l /tmp/#{fetch :application}_unicorn.sock -E #{rails_env} -c config/unicorn.rb -D" # -p #{unicorn_port}
end
task :stop, :roles => :app, :except => { :no_release => true } do
run "#{try_sudo} kill `cat #{unicorn_pid}`" rescue warn 'deploy:stop FAILED'
@ -18,9 +17,8 @@ Capistrano::Configuration.instance(:must_exist).load do
run "#{try_sudo} kill -s USR2 `cat #{unicorn_pid}`" rescue warn 'deploy:reload FAILED'
end
task :restart, :roles => :app, :except => { :no_release => true } do
# reload
stop
start
start # blue pill will do it?
end
end
end