ubuntu14.04でRailsをnginx + unicorn環境で動作させるための作業手順

はじめに

RailsをnginxとunicornによるWebサーバ環境で動作させるまでに思ったより苦労したのでその手順をメモしておきます。なお、以下の手順や使用する設定ファイルのほとんどは、以下のサイトを参考にしています。メールでお願いして使わせて頂いてます。大変参考になりました。

Setting Up Your Production Server With Nginx and Unicorn | Tealeaf Academy Blog

また、そもそもunicornとはなにかについては、以下のサイトが参考になると思います。(英語です)

Unicorn! | GitHub

今回構築する環境は、nginxをリバースプロキシとして使用し、unicornをアプリケーションサーバとして使う構成になります。

環境と前提


作業概要

主な作業としては以下のようになります。

  • nginxのインストールと設定
  • unicornのインストールと設定
  • 動作確認


nginxのインストールと設定

nginxのインストール

apt-getでupdateしてその後インストールでOKです。

$ sudo apt-get update
$ sudo apt-get install nginx

nginxのインストール時のエラーと解決策

私の環境だと、上記のsudo apt-get install nginxを実行した時に以下のようなエラーが出ました。

Reading package lists... Done
Building dependency tree
Reading state information... Done
Package nginx is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source

E: Package 'nginx' has no installation candidate

このエラーの解決策としては、まず以下のコマンドでnginxをインストールするためのリポジトリを追加します。

$ sudo add-apt-repository ppa:nginx/stable

続いて以下コマンドでアップデートし、その後でインストールを実行すれば無事に完了しました。

$ sudo apt-get update
$ sudo apt-get install nginx

参考サイト様

VagrantでUbuntu+nginx+MySQL+PHP-FPMの最新環境を構築する | Qiita


nginx.confの修正

nginxのインストールが完了したら、次に/etc/nginx配下にあるnginx.confを以下のように修正します。

user www-data;
worker_processes 4;
pid /run/nginx.pid;

events {
   worker_connections 768;
}

http {

##
# Basic Settings
##

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;

  include /etc/nginx/mime.types;
  default_type application/octet-stream;

##
# Logging Settings
##

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

  gzip on;
  gzip_disable "msie6";
  gzip_proxied any;
  gzip_http_version 1.1;
  gzip_min_length 500;
  gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

##
# nginx-naxsi config
##
# Uncomment it if you installed nginx-naxsi
##

  #include /etc/nginx/naxsi_core.rules;

##
# nginx-passenger config
##
# Uncomment it if you installed nginx-passenger
##

##
# Unicorn Settings
##
  upstream unicorn {
    server unix:/var/www/railsapp/tmp/unicorn.sock fail_timeout=0;
  }

##
# Virtual Host Configs
##

  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

なお、上記のnginx.confで以下の部分は各自の環境に合わせて変更する必要があります。

##
# Unicorn Settings
##
  upstream unicorn {
    server unix:/var/www/railsapp/tmp/unicorn.sock fail_timeout=0;
  }

上記の/var/www/railsapp/tmp/unicorn.sock/var/www/railsapp部分を各自のRailsのルートディレクトリパスに置き換える必要があります。また、Railsのルートディレクトリにtmpディレクトリがない場合はmkdir tmpで作成しておく必要があります。

vagrantを使用している場合の注意点

なお、vagrantを使用している場合に、vagrantの仮想マシン(virtualboxを使用)とホストマシンの共有フォルダ上にRailsのルートディレクトリを設置することがあると思います(vagrantのデフォルトだと、仮想マシンからみて/vagrantがホストマシンとの共有ディレクトリになっていると思います)。この場合に、nginx.confunicorn.sockの設置場所として以下のように記述することがあるかもしれません。

##
# Unicorn Settings
##
  upstream unicorn {
    server unix:/vagrant/railsapp/tmp/unicorn.sock fail_timeout=0;
  }

上記のように記述すると、unicorn起動時にエラーが発生します。これは、virtualboxの共有ディレクトリがソケットの設置を許可していないためです。よって、vagrantの共有ディレクトリ配下にunicorn.sockを設置しないように気をつける必要があります。例えば以下のように/tmpに設置するのもいいかもしれません。

##
# Unicorn Settings
##
  upstream unicorn {
    server unix:/tmp/unicorn.sock fail_timeout=0;
  }

私自身この事実をしらずに悩んだのでメモしておきます。

defaultの編集

続いて/etc/nginx/sites-availableにあるdefaultを以下のように編集します。

server {
  listen 80;

  root /var/www/railsapp;
  index index.html index.htm;

  keepalive_timeout 300;
  client_max_body_size 4G;

  # Make site accessible from http://localhost/
  server_name localhost 192.168.0.10;

  location /railsapp/ {
    try_files $uri/index.html $uri.html $uri @unicorn;
  }

  location ~ ^/assets/(.*) {
    alias /var/www/railsapp/public/assets/$1;
  }


  location / {
    # First attempt to serve request as file, then
    # as directory, then fall back to displaying a 404.
    #try_files $uri $uri/ =404;

    # Uncomment to enable naxsi on this location
    # include /etc/nginx/naxsi.rules
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn;
  }

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded_Proto $scheme;
    proxy_redirect off;

    # This passes requests to unicorn, as defined in /etc/nginx/nginx.conf
    proxy_pass http://unicorn;
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
  }


    error_page 500 502 503 504 /500.html;
    location = /500.html {
    root /var/www/railsapp/public;
  }

}

