Git Product home page Git Product logo

bluepill's Introduction

Bluepill

Bluepill is a simple process monitoring tool written in Ruby.

Gem Version Build Status Dependency Status Code Climate Coverage Status

Installation

It's hosted on rubygems.org.

sudo gem install bluepill

In order to take advantage of logging with syslog, you also need to setup your syslog to log the local6 facility. Edit the appropriate config file for your syslogger (/etc/syslog.conf for syslog) and add a line for local6:

local6.*          /var/log/bluepill.log

You'll also want to add /var/log/bluepill.log to /etc/logrotate.d/syslog so that it gets rotated.

Lastly, create the /var/run/bluepill directory for bluepill to store its pid and sock files.

Usage

Config

Bluepill organizes processes into 3 levels: application -> group -> process. Each process has a few attributes that tell bluepill how to start, stop, and restart it, where to look or put the pid file, what process conditions to monitor and the options for each of those.

The minimum config file looks something like this:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
  end
end

Note that since we specified a PID file and start command, bluepill assumes the process will daemonize itself. If we wanted bluepill to daemonize it for us, we can do (note we still need to specify a PID file):

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.daemonize = true
  end
end

If you don't specify a stop command, a TERM signal will be sent by default. Similarly, the default restart action is to issue stop and then start.

Now if we want to do something more meaningful, like actually monitor the process, we do:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.checks :cpu_usage, every: 10.seconds, below: 5, times: 3
  end
end

We added a line that checks every 10 seconds to make sure the cpu usage of this process is below 5 percent; 3 failed checks results in a restart. We can specify a two-element array for the times option to say that it 3 out of 5 failed attempts results in a restart.

To watch memory usage, we just add one more line:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.checks :cpu_usage, every: 10.seconds, below: 5, times: 3
    process.checks :mem_usage, every: 10.seconds, below: 100.megabytes, times: [3,5]
  end
end

To watch the modification time of a file, e.g. a log file to ensure the process is actually working add one more line:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.checks :cpu_usage, every: 10.seconds, below: 5, times: 3
    process.checks :mem_usage, every: 10.seconds, below: 100.megabytes, times: [3,5]
    process.checks :file_time, every: 60.seconds, below: 3.minutes, filename: "/tmp/some_file.log", times: 2
  end
end

To restart process if it's running too long:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.checks :running_time, every: 10.minutes, below: 24.hours
  end
end

We can tell bluepill to give a process some grace time to start/stop/restart before resuming monitoring:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.start_grace_time = 3.seconds
    process.stop_grace_time = 5.seconds
    process.restart_grace_time = 8.seconds
    process.checks :cpu_usage, every: 10.seconds, below: 5, times: 3
    process.checks :mem_usage, every: 10.seconds, below: 100.megabytes, times: [3,5]
  end
end

We can group processes by name:

Bluepill.application("app_name") do |app|
  5.times do |i|
    app.process("process_name_#{i}") do |process|
      process.group = "mongrels"
      process.start_command = "/usr/bin/some_start_command"
      process.pid_file = "/tmp/some_pid_file.pid"
    end
  end
end

If you want to run the process as someone other than root:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.uid = "deploy"
    process.gid = "deploy"
    process.checks :cpu_usage, every: 10.seconds, below: 5, times: 3
    process.checks :mem_usage, every: 10.seconds, below: 100.megabytes, times: [3,5]
  end
end

If you want to include one or more supplementary groups:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.uid = "deploy"
    process.gid = "deploy"
    process.supplementary_groups = ['rvm']
    process.checks :cpu_usage, every: 10.seconds, below: 5, times: 3
    process.checks :mem_usage, every: 10.seconds, below: 100.megabytes, times: [3,5]
  end
end

You can also set an app-wide uid/gid:

Bluepill.application("app_name") do |app|
  app.uid = "deploy"
  app.gid = "deploy"
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
  end
end

To track resources of child processes, use :include_children:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.checks :mem_usage, every: 1.seconds, below: 5.megabytes, times: [3,5], include_children: true
  end
end

To check for flapping:

process.checks :flapping, times: 2, within: 30.seconds, retry_in: 7.seconds

To set the working directory to cd into when starting the command:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.working_dir = "/path/to/some_directory"
  end
end

You can also have an app-wide working directory:

Bluepill.application("app_name") do |app|
  app.working_dir = "/path/to/some_directory"
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
  end
end

Note: We also set the PWD in the environment to the working dir you specify. This is useful for when the working dir is a symlink. Unicorn in particular will cd into the environment variable in PWD when it re-execs to deal with a change in the symlink.

By default, bluepill will send a SIGTERM to your process when stopping. To change the stop command:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.stop_command = "/user/bin/some_stop_command"
  end
end

If you'd like to send a signal or signals to your process to stop it:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/some_start_command"
    process.pid_file = "/tmp/some_pid_file.pid"
    process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
  end
end

