rubynginxunicorncapistrano3

Starting or restarting Unicorn with Capistrano 3.x


I'm trying to start or restart Unicorn when I do cap production deploy with Capistrano 3.0.1. I have some examples that I got working with Capistrano 2.x using something like:

namespace :unicorn do
  desc "Start unicorn for this application"
  task :start do
    run "cd #{current_path} && bundle exec unicorn -c /etc/unicorn/myapp.conf.rb -D"
  end
end

But when I try and use run in the deploy.rb for Capistrano 3.x I get an undefined method error.

Here are a couple of the things I tried:

# within the :deploy I created a task that I called after :finished
namespace :deploy do
...

  task :unicorn do
    run "cd #{current_path} && bundle exec unicorn -c /etc/unicorn/myapp.conf.rb -D"
  end

  after :finished, 'deploy:unicorn'

end

I have also tried putting the run within the :restart task

namespace :deploy do
  desc 'Restart application'
  task :restart do

  on roles(:app), in: :sequence, wait: 5 do
    # Your restart mechanism here, for example:
    # execute :touch, release_path.join('tmp/restart.txt')
    execute :run, "cd #{current_path} && bundle exec unicorn -c /etc/unicorn/deployrails.conf.rb -D"
  end
end    

If I use just run "cd ... " then I'll get awrong number of arguments (1 for 0)` in the local shell.

I can start the unicorn process with unicorn -c /etc/unicorn/deployrails.conf.rb -D from my ssh'd VM shell.

I can kill the master Unicorn process from the VM shell using kill USR2, but even though the process is killed I get an error. I can then start the process again using unicorn -c ...

$ kill USR2 58798
bash: kill: USR2: arguments must be process or job IDs

I'm very new to Ruby, Rails and Deployment in general. I have a VirtualBox setup with Ubuntu, Nginx, RVM and Unicorn, I'm pretty excited so far, but this one is really messing with me, any advice or insight is appreciated.


Solution

  • Can't say anything specific about capistrano 3(i use 2), but i think this may help: How to run shell commands on server in Capistrano v3?. Also i can share some unicorn-related experience, hope this helps.

    I assume you want 24/7 graceful restart approach.

    Let's consult unicorn documentation for this matter. For graceful restart(without downtime) you can use two strategies:

    1. kill -HUP unicorn_master_pid It requires your app to have 'preload_app' directive disabled, increasing starting time of every one of unicorn workers. If you can live with that - go on, it's your call.

    2. kill -USR2 unicorn_master_pid kill -QUIT unicorn_master_pid

    More sophisticated approach, when you're already dealing with performance concerns. Basically it will reexecute unicorn master process, then you should kill it's predecessor. Theoretically you can deal with usr2-sleep-quit approach. Another(and the right one, i may say) way is to use unicorn before_fork hook, it will be executed, when new master process will be spawned and will try to for new children for itself. You can put something like this in config/unicorn.rb:

    # Where to drop a pidfile
    pid project_home + '/tmp/pids/unicorn.pid'
    
    before_fork do |server, worker|
      server.logger.info("worker=#{worker.nr} spawning in #{Dir.pwd}")
    
      # graceful shutdown.
      old_pid_file = project_home + '/tmp/pids/unicorn.pid.oldbin'
      if File.exists?(old_pid_file) && server.pid != old_pid_file
        begin
          old_pid = File.read(old_pid_file).to_i
          server.logger.info("sending QUIT to #{old_pid}")
          # we're killing old unicorn master right there
          Process.kill("QUIT", old_pid)
        rescue Errno::ENOENT, Errno::ESRCH
          # someone else did our job for us
        end
      end
    end
    

    It's more or less safe to kill old unicorn when the new one is ready to fork workers. You won't get any downtime that way and old unicorn will wait for it's workers to finish.

    And one more thing - you may want to put it under runit or init supervision. That way your capistrano tasks will be as simple as sv reload unicorn, restart unicorn or /etc/init.d/unicorn restart. This is good thing.