上記で各自の環境に合わせて変更する必要がある箇所は、以下の2箇所です。

  • /var/www/railsappの部分を各自のRailsのルートディレクトリのパスに置き換える必要があります。
  • server_name localhost 192.168.0.10localhost192.168.0.10を各自の環境にあわせて合わせて変更する必要があります。私の場合はローカル内でRailsアプリにアクセスしたかったので、Railsが動作するサーバのIPアドレスである192.168.0.10を指定しています。

最後に、Railsのルートディレクトリの所有者をwww-dataに変更し、アクセス権限として755を設定しておきます。

$ sudo chown -R www-data:www-data /var/www/railsapp
$ sudo chmod -R 755 /var/www/railsapp

あとは以下のコマンドでnginxの設定ファイルを再読み込みし、さらに起動できるかを確認しておきます。

$ sudo service nginx reload
$ sudo service nginx start

以上でnginxのインストールと設定は完了です。

unicornのインストールと設定

unicornのインストール

unicornをインストールするために下記をGemfileに追記します。

 gem ‘unicorn'

そしてRailsのルートディレクトリ(今回の場合は/var/www/railsapp)に移動したあとでbundle installを実行します。

$ bundle install

以上でunicornのインストールは完了です。なお、上記のbundle install時にmysql2パッケージ関連でエラーが出る場合があります。An error occurred while installing mysql2 (0.3.11)...のようなエラーです。このエラーの解決策としては、以下コマンドを実行すればOKです。

$ sudo apt-get install libmysqld-dev

もし上記を実行してさらにエラーが出る場合や、別のエラーが出る場合は、こちらにいくつかのエラーとその解決策を載せているので参考になるかもしれません。

unicornをubuntuのサービスとして登録

unicornをubuntuのサービスとして登録してコマンドとして使用するために/etc/init.dunicornという名前の起動スクリプトを作成します。
そしてこのファイルの中身として以下を記述します。なお、以下の/etc/init.d/unicornの中身は冒頭に載せた参考サイト様のものをそのまま使わせて頂いてます。大変参考になりました

#!/bin/sh

# File: /etc/init.d/unicorn

### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn web server
# Description: starts unicorn
### END INIT INFO

# Feel free to change any of the following variables for your app:

# ubuntuのユーザ名 (ログインユーザのこと)
USER=user1 # 各自のubuntuのユーザ名を指定。

# Replace [PATH_TO_RAILS_ROOT_FOLDER] with your application's path. I prefer
# /srv/app-name to /var/www. The /srv folder is specified as the server's
# "service data" folder, where services are located. The /var directory,
# however, is dedicated to variable data that changes rapidly, such as logs.
# Reference https://help.ubuntu.com/community/LinuxFilesystemTreeOverview for
# more information.
#APP_ROOT=[PATH_TO_RAILS_ROOT_FOLDER]
APP_ROOT=/var/www/railsapp # 各自のRailsのルートディレクトリを指定

