CentOS7 + unicorn + nginx 設定メモ

## 環境
- CentOS Linux release 7.1.1503
- Ruby on Rails 5.0.0.1
- unicorn_rails v5.1.0
- nginx/1.10.1

## nginxのインストール

### CentOS6

```
$ sudo rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
$ sudo yum install -y nginx
```

### CentOS7

```
$ sudo yum install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
$ sudo yum install --enablerepo=nginx nginx
```

### 設定ファイル編集
{appname}の部分は任意。
/etc/nginx/conf.d/{appname}.conf

下記は最低限の設定。これだけでとりあえずrailsは動く。

```/etc/nginx/conf.d/{appname}.conf
upstream rails-unicorn {
    # nginxのソケット保存先指定
    server unix:/home/vagrant/{appname}/tmp/sockets;
}

server {
    # port
    listen       80;
    # サーバーのIPアドレス
    server_name  192.168.33.10;

    # DocumentRoot
    root   /home/vagrant/{appname}/public;
    try_files $uri $uri.html $uri/index.html @rails-unicorn;

    # charset
    charset utf8;
    # logs
    access_log  /var/log/nginx/{appname}/{appname}.access.log  main;
    error_log  /var/log/nginx/{appname}/{appname}.error.log  error;

    location @rails-unicorn {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded_For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://rails-unicorn;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

## nginxの実行ユーザー変更
実行ユーザーをnginxからvagrantに変更
/etc/nginx/nginx.conf

```lang:/etc/nginx/nginx.conf
user  vagrant;

(*snip*)
```

## nginx起動

```
# 自動起動有効
$ sudo systemctl enable nginx
# 起動
$ sudo systemctl start nginx
```


## unicronのインストール

Gemfileに下記追記

```lang:Gemfile
gem 'unicorn'
```

bundle install

```
$ bundle install --path vendor/bundle
```

configの下にunicorn.rbというファイルを作成する。
引用元：[Unicorn設定のまとめ](http://qiita.com/syou007/items/555062cc96dd0b08a610#unicornの設定ファイル)

```config/unicorn.rb
# Railsのルートパスを求める。(RAILS_ROOT/config/unicorn.rbに配置している場合。)
rails_root = File.expand_path('../../', __FILE__)
# RAILS_ENVを求める。（RAILS_ENV毎に挙動を変更したい場合に使用。今回は使用しません。)
rails_env = ENV['RAILS_ENV'] || "development"

# 追記に記載してます。入れた方がいいです。
ENV['BUNDLE_GEMFILE'] = rails_root + "/Gemfile"

# Unicornは複数のワーカーで起動するのでワーカー数を定義
# サーバーのメモリなどによって変更すること。
#worker_processes
#Unicornは、リクエストを振り分ける一つのmasterプロセスと、実際にリクエストを処理する複数のworkerプロセスで構成されるアーキテクチャになっている。
#worker_processesはworkerプロセスの数を指定する。
#実際のアプリのメモリ使用量などを見ながらworker数を調節できるように、環境変数を指定できるようにしてあるとのこと。
worker_processes 2

# 指定しなくても良い。
# Unicornの起動コマンドを実行するディレクトリを指定します。
# （記載しておけば他のディレクトリでこのファイルを叩けなくなる。）
working_directory rails_root

# 接続タイムアウト時間
timeout 30

# Unicornのエラーログと通常ログの位置を指定。
stderr_path File.expand_path('../../log/unicorn_stderr.log', __FILE__)
stdout_path File.expand_path('../../log/unicorn_stdout.log', __FILE__)

# Nginxで使用する場合は以下の設定を行う。
listen File.expand_path('../../tmp/sockets/unicorn.sock', __FILE__)
# とりあえず起動して動作確認をしたい場合は以下の設定を行う。
listen 8080
# ※「backlog」や「tcp_nopush」の設定もあるけど、よくわかって無い。

# プロセスの停止などに必要なPIDファイルの保存先を指定。
pid File.expand_path('../../tmp/pids/unicorn.pid', __FILE__)

# 基本的には`true`を指定する。Unicornの再起動時にダウンタイムなしで再起動が行われる。
preload_app true
# 効果なしとの記事を見たので、コメントアウト。
# GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true

# USR2シグナルを受けると古いプロセスを止める。
# 後述するが、記述しておくとNginxと連携する時に良いことがある。
before_fork do |server, worker|
  defined?(ActiveRecord::Base) and
      ActiveRecord::Base.connection.disconnect!

  old_pid = "#{server.config[:pid]}.oldbin"
  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
