Skip to content

Using Monit to monitor delayed_job in Rails 3

December 26, 2011

I am developing an application using RubyonRails3 . My application need to send alert notification , reminder via sms , email to hundreds of users. To make my application responsive I have to do these tasks in the background process. After exploring many alternatives I see that delayed_job fits my needs.  Delayed_job run a separate process with my rails app.

Delayed Job workers generally have a lifecycle that is equivalent to an application deployment.Because of this, their memory consumption grows over time and may eventually have high swapusage, causing workers to become unresponsive so  I  decide to use a monitoring tool Monit to watch  jobs. Monit can restart them when their memory usage hits a certain point. The good thing I like from monit is when a pre defined condition is matched, Monit can alert my for example by email by telling us what happen so I can take action accordingly.

I am using Ruby 1.9.2, rvm 1.8.6 , rails 3.07 on Ubuntu 10.10.

my application is located at : /var/www/rails3

/var/www/rails/.rvmrc file contains the following content:

rvm use 1.9.2@dyrm --create

Include the following line in you /var/www/rails3/Gemfile

gem "delayed_job"

Then to install delayed_job gem, in your terminal type:

/var/www/rails3$    bundle install

then

/var/www/rails3$ rails g delayed_job
/var/www/rails3$ rake db:migrate
#inserting job in my delayed_job
def self.alert notification
    notification.users.each do |user|
      Delayed::Job.enqueue(EmailJob.new(user, message)) unless user.email.blank?
      Delayed::Job.enqueue(SmsJob.new(user, message)) unless user.phone_number.blank?
    end
  end

My Email job:

  class EmailJob
  def initialize user, message
    @user = user
    @message = message
  end

  def perform
    UserMailer.notify_members(@user, @message).deliver
  end
end
  class SmsJob
  def initialize user, message
    @user = user
    @message = message
  end

  def perform
    Sms.send do |sms|
      sms.to = @user.phone_number
      sms.body = @message
    end
  end
end

To be able to puts your job into delayed_job, you job objects need to response to perform method. because perform method does not require any parameter so I need to pass through the constructor .

Installing Monit

In your terminal :

sudo apt-get install monit

configure monit to start up as daemon by editing the file /etc/default/monit with:

startup=1

Then start the daemon with:

sudo /etc/init.d/monit start

In my case I need to edit file

Some time the monit daemon does not start, however the output from terminal console

telling us the service  already started. I find the command:

sudo monit summary

is very useful to debug this. After all edit the file /etc/monit/monitrc

set alert channa@instedd.org

to set the alert for monit to alert to.

set httpd port 2812 and
#    use address localhost  # only accept connection from localhost
     allow localhost        # allow localhost to connect to the server and
     allow admin:monit      # require user 'admin' with password 'monit'
#    allow @monit           # allow users of group 'monit' to connect (rw)
#    allow @users readonly  # allow users of group 'users' to connect readonly

Use web user interface to monitor my process(on port 2812). Monit comes bundled with a web server using http basic auth.
allow admin:monit to login to web ui with user equal to admin and password monit

include /etc/monit/conf.d/*

I include all my monit script located in side folder: /etc/monit/conf.d

Create a file called notification inside /etc/monit/conf.d. It’s name is not important at all.

check process delayed_job
      with pidfile /var/www/rails/tmp/pids/delayed_job.pid
      stop program  = "/usr/bin/env RAILS_ENV=production  BUNDLE_GEMFILE=/var/www/rails/Gemfile /home/chenseng/.rvm/bin/bundle-ruby-1.9.2-p290@dyrm exec /var/www/rails/script/delayed_job stop"
      start program = "/usr/bin/env RAILS_ENV=production  BUNDLE_GEMFILE=/var/www/rails/Gemfile /home/my_user/.rvm/bin/bundle-ruby-1.9.2-p290@dyrm exec /var/www/rails/script/delayed_job start" 

      if totalmem > 60 MB for 3 cycles then restart
      if cpu usage > 50% for 3 cycles then restart

Here we use rvm wrapper to run delayed_job from a monit process. rvm wrapper give us ability to run any ruby script in context of it’s gemset without cd to it’s own directory.

Create wrapper

   /var/www/rails$ rvm wrapper 1.9.2@dyrm "" bundle

this will create /home/my_user/.rvm/bin/bundle-ruby-1.9.2-p290@dyrm executable.

Debug

Before making sure that monit can start my delayed_job process correctly, I try to run delayed_job with my rvm wrapper independently.
In terminal cd to your home directory:
/home/my_user$ /usr/bin/env RAILS_ENV=production BUNDLE_GEMFILE=/var/www/rails/Gemfile /home/my_user/.rvm/bin/bundle-ruby-1.9.2-p290@dyrm exec /var/www/rails/script/delayed_job start

check the output if nothing go wrong then it is time to restart my monit:

  /home/my_app$ sudo monit stop all
  /home/my_app$ sudo /etc/init.d/monit/restart
  /home/my_app$ sudo monit start all
  /home/my_app$ sudo monit summary

It is quite helpful to check if my monit config files are correctly written with the correct permission. To do this
sudo monit -c /etc/monit/monitrc
sudo monit -c /etc/monit/conf.d/notification

Web interface:

http://localhost:2832

 

 

Advertisements

From → Ruby, Rubyonrails

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: