Rails god config

Apr 07, 2008

If you're anything like me, you're not a big one for shelling out needless money to host the various development sites your working on. That inevitably means you'll end up with a whole host of small applications running on the one server, each needing a mongrel or two, and of course you want to make sure they are up and running. Thanks to the wonder that is ruby (and god) we can easily set it up to automatically become aware of any new apps we deploy via capistrano.

Enter app.god

Don't stress if you've never heard of app.god, it's a convention we've taken on where I'm currently working so chances are it doesn't exist outside my own little world. Here's what it looks like

class Rubypond

  attr_reader :ports, :server_names



  def initialize

    @ports = [5000, 5001]

    @server_names = "www.rubypond.com rubypond.com" 

  end

end



@apps 


What we do is define a new class named after whatever we want to call our application, and then create a new instance off it in an array names @apps. Next step, making god aware of these config files.

Setting up god.conf

If we are going to be running multiple rails apps on the one server, there is going to be a lot of duplication with the config files. Life will be much easy if we extract the common bits into some methods that we can call at the appropriate time to create our god watch:

def configure_mongrel(w, rails_root,  app_name, port)

  w.uid = "mongrel" 

  w.gid = "webserver" 

  w.name = "#{app_name}-#{port}" 

  w.group = "#{app_name}" 

  w.interval = 30.seconds

  w.start = "mongrel_rails start -c #{rails_root} -p #{port} -P #{rails_root}/log/mongrel.#{port}.pid -e production -d" 

  w.stop = "mongrel_rails stop -P #{rails_root}/log/mongrel.#{port}.pid" 

  w.restart = "mongrel_rails restart -P #{rails_root}/log/mongrel.#{port}.pid" 

  w.pid_file = "#{rails_root}/log/mongrel.#{port}.pid" 



  w.behavior(:clean_pid_file)

end



def start_if_not_running(w)

  w.start_if do |start|

    start.condition(:process_running) do |c|

      c.interval = 5.seconds

      c.running = false

    end

  end

end



def restart_if_resource_hog(w)

  w.restart_if do |restart|

    restart.condition(:memory_usage) do |c|

      c.notify = "glenn" 

      c.above = 150.megabytes

      c.times = [3,5]

    end



    restart.condition(:cpu_usage) do |c|

      c.notify = "glenn" 

      c.above = 50.percent

      c.times = 5

    end

  end

end



def monitor_lifecycle(w)

  w.lifecycle do |on|

    on.condition(:flapping) do |c|

      c.notify = "glenn" 

      c.to_state = [:start, :restart]

      c.times = 5

      c.within = 5.minutes

      c.transition = :unmonitored

      c.retry_in = 10.minutes

      c.retry_times = 5

      c.retry_within = 2.hours

    end

  end

end



def monitor_rails_app(w)

  start_if_not_running(w)

  restart_if_resource_hog(w)

  monitor_lifecycle(w)

end


Letting god know about our rails apps

Next thing we want god to know about all our rails apps, we can use a standard ruby trick to traverse the appropriate directory tree for find our app.god configs, and then load them in using the ruby load command:

@apps = []

Dir["/var/www/apps/*/current/config/*.god"].each { |config| load config}


And because of the way we've instantiated our classes in each config, @apps will now be an array of applications we want to configure. So we can iterate over each, and plug in the appropriate values:

@apps.each do |app|

  app_name = app.class.name.downcase

  rails_root = "/var/www/apps/#{app_name}/current" 



  app.ports.each do |port|

    God.watch do |w|

      configure_mongrel(w, rails_root, app_name, port)

      monitor_rails_app(w)

    end

  end

end


Now all you need to do when you deploy a new application is make sure you have the config/app.god file and restart your god instance, and it will automatically monitor your new application. Sweet! Next up I'll run through a capistrano task to complete the deal by using the same config to setup nginx. So, we may as well seal the deal by ensuring that happens in capistrano (in your deploy.rb file):

after "deploy:setup", "deploy:god:restart" 



namespace :deploy do



  namespace :god do



    task :restart, :roles=>:app do

      sudo "/etc/init.d/god restart" 

    end



  end

end


Hi, I'm Glenn! 👋 I'm currently Director of Product @ HashiCorp, and we're hiring! If you'd like to come and work with me and help make Terraform Cloud even more amazing we have multiple positions opening in Product ManagementDesign, and Engineering & Engineering Management across a range of levels (i.e., junior through to senior). Please send in an application ASAP so we can get in touch.