Monitor ruby processes with Monit - ruby

I have a bunch of Ruby scripts which are long running, I'd like to ensure that every 30 seconds or so these are up.
I normally start the commands by simply ruby script-name.rb
How can I configure monit to look after these scripts?
Update: I tried to follow this method to create a wrapper script which would then launch the ruby process but it did not seem to create the .pid file and typing './wrapper-script stop' did nothing :/
Should I write the pid inside ruby or use a wrapper script to create the pid necessary for monit?

The Monit Wiki has a lot of configuration examples:
http://mmonit.com/wiki/Monit/ConfigurationExamples
Just pick a simple one and modify it according to your needs.
Update: the wrapper script should create the pid for you in this line:
echo $$ > /var/run/xyz.pid;
Did you adapt the script to your needs? Is it executable (chmod +x)? Does it have write permissions for the destination? Maybe post the wrapper you are trying to use so I can help you more.

You don't need to write a wrapper script or try any kind of black magic, just use the Daemons library and you're done.
Imagine that you have a class Worker that has a method "run" that enters an infinite loop reading from a socket or anything like that, here's how you'd write your Daemons wrapper:
# this is file my_worker_control.rb
require 'rubygems'
require 'daemons'
require 'worker'
Daemons.run_proc(:app_name => 'my_worker', :dir_mode => :system, :log_output => true ) do
Worker.run
end
Once the script is done, just call it from your command line or an init.d script:
my_worker_control.rb run|start|stop|restart
This config will generate a "my_worker.pid" file under "/var/run" and you can use monit to watch over the process by using this file.

Modify the file :
/etc/init.d/skeleton
You will need to slightly modify it, and then :
chmod +x /etc/init.d/process_name
sudo update-rc.d process_name defaults
sudo /etc/init.d/process_name (start| stop| reload )
Now just use Monit with the pid at /var/run/process.pid
start location : sudo /etc/init.d/process start
stop location : sudo /etc/init.d/process stop
Cheers

Writing the pid file in your ruby script may be easiest for you (just open a file and write $$ in it). That said, the wrapper script approach should work fine. Does your script have permission to write to a file in /var/run (or wherever you are putting the pidfile)?

As an alternative (to monit), have a look at bluepill.

(Surely out of subject but) as it is about ruby, why don't you use : http://god.rubyforge.org/ ?

Add this line to your ruby script yourapp.rb, that creates a pid file named yourapp.pid
File.open('/somepath/yourapp.pid', 'w') {|f| f.write Process.pid }
Configure Monit to check for the pid in /etc/monit/conf.d/yourapp
check process yourapp with pidfile /somepath/yourapp.pid

Related

Run Script on AWS #reboot?

I'm currently trying to run script that will run in the background when my AWS instance boots for the duration of the instance life. I'm testing it with a simple script to see if it works, before I test with my more complicated one:
#!/bin/bash
while [true]; do
sleep 1
echo "Hello World" >> "tempStorage.json"
done
And my sudo crontab -l returns:
# All the comment stuff
#reboot sh /home/ubuntu/test/testScript/test.sh
Which is the path to the script. I've also obviously run chmod +x test.sh to make sure its an executable.
The problem is when I stop and then start the AWS instance there's nothing in the tempStorage.json file. I've checked other threads and they all suggest this is what I should be doing, so I'm very confused and advice would be appreciated. Thanks.
As Mark B mentioned, the issue is the execution directory of the cron script. There are two solutions then.
A) Change the path to file as Mark B recommended so the script would look something like:
#!/bin/bash
while [true]; do
sleep 1
echo "Hello World" >> "/home/ubuntu/test/testScript/tempStorage.json"
done
B) Change the directory of the cron execution and keep the script as it was. This works better if you need to put the script in any directory. It would look like this for the crontab:
# All the comment stuff
#reboot cd /home/ubuntu/test/testScript && sh test.sh
That should work fine. I think the issue is that you aren't giving the full path to the tempSTorage.json file within your script. So it is being written to in a different folder than the one you are looking in, specifically whatever folder cron starts processes in by default. Try changing it to something like /tmp/tempSTorage.json and then rebooting the server again.
Note that if you are wanting something that starts on boot and runs forever, this probably isn't the best method. In that case I would look into running your process as a service.

Chef run sh script