We added a line that will send a SIGQUIT, wait 30 seconds and check to see if the process is still up, send a SIGTERM, wait 5 seconds and check to see if the process is still up, and finally send a SIGKILL.

And lastly, to monitor child processes:

process.monitor_children do |child_process|
  child_process.checks :cpu_usage, every: 10, below: 5, times: 3
  child_process.checks :mem_usage, every: 10, below: 100.megabytes, times: [3, 5]
  child_process.stop_command = "kill -QUIT {{PID}}"
end

Note {{PID}} will be substituted for the pid of process in both the stop and restart commands.

A Note About Output Redirection

While you can specify shell tricks like the following in the start_command of a process:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "cd /tmp/some_dir && SOME_VAR=1 /usr/bin/some_start_command > /tmp/server.log 2>&1"
    process.pid_file = "/tmp/some_pid_file.pid"
  end
end

We recommend that you not do that and instead use the config options to capture output from your daemons. Like so:

Bluepill.application("app_name") do |app|
  app.process("process_name") do |process|
    process.start_command = "/usr/bin/env SOME_VAR=1 /usr/bin/some_start_command"
    process.working_dir = "/tmp/some_dir"
    process.stdout = process.stderr = "/tmp/server.log"
    process.pid_file = "/tmp/some_pid_file.pid"
  end
end

The main benefit of using the config options is that Bluepill will be able to monitor the correct process instead of just watching the shell that spawned your actual server.

CLI

Usage

bluepill [app_name] command [options]

For the "load" command, the app_name is specified in the config file, and must not be provided on the command line.

For all other commands, the app_name is optional if there is only one bluepill daemon running. Otherwise, the app_name must be provided, because the command will fail when there are multiple bluepill daemons running. The example commands below leaves out the app_name.

Commands

To start a bluepill daemon and load the config for an application:

sudo bluepill load /path/to/production.pill

To act on a process or group for an application:

sudo bluepill <start|stop|restart|unmonitor> <process_or_group_name>

To view process statuses for an application:

sudo bluepill status

To view the log for a process or group for an application:

sudo bluepill log <process_or_group_name>

To quit the bluepill daemon for an application:

sudo bluepill quit

Logging

By default, bluepill uses syslog local6 facility as described in the installation section. But if for any reason you don't want to use syslog, you can use a log file. You can do this by setting the :log_file option in the config:

Bluepill.application("app_name", log_file: "/path/to/bluepill.log") do |app|
  # ...
end

Keep in mind that you still need to set up log rotation (described in the installation section) to keep the log file from growing huge.

Foreground mode

You can run bluepill in the foreground:

Bluepill.application("app_name", foreground: true) do |app|
  # ...
end

Note that You must define only one application per config when using foreground mode.

JRuby allows you to run bluepill only in the foreground.

Links

Supported Ruby Versions

This library aims to support and is tested against the following Ruby implementations:

  • Ruby 1.9.3
  • Ruby 2.0.0
  • Ruby 2.1
  • Ruby 2.2
  • JRuby 1.7 (only in the foreground)
  • JRuby 9.0.0.0 (only in the foreground)

If something doesn't work on one of these interpreters, please open an issue.

This library may inadvertently work (or seem to work) on other Ruby implementations, however support will only be provided for the versions listed above.

If you would like this library to support another Ruby version, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped.

bluepill's People

Contributors

agibralter avatar akzhan avatar arbales avatar arya avatar blt04 avatar cbeer avatar danielsdeleo avatar dv1 avatar evanlok avatar garru avatar griff avatar gudleik avatar guss77 avatar hoylen avatar iancoffey avatar jsierles2 avatar leifcr avatar msnexploder avatar nickryand avatar qades avatar raggi avatar rakaur avatar ryansch avatar sevos avatar sferik avatar skull-squadron avatar software-project avatar syelle avatar tomdmaguire avatar vangberg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bluepill's Issues

Incompatible with JRuby

JRuby does not support fork, as can be seen below:

irb(main):003:0> JRUBY_VERSION
=> "9.1.7.0"
irb(main):004:0> fork
NotImplementedError: fork is not available on this platform

The compatibility advice for this gem should be updated to indicate that JRuby will only work with the foreground option being true.

Thanks!

:mem_usage ignored

If I have a start_command such as:

#!/usr/bin/env ruby
class Job
  def run
    a = []
    while true; a << 'x' * 1024; end
  end
end

j = Job.new
j.run

The check I have configured never kills it:

process.start_command = './job.rb'
process.checks :mem_usage, below: 20.megabytes, every: 5.seconds, times: 3

The process grows until I kill it manually, well past the 20MB limit I set. Using the :include_children option had no effect.

I have this same issue on Mac OS and Linux.

Start hangs, Ruby 2.0

Workflow load config -> daemon up -> send stop -> daemon unmonitore -> send start = hangs

Retry 1 of 5
Retry 2 of 5
Retry 3 of 5
Retry 4 of 5
Socket Timeout: Server may not be responding

last bluepill.log string daemon start command

