rails4+sidekiq+foreman+unicorn+systemd ubuntu 16.04 デプロイ


背景

sidekiqを利用するとプロセスを複数使うので、foremanで管理すると楽(と思ったが、exportでうまくsystemdのサービスができず結局最後は自分で書いた)。sidekiqの設定は、公式ホームページやRailsCastを参考に。

目標:以前までは、unicorn+capistrano+ubuntuでDeployしていたのをForemanを本番環境で使うようにする!(予定だった)

foremanのインストールとローカルでの使用方法

0. install

Gemfile
gem 'foreman'

bundleでインストール完了

1. Procfileを以下のように書く

Procfile
redis: redis-server
web: rails s
worker: bundle exec sidekiq

2. foreman起動

foreman start

deployする

参考にするのは、foremanを使ってRailsのActive Job(Sidekiq)やその他の常駐プロセスの設定をする

capistrano-foreman (https://github.com/surzycki/capistrano-foreman) を確認

  1. Capfileに capistrano-foremanを追加

    require 'capistrano/foreman'
    
  2. cap <env> foreman:exportを実行。

    <env>には、productionの場合には、そのままproductionと書く。

  3. foremanでsystemdをexportするScriptを使うとうまくいかない。

    1. https://github.com/aserafin/capistrano-foreman-systemd にあるので、試してみた(が‥)
    2. PIDFileがserviceに書けない
    3. コマンドが、/bin/bash -lc '...'となりこのせいかわからないが、動かない。
    4. 手動で、StartExecとPIDFileを編集すると、スタートできた
    5. 結論:Foremanに頼らず自分でServiceのTemplateを書いてDeployしたほうがいい

手動で作る

foreman exportがしてくれるのは、Procfile → Serviceのファイル。 手動で作る場合は、serviceを作るスクリプトとserviceのテンプレートを用意すれば良い。

今回は、sidekiqを例にとってつくる。

  1. sidekiq.service.erb
  2. sidekiq.target.erb
  3. app.target.erb
  4. make_service.rake

sidekiq.service.erb

serviceのテンプレート

lib/capistrano/templates/sidekiq.erb
[Unit]
PartOf=<%= fetch :application %>.target 

[Service]
SyslogIdentifier=sidekiq-<%= fetch :application %>
User=deploy
WorkingDirectory=<%= current_path %>
Type=forking
PIDFile=<%= current_path %>/tmp/pids/sidekiq.pid
ExecStart=/home/deploy/.rbenv/bin/rbenv exec bundle exec sidekiq -c 5 -e <%= fetch :rails_env %> -P <%= shared_path %>/tmp/pids/sidekiq.pid -d -L log/sidekiq.log >> log/sidekiq.log 2>&1
ExecStop=/home/deploy/.rbenv/bin/rbenv exec bundle exec sidekiqctl stop <%= current_path %>/tmp/pids/sidekiq.pid >> <%= current_path %>/log/sidekiq.log 2>&1

[Install]
WantedBy=multi-user.target

sidekiq.target.erb

sidekiq.targetのテンプレート。targetはService間の関係を表す

lib/capistrano/templates/sidekiq.target.erb
[Unit]
PartOf=<%= fetch(:application) %>.target
Wants=sidekiq_<%= fetch(:application) %>.service

serviceをデプロイするスクリプト

(foreman_systemdのスクリプトを変更したのでforeman_systemdがそのままになっている)

make_service.rake
namespace :foreman_systemd do
  desc 'Setup the application services'
  task :setup_service do
    on roles fetch(:foreman_systemd_roles) do
      %w[thrift sidekiq].each do |app_type| ##thriftとsidekiq両方作った 
        service = "#{app_type}_#{fetch(:application)}"
        execute "mkdir -p #{shared_path}/config"
        template "#{app_type}.service.erb", "/tmp/#{app_type}.service"
        dest = "/etc/systemd/system/#{app_type}_#{fetch(:application)}.service"
        sudo "mv /tmp/#{app_type}.service #{dest}"

        template "#{app_type}.target.erb", "/tmp/#{app_type}.target"
        dest = "/etc/systemd/system/#{app_type}_#{fetch(:application)}.target"
        sudo "mv /tmp/#{app_type}.target #{dest}"

        status = capture "sudo systemctl is-enabled #{service} | cat"
        sudo "systemctl enable #{service}" if status == 'disabled'
      end
      template 'app.target.erb', '/tmp/app.target' #これで2つのサービスを管理する
      dest = "/etc/systemd/system/#{fetch(:application)}.target"
      sudo "mv /tmp/app.target #{dest}"
      service = "#{fetch(:application)}.target"
      status = capture "sudo systemctl is-enabled #{service} | cat"
      sudo "systemctl enable #{service}" if status == 'disabled'
      sudo 'systemctl daemon-reload'
      sudo "systemctl start #{service}"
    end
  end

  desc 'Start the application services'
  task :start do
    on roles fetch(:foreman_systemd_roles) do
      sudo :systemctl, "start #{fetch(:application)}.target"
    end
  end

  desc 'Stop the application services'
  task :stop do
    on roles fetch(:foreman_systemd_roles) do
      sudo :systemctl, "stop #{fetch(:application)}.target"
    end
  end

  desc 'Restart the application services'
  task :restart do
    on roles fetch(:foreman_systemd_roles) do
      sudo :systemctl, "restart #{fetch(:application)}.target"
    end
  end

end

コマンド

cap staging foreman_systemd:setup_service
cap staging foreman_systemd:start # start/stop/restart

問題

foremanはProcfileを変更したときに、コマンドを打つだけで、変更がServiceに更新されて便利だが、自分で書くと、変更を自分で更新しないといけないし、Foremanを使う意味がほぼ無くなる。Procfileを変更してもDevで起動する時に、foreman startで起動できるくらいのメリットしかない。