unicorn を Server::Starter 経由でhot deployを実現する

blog.livedoor.jp

上記の記事を参考にServer::Starter 経由でhot deployに対応したのでその記録です。

内容

Server::Starter をinstall

$ brew install cpanminus
$ sudo cpanm Server::Starter

$ ↪ cpanm Server::Starter
Server::Starter is up to date. (0.35)

$ perldoc -l Server::Starter
/usr/local/Cellar/perl/5.30.0/lib/perl5/site_perl/5.30.0/Server/Starter.pm

$ ↪ /usr/local/Cellar/perl/5.30.0/bin/start_server
server program not specified

Hello World! を返却するシンプルなアプリケーションファイルを記述。

  • config.ru
require 'rubygems'
require 'sinatra/base'

#worker_processes 2

class HelloApp < Sinatra::Base
  get '/' do
    'Hello World!'
  end
end
run HelloApp
# frozen_string_literal: true

worker_processes 8
timeout 30
preload_app true

current_dir = "./simple-unicorn"
working_directory current_dir

listen "#{current_dir}/tmp/sockets/unicorn.sock", backlog: 2048

if ENV.key?('SERVER_STARTER_PORT')
  fds = ENV['SERVER_STARTER_PORT'].split(';').map do |x|
    _path_or_port, fd = x.split('=', 2)
    fd
  end
  ENV['UNICORN_FD'] = fds.join(',')
  ENV.delete('SERVER_STARTER_PORT')
end

before_fork do |server, worker|
  $stdout.puts "before fork"
  # Throttle the master from forking too quickly by sleeping.  Due
  # to the implementation of standard Unix signal handlers, this
  # helps (but does not completely) prevent identical, repeated signals
  # from being lost when the receiving process is busy.
  sleep 1
end

status_file = "#{current_dir}/server.status"
after_fork do |server, worker|
  $stdout.puts "after fork"
  pids = File.readlines(status_file).map { |line| line.chomp.split(':') }.to_h
  old_gen = ENV['SERVER_STARTER_GENERATION'].to_i - 1

  if old_pid = pids[old_gen.to_s]
    begin
      sig = ((worker.nr + 1) >= server.worker_processes) ? :QUIT : :TTOU
      $stdout.puts "sig: #{sig}, old_pid.to_i: #{old_pid.to_i}"
      Process.kill(sig, old_pid.to_i)
    rescue Errno::ENOENT, Errno::ESRCH # rubocop:disable Lint/HandleExceptions
    end
  else
    $stdout.puts "not condition(old_pid = pids[old_gen.to_s]): #{old_pid = pids[old_gen.to_s]}"
  end
end

before_exec do |_|
  ENV['BUNDLE_GEMFILE'] = "#{current_dir}/Gemfile"
end

Server::Starter 経由でunicornの起動

