Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to send a notification when a Unicorn master finishes a restart?

I'm running a series of Rails/Sinatra apps behind nginx + unicorn, with zero-downtime deploys. I love this setup, but it takes a while for Unicorn to finish restarting, so I'd like to send some sort of notification when it finishes.

The only callbacks I can find in Unicorn docs are related to worker forking, but I don't think those will work for this.

Here's what I'm looking for from the bounty: the old unicorn master starts the new master, which then starts its workers, and then the old master stops its workers and lets the new master take over. I want to execute some ruby code when that handover completes.

Ideally I don't want to implement any complicated process monitoring in order to do this. If that's the only way, so be it. But I'm looking for easier options before going that route.

like image 589
Adam Lassek Avatar asked Nov 06 '13 23:11

Adam Lassek


1 Answers

I've built this before, but it's not entirely simple.

The first step is to add an API that returns the git SHA of the current revision of code deployed. For example, you deploy AAAA. Now you deploy BBBB and that will be returned. For example, let's assume you added the api "/checks/version" that returns the SHA.

Here's a sample Rails controller to implement this API. It assumes capistrano REVISION file is present, and reads current release SHA into memory at app load time:

class ChecksController
  VERSION = File.read(File.join(Rails.root, 'REVISION')) rescue 'UNKNOWN'

  def version
    render(:text => VERSION)
  end
end

You can then poll the local unicorn for the SHA via your API and wait for it to change to the new release.

Here's an example using Capistrano, that compares the running app version SHA to the newly deployed app version SHA:

namespace :deploy do
  desc "Compare running app version to deployed app version"
  task :check_release_version, :roles => :app, :except => { :no_release => true } do
    timeout_at = Time.now + 60
    while( Time.now < timeout_at) do
      expected_version = capture("cat /data/server/current/REVISION")
      running_version = capture("curl -f http://localhost:8080/checks/version; exit 0")

      if expected_version.strip == running_version.strip
        puts "deploy:check_release_version: OK"
        break
      else
        puts "=[WARNING]==========================================================="
        puts "= Stale Code Version"
        puts "=[Expected]=========================================================="
        puts expected_version
        puts "=[Running]==========================================================="
        puts running_version
        puts "====================================================================="
        Kernel.sleep(10)
      end
    end
  end
end

You will want to tune the timeouts/retries on the polling to match your average app startup time. This example assumes a capistrano structure, with app in /data/server/current and a local unicorn on port 8080.

like image 80
Winfield Avatar answered Oct 02 '22 03:10

Winfield