我试图使用Upstart来管理Rails应用程序的Unicorn主进程.这是我目前的/etc/init/app.conf:
description "app" start on runlevel [2] stop on runlevel [016] console owner # expect daemon script APP_ROOT=/home/deploy/app PATH=/home/deploy/.rbenv/shims:/home/deploy/.rbenv/bin:$PATH $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1 end script # respawn
那工作很好 – 独角兽开始很棒.检测到的PID不是独角兽的主人,这不是很好,这是一个很好的过程.这本身并不是那么糟糕,无论是 – 如果我没有使用自动独角兽零停机部署策略.因为不久之后,我将-USR2发送到我的独角兽主人,一个新的主人产生了,老一个死亡…以及sh进程.所以Upstart认为我的工作已经死了,如果我想要的话,我不能再重新启动,或者停止它.
我已经玩了配置文件,试图添加-D到Unicorn行(像这样:$APP_ROOT / bin / unicorn -c $APP_ROOT / config / unicorn.rb -E production -D)来守护独角兽,以及我添加了expect守护进程,但是也没有.我也试过期望叉.所有这些事情的各种组合可能导致启动和停止挂起,然后Upstart对工作的状态感到困惑.然后我必须重新启动机器来解决它.
我认为Upstart在检测何时/如果Unicorn正在进行分支时遇到问题,因为我在$APP_ROOT / bin / unicorn脚本中使用rbenv ruby-local-exec shebang.这里是:
#!/usr/bin/env ruby-local-exec # # This file was generated by Bundler. # # The application 'unicorn' is installed as part of a gem,and # this file is here to facilitate running it. # require 'pathname' ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",Pathname.new(__FILE__).realpath) require 'rubygems' require 'bundler/setup' load Gem.bin_path('unicorn','unicorn')
此外,ruby-local-exec脚本如下所示:
#!/usr/bin/env bash # # `ruby-local-exec` is a drop-in replacement for the standard Ruby # shebang line: # # #!/usr/bin/env ruby-local-exec # # Use it for scripts inside a project with an `.rbenv-version` # file. When you run the scripts,they'll use the project-specified # Ruby version,regardless of what directory they're run from. Useful # for e.g. running project tasks in cron scripts without needing to # `cd` into the project first. set -e export RBENV_DIR="${1%/*}" exec ruby "$@"
所以在那里有一个我很担心的执行官.它引发了一个Ruby进程,它引发了独角兽,它可能或可能不会守护自己,这一切都是从一个sh进程首先发生的,这使我非常怀疑Upstart跟踪所有这些废话的能力.
是我甚至想做甚么吗根据我的理解,Upstart中的预期节只能被告知(通过守护进程或分支)最多可以预计两个分叉.
解决方法
在unix地区,有两个关键的系统调用便于运行程序:fork
和exec
.
fork复制调用它的进程.一个进程调用fork,并将控制返回到两个进程.每个进程必须从fork返回的值中确定它是(父或子)(请参阅手册页的详细信息).
exec运行一个新程序,替换调用exec的进程.
当你在shell中运行一个命令时,shell会调用fork来创建一个具有自己id的新进程,而这个新进程(在一些安装之后)立即调用exec来启动你输入的命令.这是大多数程序运行的方式,无论是通过shell还是窗口管理器或其他方式.
请参阅C中的system函数,该函数在大多数脚本语言中也有变体.
如果你认为这是低效的,你可能是对的.这是自从几天以来在unix中已经完成的,而且apparnetly没有人是改变它的游戏.其中一个原因是有很多事情没有被替换,包括(有时)打开的文件,以及进程的用户和组ID.
另一个原因是花费了大量的努力,使叉子效率高,并且实际上做得很好 – 在现代unixes(在cpu的帮助下)fork实际上很少复制过程.我猜没有人想把所有的工作都丢掉.
而且,(暂停效果)进程pid.
展示:
mslade@mickpc:~$echo $$ 3652 mslade@mickpc:~$bash mslade@mickpc:~$echo $$ 6545 mslade@mickpc:~$exec bash mslade@mickpc:~$echo $$ 6545 mslade@mickpc:~$exit exit mslade@mickpc:~$echo $$ 3652
大多数流行语言具有fork和exec的变体,包括shell,C,perl,ruby和python.但不是java.
所以考虑到这一点,你需要做什么来使你的新手工作工作确保它重新启动与启动者认为相同的次数.
ruby-local-exec中的exec行实际上是一件好事,它阻止了fork.另外加载不会启动一个新的进程,它只是将代码加载到现有的ruby解释器并运行它.
但是你的shell脚本在这行中分叉:
$APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1
为了防止这种情况,您可以将其更改为
exec $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1
如果你这样做,AFAICT独角兽不应该分叉,你不需要告诉upstart期待一个叉子.