How to restart God when you deploy a new release via Capistrano

February 7th, 2011 at 1:37 pm • permalink14 comments

I don’t have time to read and I want to jump immediately to the solution →.

Like the 90% of Rails developers, I use Capistrano to manage the deployment of my Rails applications. I also use God to monitor several application processes, such as DelayedJob workers.

The problem

Each time I execute a deployment, I need the restart all the Rails instances, including rake tasks and workers, otherwise they will continue to run with the previous (old) application version.

I cannot simply send a command to God via Capistrano because God is omnipotent and runs with root privileges, while Capistrano doesn’t (for security reasons I don’t deploy with a sudoer user).

Here comes the problem. How to restart God when you deploy a new release? I already discussed this problem in the past and the solution I adopted since today was to execute a command as root without using sudo. The problem with this solution, is that it requires you to type the root password each time you execute a new deploy. And this is extremely awkward.

Last week I was configuring a new Rails application and I decided to investigate a new solution. I really like the way how Phusion Passenger works: whenever you touch the /tmp/restart.txt file, Passenger restarts all the Rails processes.

Why not implementing the same behavior in God? Luckily, someone else already had the same idea before.

The solution

I found the following recipe on Gist.

module God
  module Conditions
    class RestartFileTouched < PollCondition
      attr_accessor :restart_file
      def initialize
        super
      end

      def process_start_time
        Time.parse(`ps -o lstart  -p #{self.watch.pid} --no-heading`)
      end

      def restart_file_modification_time
        File.mtime(self.restart_file)
      end

      def valid?
        valid = true
        valid &= complain("Attribute 'restart_file' must be specified", self) if self.restart_file.nil?
        valid
      end

      def test
        process_start_time < restart_file_modification_time
      end
    end
  end
end

Save the code in your God application or on the server and make sure God loads it. My suggestion is to add the following line in the God configuration script (e.g. /etc/god/config)

# Load in all God shared configs.
# Share configs apply to all instances.
God.load "/etc/god/conf.d/*.god"

and save the file in the /etc/god/conf.d folder. In this way, when you’ll start God with

$ god -c /etc/god/config

all the shared recipes will be loaded.

Then, configure the monitored process to watch the restart file. The configuration is available as a state transition condition.

rails_root = "/path/to/rails"

restart.condition(:restart_file_touched) do |c|
  c.interval = 5.seconds
  c.restart_file = File.join(rails_root, 'tmp', 'restart.txt')
end

Taking the GitHub DelayedJob recipe as example, you should change

# restart if memory gets too high
w.transition(:up, :restart) do |on|
  on.condition(:memory_usage) do |c|
    c.above = 300.megabytes
    c.times = 2
  end
end

to

# restart if memory gets too high
w.transition(:up, :restart) do |on|
  on.condition(:memory_usage) do |c|
    c.above = 300.megabytes
    c.times = 2
  end

  on.condition(:restart_file_touched) do |c|
    c.interval = 5.seconds
    c.restart_file = File.join(rails_root, 'tmp', 'restart.txt')
  end
end

The restart_file can be whichever file you want. I strongly encourage to use the same /tmp/restart.txt Passenger file, in this way your processes will restart each time you restart the application.

Thanks to Amedeo for his help and thanks to Nathan Humbert for creating the original God configuration.

Filed in Programming • Tags: , , , , ,

Comments

iain says:

On which OS have you tested this? With me: on OSX it will error out constantly and on debian it constantly restarts.

I tested it with Ubuntu and OpenSUSE, and it works in both cases.

D [2011-02-09 11:30:03] DEBUG: xxx-dj-1 RestartFileTouched [false] {true=>:restart}
D [2011-02-09 11:30:03] DEBUG: driver schedule # in 5 seconds
I [2011-02-09 11:30:06]  INFO: xxx-dj-0 [ok] (RestartFileTouched)
D [2011-02-09 11:30:06] DEBUG: xxx-dj-0 RestartFileTouched [false] {true=>:restart}
D [2011-02-09 11:30:06] DEBUG: driver schedule # in 5 seconds
I [2011-02-09 11:30:07]  INFO: xxx-emails-0 [ok] (RestartFileTouched)
D [2011-02-09 11:30:07] DEBUG: xxx-emails-0 RestartFileTouched [false] {true=>:restart}
D [2011-02-09 11:30:07] DEBUG: driver schedule # in 5 seconds
I [2011-02-09 11:30:08]  INFO: xxx-dj-1 [trigger] (RestartFileTouched)
D [2011-02-09 11:30:08] DEBUG: xxx-dj-1 RestartFileTouched [true] {true=>:restart}
I [2011-02-09 11:30:08]  INFO: xxx-dj-1 move 'up' to 'restart'
I [2011-02-09 11:30:08]  INFO: xxx-dj-1 stop: default lambda killer
I [2011-02-09 11:30:08]  INFO: xxx-dj-1 sent SIGTERM
I [2011-02-09 11:30:10]  INFO: xxx-dj-1 process stopped
I [2011-02-09 11:30:10]  INFO: xxx-dj-1 start: rake -f /var/www/apps/xxx/current/Rakefile jobs:work RAILS_ENV=production
iain says:

Wait, I’m probably wrong: “INFO: unicorn [ok] (RestartFileTouched)” means that it’s doesn’t need to restart. Double negations…

Jan-Willem says:

You also could just add a single command to execute with root rights within the /etc/sudoers file like this:

your_normal_user ALL = (root) /usr/sbin/apache2ctl, /etc/init.d/memcached, /usr/bin/gem, /etc/init.d/god, /usr/bin/god

as I currently configured my server… spare you a lot of hassle

I don’t want the deployment user to have root privileges for security reason.

xv22 says:

Tried this and had many problems with it, especially because the format of “ps” arguments didn’t work for FreeBSD. Also, I think it’s not the cleanest way to do this. I prefer using sudo to allow the deploying user to “god restart”. Then all you need to do is adding

namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
sudo “god restart YOUR-PROJECT”
end
end

to config/deploy.rb

/usr/local/etc/sudoers:

deployinguser ALL=(root) NOPASSWD: /usr/local/bin/god restart THEPROJECT

seanthingee says:

For FreeBSD, change to:

Time.parse(`ps -o lstart=”" -p #{self.watch.pid}`)

May work on other platforms too, but untested. Hint was found at: http://code.google.com/p/xinc/issues/detail?id=185

Duke Dorje says:

Awesome! Thank you for this. I knew about the sudo privilege thing, but this is way cleaner since I will be deploying multiple apps for different developers I’m working with onto the same server. I really like this technique and it gives me one less thing to configure every time I create a new application. Thanks!

Duke Dorje says:

Actually, that is not working for me at all. I discovered this is because I am using it for unicorn restarts. Unicorn doesn’t actually quit and start again, so the ps lstart timestamp doesn’t change. (You just send it a HUP signal and it restarts itself.)

But check this out: https://github.com/cyrilpic/god/blob/0862ec8d889470b8de09e6b75e50de2c306951d0/lib/god/conditions/file_touched.rb

It’s an open pull request right now, but just wget and “sudo god load” that rb file, and you are good to go!

James Hu says:

If you want to take advantage of events, you’re gonna need to use sudo.

Otto Hilska says:

I did a yet another take on this, running god as a non-root user, together with Bundler. Also tried to make sure that the workers restart gracefully.

http://blog.flowdock.com/2012/08/29/capistrano-god-bundler-resque-configuration/

Joost says:

For me it stops working after the first touch of the restart file.
God does not perform the RestartFileTouched condition test anymore..

Any suggestions?

Add a Comment




Follow Me
    Random Quote