↪ /usr/local/Cellar/perl/5.30.0/bin/start_server --path=/simple-unicorn/tmp/sockets/unicorn.sock --signal-on-term=QUIT --signal-on-hup=QUIT --status-file=/simple-unicorn/server.status --pid-file=/simple-unicorn/tmp/pids/unicorn.pid --kill-old-delay=5 -- bundle exec --keep-file-descriptors unicorn -c config/unicorn.conf config.ru
start_server (pid:61975) starting now...
starting new worker 61977
I, [2020-12-24T09:10:31.291689 #61977]  INFO -- : inherited addr=/simple-unicorn/tmp/sockets/unicorn.sock fd=4
I, [2020-12-24T09:10:31.291816 #61977]  INFO -- : Refreshing Gem list

unicornのpidを確認

↪ ps aux | grep unicorn
<user> 62057   0.0  0.0  4278320    872 s012  S+    9:10AM   0:00.00 grep --color=auto unicorn
<user> 62055   0.0  0.0  4364876   2080 s011  S+    9:10AM   0:00.00 unicorn worker[7] -c config/unicorn.conf config.ru
<user> 62054   0.0  0.0  4364876   2004 s011  S+    9:10AM   0:00.00 unicorn worker[6] -c config/unicorn.conf config.ru
<user> 62051   0.0  0.0  4364876   2080 s011  S+    9:10AM   0:00.00 unicorn worker[5] -c config/unicorn.conf config.ru
<user> 62043   0.0  0.0  4364876   2020 s011  S+    9:10AM   0:00.00 unicorn worker[4] -c config/unicorn.conf config.ru
<user> 62036   0.0  0.0  4364876   2132 s011  S+    9:10AM   0:00.00 unicorn worker[3] -c config/unicorn.conf config.ru
<user> 62026   0.0  0.0  4373068   2144 s011  S+    9:10AM   0:00.00 unicorn worker[2] -c config/unicorn.conf config.ru
<user> 62009   0.0  0.0  4364876   2144 s011  S+    9:10AM   0:00.00 unicorn worker[1] -c config/unicorn.conf config.ru
<user> 61991   0.0  0.0  4364876   2176 s011  S+    9:10AM   0:00.00 unicorn worker[0] -c config/unicorn.conf config.ru
<user> 61977   0.0  0.1  4365132  24988 s011  S+    9:10AM   0:00.45 unicorn master -c config/unicorn.conf config.ru
<user> 61975   0.0  0.1  4327700  11608 s011  S+    9:10AM   0:00.08 /usr/local/Cellar/perl/5.30.0/bin/start_server --path=/simple-unicorn/tmp/sockets/unicorn.sock --signal-on-term=QUIT --signal-on-hup=QUIT --status-file=/simple-unicorn/server.status --pid-file=/simple-unicorn/tmp/pids/unicorn.pid --kill-old-delay=5 -- bundle exec --keep-file-descriptors unicorn -c config/unicorn.conf config.ru

worker_processes が8なのを確認できるので、Server::Starter にHUPシグナルを送信しhot deployを行う。

$ kill -HUP 61975

プロセスを確認。

↪ ps aux | grep unicorn
<user> 62346   0.0  0.0  4293680    864 s012  S+    9:15AM   0:00.00 grep --color=auto unicorn
<user> 62344   0.0  0.0  4380236   2384 s011  S+    9:15AM   0:00.00 unicorn worker[1] -c config/unicorn.conf config.ru
<user> 62340   0.0  0.0  4380236   2268 s011  S+    9:15AM   0:00.00 unicorn worker[0] -c config/unicorn.conf config.ru
<user> 62331   0.0  0.2  4380492  25348 s011  S+    9:15AM   0:00.36 /usr/local/bin/unicorn -c config/unicorn.conf config.ru
<user> 62051   0.0  0.0  4364876   2124 s011  S+    9:10AM   0:00.01 unicorn worker[5] -c config/unicorn.conf config.ru
<user> 62043   0.0  0.0  4364876   2080 s011  S+    9:10AM   0:00.01 unicorn worker[4] -c config/unicorn.conf config.ru
<user> 62036   0.0  0.0  4364876   2172 s011  S+    9:10AM   0:00.01 unicorn worker[3] -c config/unicorn.conf config.ru
<user> 62026   0.0  0.0  4373068   2220 s011  S+    9:10AM   0:00.00 unicorn worker[2] -c config/unicorn.conf config.ru
<user> 62009   0.0  0.0  4364876   2184 s011  S+    9:10AM   0:00.00 unicorn worker[1] -c config/unicorn.conf config.ru
<user> 61991   0.0  0.0  4364876   2188 s011  S+    9:10AM   0:00.00 unicorn worker[0] -c config/unicorn.conf config.ru
<user> 61977   0.0  0.1  4365132  24996 s011  S+    9:10AM   0:00.45 unicorn master -c config/unicorn.conf config.ru
<user> 61975   0.0  0.1  4327700  11616 s011  S+    9:10AM   0:00.08 /usr/local/Cellar/perl/5.30.0/bin/start_server --path=/simple-unicorn/tmp/sockets/unicorn.sock --signal-on-term=QUIT --signal-on-hup=QUIT --status-file=/simple-unicorn/server.status --pid-file=/simple-unicorn/tmp/pids/unicorn.pid --kill-old-delay=5 -- bundle exec --keep-file-descriptors unicorn -c config/unicorn.conf config.ru

プロセスを確認し新規worker(6234462340)が立ち上がっていることが確認できる。 この状態で、status fileも確認すると古い世代とmaster、新しい世代とmasterの両方を確認。

↪ cat /simple-unicorn/server.status
1:61977
2:62331
↪ ps aux | grep unicorn
<user> 62363   0.0  0.0  4287536    884 s012  S+    9:15AM   0:00.00 grep --color=auto unicorn
<user> 62360   0.0  0.0  4380236   2208 s011  S+    9:15AM   0:00.00 unicorn worker[7] -c config/unicorn.conf config.ru
<user> 62359   0.0  0.0  4380236   2412 s011  S+    9:15AM   0:00.00 unicorn worker[6] -c config/unicorn.conf config.ru
<user> 62358   0.0  0.0  4380236   2260 s011  S+    9:15AM   0:00.00 unicorn worker[5] -c config/unicorn.conf config.ru
<user> 62357   0.0  0.0  4380236   2400 s011  S+    9:15AM   0:00.00 unicorn worker[4] -c config/unicorn.conf config.ru
<user> 62351   0.0  0.0  4380236   2364 s011  S+    9:15AM   0:00.00 unicorn worker[3] -c config/unicorn.conf config.ru
<user> 62350   0.0  0.0  4380236   2440 s011  S+    9:15AM   0:00.00 unicorn worker[2] -c config/unicorn.conf config.ru
<user> 62344   0.0  0.0  4380236   2412 s011  S+    9:15AM   0:00.00 unicorn worker[1] -c config/unicorn.conf config.ru
<user> 62340   0.0  0.0  4380236   2268 s011  S+    9:15AM   0:00.00 unicorn worker[0] -c config/unicorn.conf config.ru
<user> 62331   0.0  0.2  4380492  25348 s011  S+    9:15AM   0:00.37 unicorn master -c config/unicorn.conf config.ru
<user> 61975   0.0  0.1  4327700  11616 s011  S+    9:10AM   0:00.08 /usr/local/Cellar/perl/5.30.0/bin/start_server --path=/simple-unicorn/tmp/sockets/unicorn.sock --signal-on-term=QUIT --signal-on-hup=QUIT --status-file=/simple-unicorn/server.status --pid-file=/simple-unicorn/tmp/pids/unicorn.pid --kill-old-delay=5 -- bundle exec --keep-file-descriptors unicorn -c config/unicorn.conf config.ru

時間が経経過し、masterのpidが 61975 から 62331に代わり入れ替わっていることが確認できる。

statusファイルも次世代と新規masterに更新されていることも確認できた。

↪ cat /simple-unicorn/server.status
2:62331

おわり。