20

For my RubyOnRails-App I have to start a background job at the end of Capistrano deployment. For this, I tried the following in deploy.rb:

run "nohup #{current_path}/script/runner -e production 'Scheduler.start' &", :pty => true

Sometimes this works, but most of the time it does not start the process (= not listed in ps -aux). And there are no error messages. And there is no nohup.out, not in the home directory and not in the rails app directory.

I tried using trap('SIGHUP', 'IGNORE') in scheduler.rb instead of nohup, but the result is the same.

The only way to get it work is removing the ":pty => true" and do a manual Ctrl-C at the end of "cap deploy". But I don't like this...

Are there any other chances to invoke this Scheduler.start? Or to get some more error messages?

I'm using Rails 2.3.2, Capistrano 2.5.8, Ubuntu Hardy on the Server

Georg Ledermann
  • 2,712
  • 4
  • 31
  • 35

4 Answers4

18

With :pty => true, user shell start-up scripts (e.g. bashrc, etc.) are (usually) not loaded. My ruby program exited right after launching because of the lack of dependent environment variables.

Without :pty => true, as you described in the question, capistrano hangs there waiting for the process to exit. You'll need to redirect both stdout and stderr to make it return immediately.

run 'nohup ruby -e "sleep 5" &' # hangs for 5 seconds
run 'nohup ruby -e "sleep 5" > /dev/null &' # hangs for 5 seconds
run 'nohup ruby -e "sleep 5" > /dev/null 2>&1 &' # returns immediately. good.

If your background task still doesn't run. Try redirecting stdout and stderr to a log file so that you can investigate the output.

Arrix
  • 2,721
  • 20
  • 19
  • 3
    I struggled for a whole day with exactly the same problem. Redirecting the output as suggested solved the problem. – randomuser Jan 18 '12 at 06:03
6

I'd like to share my solution which also works when executing multiple commands. I tried many other variants found online, including the "sleep N" hack.

run("nohup sh -c 'cd #{release_path} && bundle exec rake task_namespace:task_name RAILS_ENV=production > ~/shared/log/<rakelog>.log &' > /dev/null 2>&1", :pty => true)

This is a dup response launching background process in capistrano task but want to make sure others and myself can google for this solution.

Community
  • 1
  • 1
okor
  • 666
  • 8
  • 13
1

Do you want your Scheduler job to run continually in the background and get restarted when you run Capistrano?

If so, then for that I use runit http://smarden.sunsite.dk/runit/ and DelayedJob http://github.com/Shopify/delayed_job/tree/master

  1. Install runit in the mode of not replacing init
  2. Add your background job as a runit service and add the log monitor for it from runit.
  3. Have Capistrano call sudo sv kill job_name to kill and restart the job.

My backround job is an instance of the Rails plugin DelayedJob which handles background Rails tasks. I kill it with every Capistrano deploy so it will restart with the updated code base.

This has proved to be very reliable.

HTH,

Larry

Larry K
  • 47,808
  • 15
  • 87
  • 140
  • Yes, the scheduler should run continually in the background and should be restartet on every deploy. Im using DelayedJob in the scheduler - and some own stuff. Because starting via script/runner works sometimes with Capistrano (and always if I start it manually via a SSH session) I think there is only a small problem with Capistrano - and so I don't want to make the move to runit, if it's not really needed... But thank you very much for for anwer, Larry! – Georg Ledermann Jul 10 '09 at 14:18
  • What about auto-restart upon reboot? That's something else that runit takes care of. -- Plus restarts if it ever dies. Regards. – Larry K Jul 10 '09 at 20:51
  • The deploy user should never have `sudo` privileges. In most deploys, the deploy user is the same as the user that runs the rails app. Giving sudo permissions to that user is a Very Bad Idea, especially given recent security issues with rails where attackers have been able to execute arbitrary code. – platforms Feb 16 '13 at 15:19
1

If this task scheduler has a -d switch it will work. For example passenger standalone has a -d option to start it as a demonized process.

namespace :passenger_standalone do
  task :start do
    run "cd #{current_path} && passenger start -e #{rails_env} -d"
  end
  task :stop do
    run "cd #{current_path} && RAILS_ENV=#{rails_env} passenger stop"
  end
  task :restart do
    stop
    start
  end
end
Pratik Khadloya
  • 12,509
  • 11
  • 81
  • 106