Question

Zero-downtime Unicorn deploy

  • Posted on December 18, 2014
  • jlecourAsked by jlecour

Hi

I’m trying to use the Ruby on Rails with Ubuntu image. It has an init script to manage the Unicorn process, but it doesn’t do zero-downtime deploy.

I’m not familiar enough with init scripts, so I can’t do this myself.

Here is a different flavour of init script that knows how to do this : https://github.com/ValencePM/capistrano-unicorn-init

Here is the official documentation for this feature of Unicorn : http://unicorn.bogomips.org/SIGNALS.html (check the “Procedure to replace a running unicorn executable” paragraph)

Thanks


Submit an answer

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Sign In or Sign Up to Answer

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in Q&A, subscribe to topics of interest, and get courses and tools that will help you grow as a developer and scale your project or business.

Thanks @asb, I’ll try this.

I see that this script is autonomous ; it sends the USR2 signal, sleeps (3 seconds should be enough for most of web requests to finish), then sends a QUIT signal to the old master.

There another approach where Unicorn is responsible for killing an eventual old master when forking workers.

before_fork do |server, worker|
  ##
  # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
  # immediately start loading up a new version of itself (loaded with a new
  # version of our app). When this new Unicorn is completely loaded
  # it will begin spawning workers. The first worker spawned will check to
  # see if an .oldbin pidfile exists. If so, this means we've just booted up
  # a new Unicorn and need to tell the old one that it can now die. To do so
  # we send it a QUIT.
  #
  # Using this method we get 0 downtime deploys.

  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
      # someone else did our job for us
    end
  end
end

This snippet is considered common use by a lot of Unicorn users. It is even in the examples shipped with Unicorn source code.

I guess it might be possible to have an init script that starts by sending the USR2 signal, sleeps (maybe a bit longer), then checks if the reloading has properly occurred. Right now, I guess it fails (returning a 1 code) if the old PID is missing (removed by Unicorn itself within the sleep time.

What do you think?

To do a “zero-downtime deploy” or “graceful reload” by sending the USR2 signal, you can add a new case to the Unicorn init script in /etc/init.d/unicorn

  upgrade)
        log_daemon_msg "Upgrading $DESC" $NAME || true
        if start-stop-daemon --stop --signal USR2 --quiet --oknodo --pidfile $PID; then
          sleep 3
          if start-stop-daemon --stop --signal QUIT --quiet --oknodo --pidfile $PID.oldbin; then
              log_end_msg 0 || true
          else
              log_end_msg 1 || true
          fi
        else
          log_end_msg 1 || true
        fi
        ;;

This sends the signal and then reaps the old processes when you run service unicorn upgrade

Give it and try and let us know how it goes!