```

## unicorn起動

```
$ bundle exec unicorn_rails -c config/unicorn.rb -D -E development
```

## unicorn起動スクリプト例

```rb:lib/tasks/unicorn.rake
namespace :unicorn_rails do
  RAILS_ENV=ENV['RAILS_ENV']
  UNICORN_CMD='unicorn_rails'
  UNICORN_CONFIG=%Q(#{File.dirname(File.expand_path(__FILE__))}/../../config/unicorn/#{ENV['RAILS_ENV']}.rb)
  UNICORN_PID=%Q(#{File.dirname(File.expand_path(__FILE__))}/../../tmp/pids/unicorn.pid)

  task :environment do
  end

  desc 'unicorn_rails start'
  task :start => :environment do

    sh %Q(/bin/bash -lc "bundle exec #{UNICORN_CMD} -c #{UNICORN_CONFIG} -E #{RAILS_ENV} -D")
  end

  desc 'unicorn_rails stop'
  task :stop => :environment do 
    if %x(ps ho pid p `cat #{UNICORN_PID}`).present?
      sh %Q(kill `cat #{UNICORN_PID}`)
    end
  end

  desc 'unicorn_rails graceful_stop'
  task :graceful_stop => :environment do
    if %x(ps ho pid p `cat #{UNICORN_PID}`).present?
      sh %Q(kill -s QUIT `cat #{UNICORN_PID}`)
    end
  end

  desc 'unicorn_rails reload'
  task :reload => :environment do
    if %x(ps ho pid p `cat #{UNICORN_PID}`).present?
      sh %Q(kill -s USR2 `cat #{UNICORN_PID}`)
    else
      Rake::Task['unicorn_rails:start'].execute
    end
  end

  desc 'unicorn_rails restart'
  task :restart => :environment do
    if %x(ps ho pid p `cat #{UNICORN_PID}`).present?
      Rake::Task['unicorn_rails:stop'].execute
    else
      puts %Q(unicorn didn't work.)
    end
    Rake::Task['unicorn_rails:start'].execute
  end
end
```

## capistrano task用

```rb:lib/capistrano/tasks/unicorn.rake
namespace :unicorn_rails do
  task :environment do
    set :unicorn_cmd, 'unicorn_rails'
    set :unicorn_config, "#{current_path}/config/unicorn/production.rb"
    set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
  end

  desc 'unicorn_rails start'
  task :start => :environment do
    on roles(:app), reject: lambda { |h| h.properties.no_release } do
      execute "cd #{current_path} && bundle exec #{fetch(:unicorn_cmd)} -c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
    end
  end
  task :stop => :environment do
    on roles(:app), reject: lambda { |h| h.properties.no_release } do
      if test("[ -f #{fetch(:unicorn_pid)} ]")
        execute "kill `cat #{fetch(:unicorn_pid)}`"
      end
    end
  end
  task :graceful_stop => :environment do
    on roles(:app), reject: lambda { |h| h.properties.no_release } do
      if test("[ -f #{fetch(:unicorn_pid)} ]")
        execute "kill -s QUIT `cat #{fetch(:unicorn_pid)}`"
      end
    end
  end
  task :reload => :environment do
    on roles(:app), reject: lambda { |h| h.properties.no_release } do
      if test("[ -f #{fetch(:unicorn_pid)} ]")
        execute "kill -s USR2 `cat #{fetch(:unicorn_pid)}`"
      else
        Rake::Task['unicorn_rails:start'].execute
      end
    end
  end
  task :restart => :environment do
    on roles(:app), reject: lambda { |h| h.properties.no_release } do
      Rake::Task['unicorn_rails:stop'].execute
      Rake::Task['unicorn_rails:start'].execute
    end
  end
end
```

## unicorn自動起動設定
起動スクリプトの作成

``` bash:/etc/init.d/unicorn
#!/bin/bash

#chkconfig:2345 85 70
#description:unicorn shell

USER='rails_user'
RAILS_ROOT='/path/to/rails_app/current'
NAME='unicorn_rails'
PID="${RAILS_ROOT}/tmp/pids/unicorn.pid"

start()
{
  if [ -e $PID ]; then
    PID_NUM=`cat ${PID}`
    PROCESS=`ps ho pid p ${PID_NUM} > /dev/null; echo $?`
    if [ "${PROCESS}" != 0 ]; then
      echo 'rm ghost pid'
      rm -f ${PID}
    else
      echo "$NAME already started"
      exit 1
    fi
  fi
  echo "start $NAME"
  su - $USER -lc "cd ${RAILS_ROOT} && bundle exec rake unicorn_rails:start"
}

stop()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started"
    exit 1
  fi
  echo "stop $NAME"
  su - $USER -lc "cd ${RAILS_ROOT} && bundle exec rake unicorn_rails:stop"
}

force_stop()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started"
    exit 1
  fi
  echo "stop $NAME"
  kill -INT `cat ${PID}`
  
}

reload()
{
  if [ ! -e $PID ]; then
    echo "$NAME not started"
    start
    exit 0
  fi
  echo "reload $NAME"
  #kill -HUP `cat ${PID}`
  su - $USER -lc "cd ${RAILS_ROOT} && bundle exec rake unicorn_rails:reload"
}

restart()
{
    stop
    sleep 3
    start
}

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  force-stop)
    force_stop
    ;;
  reload)
    reload
    ;;
  restart)
    restart
    ;;
  *)
    echo "Syntax Error: release [start|stop|force-stop|reload|restart]"
    ;;
esac
```

```
$ sudo chkconfig --add unicorn
$ sudo chkconfig on
$ sudo chkconfig --list
```

## 参考
- [CentOS7 に Nginx をインストールする](https://www.saintsouth.net/blog/install-nginx-on-centos7/)
- [Unicorn設定のまとめ](http://qiita.com/syou007/items/555062cc96dd0b08a610)
- [unicorn 導入手順書](http://qiita.com/k4zzk/items/11ac64214fb5908de911)
- [Unicornの自動起動設定](http://blog.mmmcorp.co.jp/blog/2015/10/23/unicorn-auto-start/)