部署 Unicorn 怎才做才能正确的无缝重启?

huacnlee · 2011年11月22日 · 最后由 geekontheway 回复于 2012年02月25日 · 11138 次阅读
本帖已被管理员设置为精华贴

目前我的用法是:

$ cat cat tmp/pids/unicorn.pid
25361
$ kill -USR2 25361

但是,我发现这样做了以后,进程列表会出现两份,新的是启动上来了,但是老的进程依然存在,除非我再 kill -QUIT 25361 这个进程

我这个方法不知道对不对

需要改下配置:

# 如果用USR2信号重启服务,必须要配置这一步, 用来退出老的master
# 请参考:
#   http://unicorn.bogomips.org/SIGNALS.html
#   https://github.com/blog/517-unicorn
before_fork do |server, worker|
  old_pid = "#{shared_path}/pids/unicorn.pid.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      puts "Send 'QUIT' signal to unicorn error!"
    end
  end
end

@huacnlee 其实这个问题在我的本机开发中我也遇到了,你的做法是对的,先用 USR2,当新的 master process 启动好了以后,需要给原有的 master process 发送一个 QUIT singal,对于这个流程官方网站解释的非常清楚: http://unicorn.bogomips.org/SIGNALS.html (Procedure to replace a running unicorn executable)

关于自动化这个流程,可以参见 Unicorn 的官方 example: https://github.com/defunkt/unicorn/blob/master/examples/init.sh

btw: 我用

pstree -s unicorn

来查看 unicorn 的新老进程的状态

_pid=cat tmp/pids/unicorn.pid; sudo kill -USR2 $_pid; sudo kill -QUIT $_pid; ps -ef | grep unicorn | grep master

#1 楼 @liuzihua #2 楼 @lgn21st 我如何在自动话脚本里面判断进程是否启动好了呢?换句话说什么时候杀之前那个 old master 进程比较好?

现在 Thin 无缝重启的机制是杀一个,启动一个,第一个进程完全启动好以后再杀第二个,启第二个,同样继续等待第二个启动好以后在杀第三个,启动第三个

stop 5000 
start 5000...
stop 5001
start 5001...

#4 楼 @huacnlee 在官方提供的 init.sh 脚步中,是这样判断的:

upgrade)
    if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
    then
        n=$TIMEOUT
        while test -s $old_pid && test $n -ge 0
        do
            printf '.' && sleep 1 && n=$(( $n - 1 ))
        done
        echo

        if test $n -lt 0 && test -s $old_pid
        then
            echo >&2 "$old_pid still exists after $TIMEOUT seconds"
            exit 1
        fi
        exit 0
    fi
    echo >&2 "Couldn't upgrade, starting '$CMD' instead"
    $CMD
    ;;

不过我还没有在本机测试过,我现在仍然采用手杀,晚上找个时间我试试看。

#2 楼 @lgn21st 那个 init.d 脚本看起来是这样的

kill -USR2 oldpid
sleep 2 # 等待了两秒
kill -QUIT oldpid

但是,大一点的项目,两秒是不准确的,有时候 2 两秒都没启动完成,而且没有预热,刚启动好那段时间,所有进程占用 CPU 一下上去 另外还有一点,这个过程中有段时间进程数是翻倍的,unicorn_rails 暂用的内存将会多出本应该占有内存的 50% 左右

就是用一楼的办法呀,before_fork 就是在新的 master 已经加载好环境,准备 fork worker 的时候调用的

多服务器的话挨个杀了重启也无缝...

蛋疼的"内存占用也平滑"法: 让发减员信号给老 master, 起进程数稍少的新 master, 起来后杀老 master, 然后发增员信号给新 master

谁之前用得熟悉的,看看 ruby-china 的 unicorn.rb 写得对不对 https://github.com/huacnlee/ruby-china/blob/master/config/unicorn.rb

#9 楼 @huacnlee 我刚刚测试了一下 cap deploy:restart,重启后已经能够自动给 old 的 master process 发送 QUIT signal 了,就是说做到了你要的无缝重启。

其实 #1 楼@liuzihua 同学的方案是对的,但是更新了 config/unicorn.rb 后,服务器上的 unicorn.rb 需要手动更新

www/ruby-china/shared/config/unicorn.rb

#10 楼 @lgn21st 哦,是我忘了更新 shared 那个文件

unicorn.rb : 为什么既 listen port 又 listen socket, 其中一个可以去掉吧,和 nginx 的配置对应就可以了

timeout 设短一点,假如 6 个人同时点了用 google 登录,每个进程都在 wait google response, 就要死 120 秒了

@huacnlee @lgn21st 我看了一下 ruby-china 的部署脚本

desc "Restart Application"
  task :restart, :roles => :app do
    run "kill -USR2 `cat #{deploy_to}/current/tmp/pids/unicorn.pid`"
  end

杀掉 unicorn 的进程后 unicorn 会自动重启吗

@huacnlee @lgn21st sorry,原来是这样的

before_fork do |server, worker|
  old_pid = "#{Rails.root}/tmp/pids/unicorn.pid.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      puts "Send 'QUIT' signal to unicorn error!"
    end
  end
end
需要 登录 后方可回复, 如果你还没有账号请 注册新账号