I have a problem trying to run shell script via Chef (with docker-provisioning).
This is how I try to execute my script:
bash 'shell_try' do
user "root"
run = "#{some_path_to_script}/my_script.sh some_params"
code " #{run} > stdout.txt 2> stderr.txt"
end
(note that this script should run another scripts, processes and write logs)
Here's no errors in the output, but when I log into machine and run ps aux process isn't running.
I guess something wrong with permissions (or env variables), because when I try the same command manually - it works.
A bash resource just runs the provided script text directly, if you wanted to run a long-running process generally you would set up an Upstart or systemd service and use the service resource to start it.
Finally find a solution (thanks to #coderanger) -
Install supervisor:
Download supervisor cookbook
Add:
include_recipe 'supervisor::default'
Add my service to supervisor:
supervisor_service "name" do
action :enable
#action :start
command '/path/script.sh start'
end
Run supervisor service
All done!
Please see the Chef documentation for your resource: https://docs.chef.io/resource_bash.html. The bash resource does not support a run attribute. Text of the code attribute is run as a bash script. The default action is to run the script unless told otherwise by the resource.
bash 'shell_try' do
user "root"
code " #{run} > stdout.txt 2> stderr.txt"
action :run
end
The code attribute is written to a temporary file where it is then run using the attributes specified in the resource.
The line run = "#{some_path_to_script}/my_script.sh some_params" at this point does nothing.

How can I automate a script [Ruby] to run at a given time every day?

I've written a webcrawler that pulls information into a report and would like to run it every day at 12:00pm. The script is run using:
ruby script.rb
I've tried using the whenever gem (https://github.com/javan/whenever).
My directory structure is this:
/config
schedule.rb
script.rb
In my script.rb file, I have the following:
every :day, :at => '12:00pm' do
command "ruby script.rb"
end
I've modified the time :at to take see if it runs and it doesn't.
I've also tried:
every :day, :at => '12:00pm' do
`ruby script.rb`
end
I've also looked into the "at" linux utility but it appears suited to one-time jobs. I'd like this to generate a report everyday.
Note: the script specifies where to output so I don't need to give it an output.
I've also tried creating a crontab but have encountered a problem with saving.
I use http://crontab-generator.org/ to generate the correct syntax.
Then I run:
crontab -e
Which opens vi and I copy the syntax. However, it exits with a status of 1 and if I run:
crontab -l
It says there's no jobs listed.
I've also tried running this as the super user, and it exits the same.
The error message is
/usr/bin/vi" exited with status 1
I just want a command to run at a given time, what am I missing?
Edit
Does it matter that I'm on a Mac?

Monitoring Ruby script, using Monit - Including RVM

Im using Monit to monitor a ruby script that uses Ruby daemons gem, which launches a separate process with PID - following the instructions from Monitor ruby processes with Monit
In order to execute the ruby script I need to include RVM in the Monit start and stop strings, so I have access to all the gems.
However when .monitrc executes I get the following error:
$rvm_path (/usr/local/rvm) does not exist./home/william/.rvm/scripts/rvm: line 174: rvm_is_a_shell_function: command not found
/home/william/.rvm/scripts/rvm: line 185: __rvm_teardown: command not found
'myserver_1' failed to start
Aborting event
I added PATH=$PATH:/home/william/.rvm/bin && . /home/william/.rvm/scripts/rvm to the start and stop command strings to try and include RVM. However still it doesn't work
Config file .monitrc:
....
check process myserver_1
with pidfile /home/william/ruby/barclays/myapp.rb.pid
start = "/bin/bash -c 'PATH=$PATH:/home/william/.rvm/bin && . /home/william/.rvm/scripts/rvm && ruby /home/william/ruby/barclays/daemonloader.rb start'"
stop = "/bin/bash -c 'PATH=$PATH:/home/william/.rvm/bin && . /home/william/.rvm/scripts/rvm && ruby /home/william/ruby/barclays/daemonloader.rb stop'"
....
Thanks for your help.
EDIT
Ive got a feeling the problem is related to environment variables. Quoting from this page
You should also know that for security reasons Monit purges the
environment and only sets a spartan PATH variable that contains /bin,
/usr/bin, /sbin and /usr/sbin. If your program or script dies, the
reason could be that it expects certain environment variables or to
find certain programs via PATH. If this is the case you should set the
environment variables you need directly in the start or stop script
called by monit.
Finally, Monit uses the system call execv to execute a program or a
script. This means that you cannot write shell commands directly in
the start, stop or exec statements. To do this, you must do as above;
start a shell and issue your commands there. For example:
start program = "/bin/bash -c 'my shell command && my other
command'"
Use this:
/path/to/rvm/bin/rvm in /path/to/project do ...
Replace the paths with proper directories for rvm and project and the ... with the commands to stop/start - try:
/usr/bin/env "HOME=/home/william rvm_path=/home/william/.rvm
/home/william/.rvm/bin/rvm in /home/william/ruby/project do
ruby daemonloader.rb start"
This command will load RVM, cd into project path, load ruby for this ruby and execute given command.
You could try something like this in Monit.
start = "/bin/su - william -c 'cd /home/william/ruby/project && ~/.rvm/bin/rvm default do bundle exec ruby daemonloader.rb start'"
This worked for me.
Mentioning the gemset and ruby source solves the problem for me.
start program = "/bin/bash -c 'cd /home/project_path && source /home/user/.rvm/environments/ruby-2.4.2#global && RAILS_ENV=production bundle exec rails s'"

Log process output

I used the method system to start a process. the pid of that process is being stored in a file worker.pid
however I need to generate the log of this process, how can I store the output of this process?
the process is being created with this command:
system "bundle exec rake resque:work >> ./resque.log QUEUE=* PIDFILE=#{pid_file} &"
P.S.: I am using ruby 1.8, BACKGROUND=yes won`t work.
P.S.2: platform linux
Maybe what you're looking for is IO.popen
This lets you fork off a subprocess and access it's output via an IO object
# fork off a one-off task
# and return the output as a string
ls = IO.popen("ls")
ls.read
# or return an array of lines
IO.popen("ls").readlines
# or create a continuing task
tail = IO.popen("tail -f /some/log/file.log")
loop do
puts tail.gets
end
I suggest you read the documentation,
but you can also write to the stream, and do all sorts of clever stuff.
If I'm understanding what you are trying to achieve correctly, you are looking for the Open3 class. http://www.ruby-doc.org/stdlib-1.8.7/libdoc/open3/rdoc/Open3.html

Resources