But anyway daemon is started.

Same work perfect in Ruby 1.9

PS: i use test daemon do loop ... print i work ... end

Capistrano 3 - stderr: /usr/bin/bluepill:3:in `require': no such file to load

Hey guys,

I'm trying to configure bluepill with capistrano 3 and I have the following issue:
On the server side I install "gem install bluepill". Then I add a symbolic link for bluepill binary:
sudo ln -s /home/deploy/.rvm/gems/ruby-2.1.2/gems/bluepill-0.0.68/bin/bluepill /usr/bin/bluepill
Then I add bluepill to startup:
sudo -H -u deploy bash -c '/home/deploy/.rvm/bin/rvm 2.1.2 do bluepill load /var/www/test_app/current/config/staging.pill --no-privileged'

On my deploy.rb I have:

desc "Load bluepill"
task :load_config do
on roles(fetch(:bluepill_roles)) do
execute "bluepill load /var/www/test_app/current/config/staging.pill --no-privileged"
end
end

then I try to deploy and I get the following error in capistrano:

The deploy has failed with an error: #<SSHKit::Runner::ExecuteError: Exception while executing on host XXX.XXX.XXX.XXX: bluepill load /var/www/test_app/current/config/staging.pill --no-privileged exit status: 1
bluepill load /var/www/test_app/current/config/staging.pill --no-privileged stdout: Nothing written
bluepill load /var/www/test_app/current/config/staging.pill --no-privileged stderr: /usr/bin/bluepill:3:in `require': no such file to load -- bluepill (LoadError)
from /usr/bin/bluepill:3

Does someone know why this is failing?

Capistrano version 3.1

Regression: bluepill status no longer shows processes status

After updating to version 0.0.69, the status command no longer shows the processes being monitored:

# bluepill --version
bluepill, version 0.0.68
# bluepill wfs status
wfs-puma(pid:89264): up

resque:
  wfs-resque-0(pid:89274): up
  ...

# bluepill quit
...
# gem update bluepill
...
# bluepill --version
bluepill, version 0.0.69
# bluepill load wfs.pill
# bluepill wfs status

resque:

Ability to specify supplementary groups in bluepill

It would be useful to have the option of bluepill applications and processes having:

  • The default groups of a user
  • Ability to specify the supplementary groups of a process

If we have a user user1 who has a primary group groupa and supplementary groups groupb and groupc. And a few files:

-rw-r----- 1 user1    groupa  1 Jan  8 10:30 file1
-rw-r----- 1 user2    groupb  1 Jan  8 10:30 file2
-rw-r----- 1 user2    groupa  1 Jan  8 10:30 file3
-rw-r----- 1 user2    groupc  1 Jan  8 10:30 file4

In this case the user should be able to read all of the files above as should a process invoked from the users shell. It would be nice to have this functionality in bluepill as we are currently restricted to just specifying the gid which means that we can only access a subset of the files above.

Bluepill doesn't honor stop_signals delays

When using stop signals to set up a multiple signals shutdown process with a delay between signals, then when issuing a stop command Bluepill does the first signal and then just terminates all the processes and exits.

The problem seems to be in process.rb, where if stop_signals are set, it starts a thread to handle the stop_signals, and then immediately calls kill_all_from_journal which basically just sends TERM to all processes.

A correct implementation of this is required to handle graceful shutdown of processes with a long shutdown process, such as a resque worker.

Bluepill and Puma won't play together

Bluepill config

APP_ROOT = '/root/bytet'
REDIS_ROOT = '/usr/bin'

Bluepill.application('bytet', :log_file => "#{APP_ROOT}/tmp/logs/bluepill.log") do |app|

  app.working_dir = APP_ROOT

  app.process('puma') do |process|
    process.start_command = 'bundle exec puma -e production'
    process.pid_file = 'tmp/pids/puma.pid'

    process.deamonize = true
    process.environment = {'RAILS_ENV' => 'production'}
    process.stdout = process.stderr = 'tmp/logs/puma.log'
    process.stop_command = 'bundle exec pumactl stop --pid {{PID}}'
    process.monitor_children do |children|
      children.checks :cpu_usage,
                       :every => 10,
                       :below => 50,
                       :times => 3
      children.checks :mem_usage,
                     :every => 10,
                     :below => 300.megabytes,
                     :times => 3
    end
    process.restart_command = 'bundle exec pumactl restart --pid {{PID}}'
    process.restart_grace = 10.seconds
  end
end

Logs
Bluepill is looping these messages over and over.
bluepill log puma