# Set the environment. This can be changed to staging or development for staging
# servers.
RAILS_ENV=production # Railsを動作させる環境を指定

# This should match the pid setting in $APP_ROOT/config/unicorn.rb.
PID=$APP_ROOT/tmp/unicorn.pid

# A simple description for service output.
DESC="Unicorn app - $RAILS_ENV"

# If you're using rbenv, you may need to use the following setup to get things
# working properly:
RBENV_RUBY_VERSION=`cat $APP_ROOT/.ruby-version`
RBENV_ROOT="/home/$USER/.rbenv"
PATH="$RBENV_ROOT/bin:$PATH"
SET_PATH="cd $APP_ROOT && rbenv rehash && rbenv local $RBENV_RUBY_VERSION"

# Unicorn can be run using `bundle exec unicorn` or `bin/unicorn`.
#UNICORN="bin/unicorn"
UNICORN="bundle exec unicorn"

# Execute the unicorn executable as a daemon, with the appropriate configuration
# and in the appropriate environment.
UNICORN_OPTS="-c $APP_ROOT/config/unicorn.rb -E $RAILS_ENV -D"
CMD="$SET_PATH && $UNICORN $UNICORN_OPTS"

# Give your upgrade action a timeout of 60 seconds.
TIMEOUT=60

# Store the action that we should take from the service command's first
# argument (e.g. start, stop, upgrade).
action="$1"

# Make sure the script exits if any variables are unset. This is short for
# set -o nounset.
set -u

# Set the location of the old pid. The old pid is the process that is getting
# replaced.
old_pid="$PID.oldbin"

# Make sure the APP_ROOT is actually a folder that exists. An error message from
# the cd command will be displayed if it fails.
cd $APP_ROOT || exit 1

# A function to send a signal to the current unicorn master process.
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}

# Send a signal to the old process.
oldsig () {
test -s $old_pid && kill -$1 `cat $old_pid`
}

# A switch for handling the possible actions to take on the unicorn process.
case $action in
# Start the process by testing if it's there (sig 0), failing if it is,
# otherwise running the command as specified above.
start)
sig 0 && echo >&2 "$DESC is already running" && exit 0
su - $USER -c "$CMD"
;;

# Graceful shutdown. Send QUIT signal to the process. Requests will be
# completed before the processes are terminated.
stop)
sig QUIT && echo "Stopping $DESC" exit 0
echo >&2 "Not running"
;;

# Quick shutdown - kills all workers immediately.
force-stop)
sig TERM && echo "Force-stopping $DESC" && exit 0
echo >&2 "Not running"
;;

# Graceful shutdown and then start.
restart)
sig QUIT && echo "Restarting $DESC" && sleep 2 \
&& su - $USER -c "$CMD" && exit 0
echo >&2 "Couldn't restart."
;;

# Reloads config file (unicorn.rb) and gracefully restarts all workers. This
# command won't pick up application code changes if you have `preload_app
# true` in your unicorn.rb config file.
reload)
sig HUP && echo "Reloading configuration for $DESC" && exit 0
echo >&2 "Couldn't reload configuration."
;;

# Re-execute the running binary, then gracefully shutdown old process. This
# command allows you to have zero-downtime deployments. The application may
# spin for a minute, but at least the user doesn't get a 500 error page or
# the like. Unicorn interprets the USR2 signal as a request to start a new
# master process and phase out the old worker processes. If the upgrade fails
# for some reason, a new process is started.
upgrade)
if sig USR2 && echo "Upgrading $DESC" && sleep 10 \
&& 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 'su - $USER -c \"$CMD\"' instead"
su - $USER -c "$CMD"
;;

# A basic status checker. Just checks if the master process is responding to
# the `kill` command.
status)
sig 0 && echo >&2 "$DESC is running." && exit 0
echo >&2 "$DESC is not running."
;;

# Reopen all logs owned by the master and all workers.
reopen-logs)
sig USR1
;;

# Any other action gets the usage message.
*)
# Usage
echo >&2 "Usage: $0 "
exit 1
;;
esac

