使用Upstart管理Unicorn w/rbenv + bundler binstubs w/ruby​​-local-exec shebang

cod*_*ger 9 ruby ruby-on-rails upstart unicorn

好吧,这正在融化我的大脑.这可能与我不理解Upstart以及我应该理解的事实有关.提前抱歉,这个问题很长.

我正在尝试使用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
Run Code Online (Sandbox Code Playgroud)

这很好 - 独角兽开始很棒.不太好的是,检测到的PID不是独角兽大师,而是一个sh过程.这本身并不是那么糟糕 - 如果我没有使用自动化的Unicorn零停机时间部署策略.因为我发送-USR2给我的独角兽大师后不久,一个新的主人产生了,旧的主人死了......这个sh过程也是如此.所以Upstart认为我的工作已经死了,如果我愿意,我不能再用它重新启动restart或停止它stop.

我玩过配置文件,试图将-D添加到Unicorn线(如下$APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production -D:)以守护Unicorn,我添加了这expect daemon条线,但这也没有用.我也试过expect fork了.所有这些事情的各种组合都可能导致startstop挂起,然后Upstart对工作状态感到困惑.然后我必须重新启动机器来修复它.

我认为Upstart在检测何时/如果Unicorn正在分叉时遇到问题,因为我ruby-local-exec在我的$APP_ROOT/bin/unicorn脚本中使用了rbenv + 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')
Run Code Online (Sandbox Code Playgroud)

此外,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 "$@"
Run Code Online (Sandbox Code Playgroud)

所以exec我在那里担心.它启动了一个Ruby进程,它启动了Unicorn,它可能会或可能不会自我守护,这一切都是从一个sh进程发生的......这让我严重怀疑Upstart跟踪所有这些废话的能力.

我正在努力做甚么可能吗?根据我的理解,expectUpstart中的节只能告诉(通过daemonfork)期望最多两个叉子.

Mic*_*ade 15

您的新贵作业需要进行配置,以便新贵真正知道它分叉的次数.它只能分叉一次或两次,不能再分叉.

在unix land中,有两个关键的系统调用,便于运行程序:forkexec.

fork复制调用它的进程.一个进程调用fork,它将控制权返回给两个进程.每个进程必须从fork返回的值中识别它(父对象或子对象)(有关详细信息,请参见手册页).

exec运行一个新程序,替换调用的进程exec.

当你只是在shell中运行一个命令时,shell调用fork创建一个具有自己id的新进程,并且新进程(在某些设置之后)立即调用exec以启动你键入的命令.这是大多数程序运行的方式,无论是shell还是窗口管理器或其他任何程序.请参阅C中的系统函数,该函数在大多数脚本语言中也有变体.

如果你认为它效率低下,你可能是对的.这就是自从以前几天在unix中完成它的方式,而且很明显没人会改变游戏.其中一个原因是有很多东西没有被替换exec,包括(有时)打开的文件,以及进程的用户和组ID.

另一个原因是花了很多精力来提高fork效率,而且他们实际上做得非常好 - 在现代的unix中(在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
Run Code Online (Sandbox Code Playgroud)

大多数流行语言都有fork和exec的变体,包括shell,C,perl,ruby和python.但不是java.

因此,考虑到所有这一切,您需要做的是让新手工作正常工作,确保它与upstart认为的那样分叉次数相同.

execRuby中,本地高管行实际上是一件好事,它可以防止叉.也load没有启动新进程,它只是将代码加载到现有的ruby解释器中并运行它.

但是你的shell脚本在这行中分叉:

$APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1
Run Code Online (Sandbox Code Playgroud)

为了防止这种情况,您可以将其更改为

exec $APP_ROOT/bin/unicorn -c $APP_ROOT/config/unicorn.rb -E production # >> /tmp/upstart.log 2>&1
Run Code Online (Sandbox Code Playgroud)

如果你这样做,AFAICT独角兽根本不应该分叉,你也不需要告诉新贵要求分叉.