W, [2015-08-07T12:06:23.581404 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
W, [2015-08-07T12:06:23.581598 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
E, [2015-08-07T12:06:23.581661 #6585] ERROR -- : [bytet:puma] Failed to signal process  with code 0: no implicit conversion from nil to integer
W, [2015-08-07T12:06:23.581753 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
W, [2015-08-07T12:06:23.581862 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
E, [2015-08-07T12:06:23.581919 #6585] ERROR -- : [bytet:puma] Failed to signal process  with code 0: no implicit conversion from nil to integer
W, [2015-08-07T12:06:23.582664 #6585]  WARN -- : [bytet:puma] Executing start command: bundle exec puma -e production
E, [2015-08-07T12:06:26.584153 #6585] ERROR -- : [bytet:puma] Execution is taking longer than expected.
E, [2015-08-07T12:06:26.584336 #6585] ERROR -- : [bytet:puma] Did you forget to tell bluepill to daemonize this process?
W, [2015-08-07T12:06:26.585035 #6585]  WARN -- : [bytet:puma] Clearing child list
I, [2015-08-07T12:06:26.585114 #6585]  INFO -- : [bytet:puma] Going from down => starting
W, [2015-08-07T12:06:29.657280 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
W, [2015-08-07T12:06:29.657467 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
E, [2015-08-07T12:06:29.657570 #6585] ERROR -- : [bytet:puma] Failed to signal process  with code 0: no implicit conversion from nil to integer
W, [2015-08-07T12:06:29.657893 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
W, [2015-08-07T12:06:29.657994 #6585]  WARN -- : [bytet:puma] pid_file /var/run/bluepill/pids/bytet/puma.pid does not exist or cannot be read
E, [2015-08-07T12:06:29.658053 #6585] ERROR -- : [bytet:puma] Failed to signal process  with code 0: no implicit conversion from nil to integer
W, [2015-08-07T12:06:29.658386 #6585]  WARN -- : [bytet:puma] Clearing child list
I, [2015-08-07T12:06:29.658479 #6585]  INFO -- : [bytet:puma] Going from starting => down

Puma config

workers 2
threads 1, 3
preload_app!
daemonize false
rackup DefaultRackup
port        3000
environment 'production'
pidfile 'tmp/pids/puma.pid'

Trying to restart

root@bytet:~# bluepill stop puma
/root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:21:in `load': marshal data too short (ArgumentError)
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:21:in `block (3 levels) in client_command'
    from /root/.rbenv/versions/2.2.2/lib/ruby/2.2.0/timeout.rb:89:in `block in timeout'
    from /root/.rbenv/versions/2.2.2/lib/ruby/2.2.0/timeout.rb:34:in `block in catch'
    from /root/.rbenv/versions/2.2.2/lib/ruby/2.2.0/timeout.rb:34:in `catch'
    from /root/.rbenv/versions/2.2.2/lib/ruby/2.2.0/timeout.rb:34:in `catch'
    from /root/.rbenv/versions/2.2.2/lib/ruby/2.2.0/timeout.rb:104:in `timeout'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:19:in `block (2 levels) in client_command'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:11:in `open'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:11:in `client'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:18:in `block in client_command'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:16:in `times'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/socket.rb:16:in `client_command'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/controller.rb:65:in `send_to_daemon'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/lib/bluepill/controller.rb:28:in `handle_command'
    from /root/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/bluepill-0.0.69/bin/bluepill:123:in `<top (required)>'
    from /root/.rbenv/versions/2.2.2/bin/bluepill:23:in `load'
    from /root/.rbenv/versions/2.2.2/bin/bluepill:23:in `<main>'

Is this a configuration error, in which a misunderstand the documentation completely?

Sending โ€˜stopโ€™ or โ€˜restartโ€™ to โ€˜startingโ€™ service gives SystemStackError

This issue seems to have popped up since weโ€™ve upgraded to Ruby 2.1.1.

If we start our service:

$ sudo bluepill myapp start
Sent start to:
  :myapp_unicorn
  :myapp_sidekiq

And then, while itโ€™s still starting:

$ sudo bluepill myapp status
myapp_unicorn(pid:): starting
myapp_sidekiq(pid:): starting

If we try to stop it or restart it:

$ sudo bluepill myapp stop
Received error from server:
#<SystemStackError: stack level too deep>
(eval):17

$ sudo bluepill myapp restart
Received error from server:
#<SystemStackError: stack level too deep>
(eval):17

Bluepill fails to create process silently

I have a problem where I load a bluepill file and rather than bluepill creating the process and monitoring the new pid, it reports pid of zero. Bluepill is running as root, but dropping privileges to a non-privileged user for the created process.

Some more details:

  1. The pid file shows an actual pid number and a sock file is generated. Notice that the journals are not created.
find /var/run/bluepill/ | grep myapp
/var/run/bluepill/pids/myapp.pid
/var/run/bluepill/pids/myapp
/var/run/bluepill/socks/myapp.sock

/var/run/bluepill/pids/myapp.pid
10602
  1. The bluepill log shows a pid as well, but doesn't report any stats. Missing stats makes sense because there is no process matching the pid
bluepilld[10602]: [myapp:sidekiq] cpu_usage: [0.0, 0.0, 0.0, 0.0, 0.0]
bluepilld[10602]: [myapp:sidekiq] mem_usage: [0KB, 0KB, 0KB, 0KB, 0KB]
bluepilld[10602]: [myapp:sidekiq] mem_usage: [0KB, 0KB, 0KB]
bluepilld[10602]: [myapp:sidekiq] cpu_usage: [0.0, 0.0, 0.0, 0.0]
bluepilld[10602]: [myapp:sidekiq] mem_usage: [0KB, 0KB, 0KB, 0KB]
bluepilld[10602]: [myapp:sidekiq] cpu_usage: [0.0, 0.0, 0.0, 0.0, 0.0]
  1. And finally bluepill status reports pid 0, but there is not a process created.
bluepill myapp status
myapp:sidekiq(pid:0): up

Any hints or tips to troubleshoot further? Is there any way to get more verbosity into this logging?

Installation of bluepill in secure production env

how do i install bluepill in secure production environment which is not exposed to internet, gem install bluepill is not working for me as needed dependencies are not available, any option to download full package ?.

Marshall socket error

I have custom trigger:

class MyNotifier < Bluepill::Trigger
require 'redis'
  def initialize(process, options = {})
    @redis = Redis.new
    super
  end
  def notify(transition)
    return if transition.from_name == transition.to_name
    msg = "Notification at [#{Time.now}] for [#{self.process.name}] #{transition.from_name} -> #{transition.to_name}"

    open('/tmp/worker.transitions.log', 'a') do |f|
      f.puts msg
    end

    @redis.lpush('transitions', msg)
  end
end

But runtime error:

/home/samuel/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bluepill-0.0.68/lib/bluepill/socket.rb:22:in `load': undefined class/module Redis:: (ArgumentError)
        from /home/samuel/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/bluepill-0.0.68/lib/bluepill/socket.rb:22:in `block (3 levels) in client_command'
        from /home/samuel/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:91:in `block in timeout'
        from /home/samuel/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:35:in `block in catch'
        from /home/samuel/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:35:in `catch'
        from /home/samuel/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:35:in `catch'
        from /home/samuel/.rbenv/versions/2.1.2/lib/ruby/2.1.0/timeout.rb:106:in `timeout'

Have you an issue ?

Jruby: NotImplementedError: fork is not available on this platform

My jruby version is jruby-1.7.10.

And my pill file is

require 'rubygems'
require 'bluepill'
require 'logger'

ROOT_DIR = File.dirname(__FILE__)
RAILS_ROOT = File.expand_path("..", ROOT_DIR)

# Watch with
# watch -n0.2 'ps axu | egrep "(CPU|forking|bluepill|sleep)" | grep -v grep | sort'
Bluepill.application(:sidekiq) do |app|
  app.process("sidekiq") do |process|
    process.pid_file = "#{RAILS_ROOT}/tmp/pids/sidekiq.pid"

      # Example of use of pid_command option to find memcached process
      # process.pid_command = "ps -ef | awk '/memcached$/{ print $2 }'"

      # I could not figure out a portable way to
      # specify the path to the sample forking server across the diff developer laptops.
      # Since this code is eval'ed we cannot reliably use __FILE__
    process.start_command = "cd #{RAILS_ROOT} && sidekiq -C config/sidekiq.yml"
    process.stop_command = "sidekiqctl stop #{RAILS_ROOT}/tmp/pids/sidekiq.pid"
    process.daemonize = true

    process.start_grace_time = 1.seconds
    process.restart_grace_time = 7.seconds
    process.stop_grace_time = 7.seconds

    process.uid = "kona"
    process.gid = "kona"

    process.stderr = "#{RAILS_ROOT}/log/sidekiq-bluepill.log"
    process.stdout = "#{RAILS_ROOT}/log/sidekiq-bluepill.log"
      # process.checks :cpu_usage, :every => 10, :below => 0.5, :times => [5, 5]
    process.checks :flapping, :times => 2, :within => 30.seconds, :retry_in => 7.seconds

  end
end

When I run bluepill load config/sidekiq.pill, I get an error

NotImplementedError: fork is not available on this platform
          fork at org/jruby/RubyKernel.java:1886
      safefork at /home/user/.rvm/gems/jruby-1.7.10/gems/daemons-1.1.9/lib/daemons/daemonize.rb:11
     daemonize at /home/user/.rvm/gems/jruby-1.7.10/gems/daemons-1.1.9/lib/daemons/daemonize.rb:93
  start_server at /home/user/.rvm/gems/jruby-1.7.10/gems/bluepill-0.0.66/lib/bluepill/application.rb:131
          load at /home/user/.rvm/gems/jruby-1.7.10/gems/bluepill-0.0.66/lib/bluepill/application.rb:46
   application at /home/user/.rvm/gems/jruby-1.7.10/gems/bluepill-0.0.66/lib/bluepill/dsl.rb:10
        (root) at /home/user/master/web/script/sidekiq.pill:10

Sending (star, stop, status etc.) commands to processes in a process group does not work => ArgumentError

Example:

$ bluepill status
web_interface(pid:32667): up
jobs:
  job_1:IMPORT(pid:6994): up
  job_2:UPDATE(pid:7032): up

$ bluepill status job_1:IMPORT
$ bluepill status jobs:job_1:IMPORT
Received error from server:
#<ArgumentError: wrong number of arguments (3 for 0..2)>
(eval):1:in `status'
/shared/bundle/ruby/2.0.0/gems/bluepill-0.0.68/lib/bluepill/application.rb:102:in `block (3 levels) in start_listener'
/shared/bundle/ruby/2.0.0/gems/bluepill-0.0.68/lib/bluepill/application.rb:41:in `synchronize'
/shared/bundle/ruby/2.0.0/gems/bluepill-0.0.68/lib/bluepill/application.rb:41:in `mutex'
/shared/bundle/ruby/2.0.0/gems/bluepill-0.0.68/lib/bluepill/application.rb:102:in `block (2 levels) in start_listener'
/shared/bundle/ruby/2.0.0/gems/bluepill-0.0.68/lib/bluepill/application.rb:96:in `loop'
shared/bundle/ruby/2.0.0/gems/bluepill-0.0.68/lib/bluepill/application.rb:96:in `block in start_listener'

FIFO and Pipes with Bluepill

I am trying to use a FIFO (named pipe) with Bluepill.

In the console, if I directly run:

tail -f /srv/input.fifo | /usr/lib/jvm/java-7-openjdk-amd64/bin/java -jar server.jar

The server starts fine and begins running. I can also redirect to the pipe, as expected:

echo 'Hello World' > /srv/input.fifo

And the server responds and behaves as intended.

I am trying to port this same functionality over to Bluepill, but I'm having some trouble. Here's the Bluepill application script:

Bluepill.application('server', log_file: '/srv/bluepill.log') do |app|
  app.process('server') do |process|
    process.pid_file = '/var/run/server.pid'
    process.working_dir = '/srv'
    process.start_command = "tail -f /srv/input.fifo | /usr/lib/jvm/java-7-openjdk-amd64/bin/java -jar server.jar"
    process.daemonize = true
  end
end

Bluepill, from the log, reflects the start_command correctly:

WARN -- : [server:server] Executing start command: tail -f /srv/input.fifo | /usr/lib/jvm/java-7-openjdk-amd64/bin/java -jar server.jar

But the server never starts running. ps aux | grep 'server' shows that it isn't running and pidof java returns nothing. Additionally, I can tell from the server's log that it never attempts to run.

If I remove tail -f /srv/input.fifo | from the start_command, the Bluepill process works as expected, although it obviously isn't using the pipe.

Out of desperation, I also tried setting an stdin attribute in the process file because this definition appeared in the repo's code (but not in the documentation), like so:

process.stdin = "/srv/input.fifo"

How can I get Bluepill to call the server using the FIFO?

I originally posted this on StackOverflow, but it hasn't gotten much attention. Given that the README shows a complex implementation with redirection and conditional operation (cd /tmp/some_dir && SOME_VAR=1 /usr/bin/some_start_command > /tmp/server.log 2>&1), this looks to me more like a bug with Bluepill. I see no mention of pipes in the documentation, so it could be that this functionality is not supported.

Let me know! Thank you!

Before and start hook commands

Hello all,

I need some before and start commands on thin execution.
For example, before thin I need to run:

rm -fr public/javascripts/cache*
rm -fr public/stylesheets/cache*

And after I need to run

 renice 20 `cat tmp/pids/thin.50003.pid`

How can I code these after/before scripts?

'mkdir': Permission denied

Using 0.0.69:

$ bluepill load config/services.pill --no-privileged
/usr/share/ruby/2.0/logger.rb:601:in `initialize': Permission denied - /var/app/support/logs/bluepill.log (Errno::EACCES)
    from /usr/share/ruby/2.0/logger.rb:601:in `open'
    from /usr/share/ruby/2.0/logger.rb:601:in `create_logfile'
    from /usr/share/ruby/2.0/logger.rb:596:in `open_logfile'
    from /usr/share/ruby/2.0/logger.rb:551:in `initialize'
    from /usr/share/ruby/2.0/logger.rb:318:in `new'
    from /usr/share/ruby/2.0/logger.rb:318:in `initialize'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/logger.rb:46:in `new'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/logger.rb:46:in `create_logger'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/logger.rb:7:in `initialize'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/application.rb:27:in `new'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/application.rb:27:in `initialize'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/dsl/app_proxy.rb:9:in `new'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/dsl/app_proxy.rb:9:in `initialize'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/dsl.rb:3:in `new'
    from /usr/local/share/ruby/gems/2.0/gems/bluepill-0.0.69/lib/bluepill/dsl.rb:3:in `application'
    from /var/app/current/config/services.pill:5:in `<main>'

Both the gid and uid are set properly for this directory. In config/services.pill:

Bluepill.application('xxxxx', :foreground => false, :log_file => "/var/app/support/logs/bluepill.log") do |app|
  app.working_dir = '/var/app/support/logs'
  app.gid = 'webapp'
  app.uid = 'webapp'

  . . . .

end

And in the console:

$ ls -ld /var/app/support/
drwxr-xr-x 5 webapp webapp 4096 Jul 20 23:43 /var/app/support/

Seems like it might be related to https://github.com/arya/bluepill/issues/144. Any idea what might be going on?

On smartos ps complains about o option

On smartos ps complains about the o option. So this code breaks:

 process.checks :mem_usage, :every => 1.minutes, :below => 750.megabytes, :times => [3, 5]

with ps: illegal option -- o

The code says ps axo pid,ppid,pcpu,rss,etime,command.

This seems to work on smartos:

ps -eo pid,ppid,pcpu,rss,etime,comm

Logging from custom process conditions

I can't seem to figure out how to get access to the Bluepill::Application's logger from within a process condition I'm writing. For example

module Bluepill
  module ProcessConditions
    class MemUsage < ProcessCondition
      def initialize(options = {})
        # Would love a logger method or Bluepill::Application::get_instance method or something here...

Any ideas? Thanks much.

-DB

Bring the wiki

Hi! I think you should not forget to move the wiki contents from the old github repository to this new one.

process.daemonize behavior

If I have the option

process.daemonize = true

configured my start command cannot perform any sort of logging or output to stdout:

#!/usr/bin/env ruby
require 'logger'
class Job
  attr_accessor :logger
  def initialize
    @logger = Logger.new('job.log')
  end
  def log(string)
    @logger.info(string)
    # File.open('job.log', 'w') do |f|
    #   f.write(string)
    # end
    # puts string
  end

  def run
    a = []
    while true; a << 'x' * 1024; end
    log("size: #{a.size / 1024}M")
  end
end

j = Job.new
j.run

I also tried these options:

process.stdout = 'stdout.log'
process.stderr = 'stderr.log'

but did not see any notable change in behavior.

Bluepill swallows unknown process configuraton attributes silently

I ported some configuration over from eye. As there was no error when launching bluepill, I assumed all options are working correctly.

I now discovered I was using a non-existing option inside a process block. As it turns out, bluepill accepts any option given in a process block. But only known options will be used. As a consequence, it is impossible to determine which options are actually being used. It was rather surprising and hard to find out the cause of these problems.

In my opinion, this is actually bad behaviour for a Ruby DSL. A configuration file which contains invalid options should lead to an error instead. I believe this would be rather easy to implement. Please consider adjusting the code accordingly.

That said, thanks for the the work on a very useful project!

TypeError: No implicit conversion of false into Integer

I get the follow error when trying to start/quit a bluepill process that is not yet running yet.

 /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/lib/bluepill/system.rb:23:in `kill' : no implicit conversion of false into Integer (TypeError)
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/lib/bluepill/system.rb:23:in `pid_alive?'
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/lib/bluepill/controller.rb:89:in `block in cleanup_bluepill_directory'
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/lib/bluepill/controller.rb:87:in `each'
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/lib/bluepill/controller.rb:87:in `cleanup_bluepill_directory'
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/lib/bluepill/controller.rb:15:in `initialize'
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/bin/bluepill:77:in `new'
 from /somewhere/shared/bundle/ruby/2.1.0/gems/bluepill-0.0.70/bin/bluepill:77:in `<top (required)>'
 from /somewhere/shared/bundle/ruby/2.1.0/bin/bluepill:23:in `load'
 from /somewhere/shared/bundle/ruby/2.1.0/bin/bluepill:23:in `<main>'

Reading the code, I assume the following test next if pid || System.pid_alive?(pid) (https://github.com/bluepill-rb/bluepill/blob/master/lib/bluepill/controller.rb#L89) should rather use && then ||, right? -> otherwise we're potentially calling pid_alive? with false :/

(Using bluepill 0.70 & ruby 2.1)

Having a changelog

Would be really usefull to reference breaking changes and needed config changes between each version, new features, etc.

Bluepill crashing when checking memory usage

About once a day, one of our webservers will have a failed deploy because its Bluepill daemon has crashed. Inspecting the latest logs, this is the error:

E, [2015-09-03T06:00:31.951188 #24114] ERROR -- : [web:web:web_0] ["/var/lib/gems/1.9.1/gems/bluepill-0.1.1/lib/bluepill/system.rb:227:in ``'", "/var/lib/gems/1.9.1/gems/bluepill-0.1.1/lib/bluepill/system.rb:227:in `ps_axu'", "/var/lib/gems/1.9.1/gems/bluepill-0.1.1/lib/bluepill/system.rb:42:in `memory_usage'", "/var/lib/gems/1.9.1/gems/bluepill-0.1.1/lib/bluepill/process_conditions/mem_usage.rb:15:in `run'", "/var/lib/gems/1.9.1/gems/bluepill-0.1.1/lib/bluepill/condition_watch.rb:28:in `run'", "/var/lib/gems/1.9.1/gems/bluepill-0.1.1/lib/bluepill/process.rb:221:in `block (2 levels) in run_watches'"]

Any idea what could be causing this? The memory usage is set to a fairly sane value:

process.checks :mem_usage, :every => 30.seconds, :below => 512.megabytes, :times => [3, 5]

Comparison of Float with nil failed (mem_usage.rb:20)

If I include this line in my pill file:

process.checks :mem_usage, :every => 30.seconds, :above => 350.megabytes, :times => [3,5]

I get this:

Failed to signal process 14363 with code 0: No such process
Failed to start bluepill: ArgumentError `comparison of Float with nil failed`
~/bundle/ruby/1.9.1/gems/bluepill-0.0.67/lib/bluepill/process_conditions/mem_usage.rb:20:in `<'
~/bundle/ruby/1.9.1/gems/bluepill-0.0.67/lib/bluepill/process_conditions/mem_usage.rb:20:in `check'
~/bundle/ruby/1.9.1/gems/bluepill-0.0.67/lib/bluepill/condition_watch.rb:30:in `run'
~/bundle/ruby/1.9.1/gems/bluepill-0.0.67/lib/bluepill/process.rb:219:in `block (2 levels) in run_watches'

Take the line out and everything seems to work properly again.

Postgres process keeps getting restarting

I am trying to watch the Postgres process on my ec2 instance using sudo bluepill load pg.pill ...but I have noticed that bluepill keeps recycling the Postgres process. Anyone know what I could be doing wrong? I saw the same behavior in God and trying bluepill as an alternative, but I am seeing the same behavior.

When I run sudo bluepill status I see status switching between postgres(pid:): starting and postgres(pid:): down

This is the message in the logs...

[warning]: [postgresql:postgres] pid_file /tmp/postgres.pid does not exist or cannot be read
[warning]: [postgresql:postgres] pid_file /tmp/postgres.pid does not exist or cannot be read
[err]: [postgresql:postgres] Failed to signal process  with code 0: no implicit conversion from nil to integer
# pg.pill
Bluepill.application('postgresql') do |app|
  app.process('postgres') do |process|
    process.start_command = 'sudo service postgresql restart'
    process.stop_command = 'sudo service postgresql stop'
    process.pid_file = '/tmp/postgres.pid'
    process.start_grace_time = 20.seconds
    process.stop_grace_time = 20.seconds
    process.restart_grace_time = 20.seconds
  end
end

Create Zombie process on ruby 2.1.0 after start

I'm using rbenv ruby 2.1.0, bluepill 0.0.68 with this pill file:

Bluepill.application("resque") do |a|
  a.working_dir = "/path/to/current"
  a.process("resque") do |p|
    p.start_command = "bundle exec rake workers:start RAILS_ENV=production WORKERS_ENV=true"
    p.pid_file = "/path/to/shared/pids/workers.pid"
    p.uid = "deploy"
    p.gid = "deploy"
  end
end

Everytime I run a bluepill restart resque, the process restart properly but it creates a zombie child process: http://cl.ly/image/0V3o0U1V3015

What is the issue with those pid jornals?

Bluepill now uses 'journals' to keep track of past PIDs and kill them on startup (lib/bluepill/process_journal.rb).

The problem is cases like

  • Systems goes down for whatever reason, the journal still being around
  • System comes back up, and tries to restart the service
  • The pids in the journal may now belong to system processes
  • Since bluepill doesn't run w/ root privileges for me, it bails out at this point

Pids may also overflow and get reused during normal operations. In short, a pid is not unique to a process forever.

I appreciate what this feature is trying to solve. Still, I don't appreciate having a software on my production setup that tries to randomly kill processes after a reboot.

Dependency problem with activesupport

Current dependency on activesupport is:
spec.add_dependency 'activesupport', ['>= 3.2', '< 5']
Unfortunately this causes a problem due to activesupport 5.0.0beta2 being consider as < 5. During gem install bluepill one can get a complaint that activesupport now requires Ruby >= 2.2.2. Therefore with fresh install (without activesupport being installed prior to bluepill), this causes problems on Rubies < 2.2.2 where bluepill will simply not install.

Custom logging broken?

Bluepill.application("httpd", log_file: "$HOME/opt/logs/bluepill.log") do |app|
# Also tried below syntax:
#Bluepill.application("httpd", :log_file => "$HOME/opt/logs/bluepill.log") do |app|

The bluepill daemon still comes online just fine, seems to be able to stop/start/restart the service the same as before, but I'm still getting the same error when I try to tail the logs:

$ bluepill --no-privileged httpd log
Tailing log for ...
tail: cannot open `/var/log/bluepill.log' for reading: No such file or directory
tail: no files remaining

Any suggestions are appreciated. I'm working in an environment where sudo is unavailable and bluepill seems the best monitoring tool for that situation. Being able to see a log of what's going on is always a plus though ;)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.