上記の/etc/init.d/unicornの記述のうち、各自の環境に合わせて変更する必要がある箇所、確認が必要な箇所は以下の4箇所です。

  • USER=user1は各自のubuntuのユーザ名(PCでログインしているユーザ名)を記述します。上記ではubuntuのユーザとしてuser1を記述していますが、このuser1を各自の環境にあわせて合わせて変更する必要があります。例えば、vagrantを使用している場合は、デフォルトのユーザ名はvagrantです。
  • APP_ROOT=/var/www/railsappは、各自のRailsのルートディレクトリを指定する必要があります。
  • RAILS_ENV=productionは、各自のRailsを動作させる環境を指定する必要があります。ここでは本番環境であるproductionを指定しています。
  • RBENV_RUBY_VERSION=`cat $APP_ROOT/.ruby-version`は、各自のRailsルートディレクトリにある.ruby-versionというファイルを読み込んでいます。この.ruby-versionにはそのファイル名とおり、使用しているRubyのバージョン番号が記述されています。もしRailsルートディレクトリにこのファイルがなければ、自分で作成し、使用しているRubyのバージョン番号を記述しておく必要があります。Rubyのバージョンはruby -vで確認できます。また、私の場合は2.2.0を使用しているので単純に.ruby-versionには2.2.0とだけ記述しておけばOKです。

続いて今作成したunicornに実行権限を与えてさらにubuntuの起動と同時にunicornを起動させるためにupdate-rc.dを実行します。

$ sudo chmod +x /etc/init.d/unicorn
$ sudo update-rc.d unicorn defaults


unicornの設定ファイル作成

Railsのunicronの設定ファイルとして/var/www/railsapp/configunicorn.rbを作成します。そして以下を記述します。

app_path = File.expand_path(File.dirname(__FILE__) + '/..')

worker_processes 2
listen app_path + '/tmp/unicorn.sock', backlog: 64
timeout 300
working_directory app_path
pid app_path + '/tmp/unicorn.pid'
stderr_path app_path + '/log/unicorn.log'
stdout_path app_path + '/log/unicorn.log'

preload_app true

GC.respond_to?(:copy_on_write_friendly=) &&
GC.copy_on_write_friendly = true

before_fork do |server, worker|
defined?(ActiveRecord::Base) &&
ActiveRecord::Base.connection.disconnect!
end

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

上記のunicorn.rbでは、app_pathがRailsのルートディレクトリのパスを値として持つため、listen app_path + '/tmp/unicorn.sock', backlog: 64listen '/var/www/railsapp/tmp/unicorn.sock', backlog: 64と同義です。そしてこの部分はnginx.confに記述したserver unix:/var/www/railsapp/tmp/unicorn.sock fail_timeout=0;と合わせる必要があります。よって、もしvagrant(virtualboxを使用)の仮想マシンとホストマシンの共有フォルダ上にRailsのルートディレクトリを設置していて、nginx.confserver unix:/tmp/unicorn.sock fail_timeout=0;として記述した場合はunicorn.rbnginx.confに合わせて以下のように変更する必要があります。

listen '/tmp/unicorn.sock', backlog: 64


動作確認

最後に動作確認を行います。以下のコマンドでnginx、unicornをそれぞれ起動します。

$ sudo service nginx start
$ sudo service unicorn start

起動後、ブラウザを開いて192.168.0.10にアクセスして各自のRailsアプリにアクセスできればOKです。もしアクセスした際に403エラーが表示される場合は、各自のRailsルートディレクトリの権限などを確認すると解決につながるかもしれません。また、unicornが起動しない場合は、Railsルートディレクトリのlog/unicorn.logを参照すると原因がわかるかもしれません。あとは、rails s -b 0.0.0.0を実行してまずRails自体が正常に起動するかを確認するといいかもしれません。Railsが正常に起動できない状態だとunicornも起動できません。
なお、nginx, unicornは以下のコマンドで停止させることが可能です。

$ sudo service nginx stop
$ sudo service unicorn stop


おわりに

Railsをnginx + unicornで動作させるまでの手順をメモしました。nginx + unicorn環境だとRailsアプリの動作がwebrickなどの場合に比べて体感的に高速になりました。

SPONSORED LINK

この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。

この投稿へのトラックバック

トラックバックはありません。

トラックバック URL