Nginx + Unicorn + Rails + PostgreSQLでサーバー構築

# 前提条件
- AWS上で既にEC2やRDSが立ててあり、ネットワーク設定等が済んでいる状態を想定。
- 各ミドルウェアは2018年6月現在の最新安定板のバージョンを利用。

# Ruby + Railsのインストールと設定

## 1. 環境構築に必要なパッケージをインストール
`$ sudo yum -y install git gcc gcc-c++ make zlib-devel httpd-devel openssl-devel curl-devel readline-devel`

### yumとは
- Yellowdog Updater Modified
- パッケージ管理システムのこと
- yumはrpmを統合管理している（rpmをラップしている）

### パッケージの概要
- git : git
- gcc gcc-c++ : C言語やC++などのコンパイラ
- make : コンパイル型プログラムビのビルダー
- zlib : データの圧縮や伸張を行うライブラリ
- ssl : SSL/TLC（暗号化通信プロトコル）のライブラリ
- curl : 色々なプロトコルに対応したデータ転送ライブラリ
- readline : 行頭・行末移動やヒストリ機能などを提供するライブラリ

### 無印とdevelとの違い
- `-devel`はを示し、ヘッダファイルが含まれている。
- これがないと、ソースコードからアプリケーションをインストールしたいときにヘッダファイルが見つからず困ることがある。
- 詳しくは[こちらの記事](http://www.unknownengineer.net/entry/2016/05/11/120014)を参考。

## 2. rbenvをインストール
`$ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv`

### rbenvとは
- Simple Ruby Version Management
- rvmをよりシンプルにしたRubyのバージョン管理ができるライブラリ

## 3. `PATH`に追加
`$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile`

## 4. `.bash_profile`に追加
`$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile`

## 5. `.bashrc`の再読み込み
`$ source ~/.bashrc`

## 6. シェルをログインシェルから起動させる
`$ exec $SHELL -l`

- 詳しくは[シェルを再起動させる簡単な方法
](https://qiita.com/yusabana/items/c4de582c6f85a42817d8)を参照。

## 7. `ruby-build`をインストール
`$ sudo git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build`
`$ cd ruby-build`
`$ sudo ./install.sh`

### ruby-buildとは
- ruby-buildは、UNIX系のシステム上で異なるバージョンのRubyをコンパイルしインストールするための、 rbenv installコマンドを提供するrbenvのプラグイン
- install.shを叩き、ruby-buildを最新にしている

## 8. rubyをインストール可能かどうか確認
`$ rbenv install --list | grep 2.5.1`

## 9. バージョンを指定してインストール
`$ rbenv install 2.5.1`

## 10. rbevの再読み込み
`$ rbenv rehash`

## 11. 使用するrubyを指定
`$ rbenv global 2.5.1;rbenv rehash`

### 指定方法について
- global : システム全体で使用するバージョンを指定
- local : 特定のディレクトリでバージョンを使用する場合に指定
    - そのディレクトリにある.ruby-versionファイルを書き換えて管理している

## 12. インストールしたrubyのバージョンを確認
`$ ruby -v`

## 13. bundlerのインストール
`$ gem install bundler;rbenv rehash`

### bundlerとは
- 前提として、gemというRubyのライブラリがある
    - （例）rails, sinatra, unicorn, Rspec・・・
- bundlerとは、gemを個別にインストールできるgemのライブラリ
- 詳しくは[Bundlerの使い方](https://qiita.com/oshou/items/6283c2315dc7dd244aef)を参照。

## 14. railsのインストール
`$ gem install rails;rbenv rehash`

## 15. railsのバージョンを確認
`$ rails -v`

### railsのバージョンを変更したい時
`$ gem uninstall railties -v '5.2.0'`で最新版のrailsを削除後、バージョンを指定してインストール
`$ gem i -v 5.1.4 rails;rbenv rehash`

## 16.SQLiteのインストール（不要な場合は飛ばしてOK）
`$ sudo yum -y install sqlite-devel`
`$ gem install sqlite3;rbenv rehash`

## 17. Railsアプリを`/var/www/`配下に作成
※ 今回DBはpostgresqlを利用するため、rails new時に指定しておく。

`$ sudo chown ec2-user /var`
`$ cd /var`
`$ sudo chown ec2-user www`
`$ cd /var/www/`
`$ rails new myapp2 -d postgresql`

## 18. JavaScript Runtimeを有効にする
`$ vi /var/www/myapp/Gemfile`
`gem 'therubyracer', :platforms => :ruby`を追加

## 19. unicornのgemをインストール
`$ vi /var/www/myapp/Gemfile`
`gem 'unicorn'`を追加。

## 20. Gemfileを更新したのでbundleをインストール
`$ cd myapp`
`$ bundle install`

## 21. railsアプリを確認
`$ rails server`

## 22. 別タブを開き、curlする
`$ curl -I localhost:3000`
以下のように、200番が帰って来ればOK
`HTTP/1.1 200 OK`


# Unicorn + Nginx のインストールと設定

## 1. Unicornのインストール
`$ gem install unicorn;rbenv rehash`

## 2. configファイル作成
`$ vi myapp/config/unicorn.conf`

次のように修正する。
参考
- [Unicorn設定のまとめ](https://qiita.com/syou007/items/555062cc96dd0b08a610)

```/var/www/myapp/config/unicorn.conf
# set lets
$worker  = 2
$timeout = 30
$app_dir = "/var/www/myapp"
$listen  = File.expand_path 'tmp/sockets/unicorn.sock', $app_dir
$pid     = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
$std_log = File.expand_path 'log/unicorn.log', $app_dir

# set config
## Unicornは複数のワーカーで起動するのでワーカー数を定義
## サーバーのメモリなどによって変更すること。
worker_processes  $worker

## Unicornの起動コマンドを実行するディレクトリを指定
working_directory $app_dir

## Unicornのエラーログと通常ログの位置を指定
stdout_path $std_log
stderr_path $std_log

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

## Nginxでのlisten指定 unicornを呼ぶ
listen '/var/www/myapp/tmp/sockets/unicorn.sock'

## プロセスの停止等に必要なpidファイルの保存先を指定
pid $pid

## loading booster : Unicornの再起動時にダウンタイムなしで再起動が行われる
preload_app true

## before starting processes
## - $ kill -USR2 `cat /path/to/unicorn.pid` のようなUSR2で再起動をかける時に、新プロセスを立ち上げて旧プロセスを停止する
## before starting processes
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
      Process.kill "QUIT", File.read(old_pid).to_i
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
end
# after finishing processes
after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
```

## 3. nginxインストール
`$ sudo yum install nginx`

**※ Amazone Linux2の場合は、以下の操作**
`$ amazon-linux-extras | grep nginx`
`$ sudo amazon-linux-extras install nginx1.12`

## 4. nginxの起動確認
`$ sudo /etc/init.d/nginx start`
`$ ps -ax | grep nginx`でプロセスを確認するか、EC2のパブリックDNSでの確認する。

**※ Amazone Linux2の場合は、以下の操作**
`$ sudo systemctl start nginx.service`

## 5. nginxを停止
`$ sudo /etc/init.d/nginx stop`

**※ Amazone Linux2の場合は、以下の操作**
`$ sudo systemctl stop nginx.service`

## 6. nginxの設定ファイル編集
参考

- Nginx設定のまとめ : https://qiita.com/syou007/items/3e2d410bbe65a364b603
- nginxについてまとめ(設定編) : https://qiita.com/morrr/items/7c97f0d2e46f7a8ec967

`$ sudo vi /etc/nginx/nginx.conf`

```/etc/nginx/nginx.conf
# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

# nginxを起動するとmaster processとworker process、cache manager processの三つのプロセスが起動します。
# その中のmaster process以外を起動するユーザーを指定します。
# master processはrootユーザーで起動
user nginx;

# コア数よりも多くの値を指定しても問題がない
# - autoに指定するとコア数を設定してくれる
# - コア数は以下のコマンドで調べれる
# - $ grep processor /proc/cpuinfo |wc
worker_processes auto;

# エラーログを格納するファイルの指定
error_log /var/log/nginx/error.log;

# pidを格納するファイルの指定
pid /var/run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

# eventモジュールの指定
events {
    # 1つのワーカーが同時に処理できる接続数
    worker_connections 1024;
}

# webサーバーとして使用するメインとなる処理
http {
    # ログのフォーマットを定義
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # 上記で指定したlog_formatを最後に指定
    # - この定義はserverブロックなどでも使用できる
    access_log /var/log/nginx/myapp_access.log main;
    # error_logにフォーマットはしてきでないの注意
    error_log /var/log/nginx/myapp_error.log;

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

    # MIMEタイプと拡張子の関連付けを定義したファイルを読み込み
    include             /etc/nginx/mime.types;
    # 上述のmime.typesで拡張子からMIMEタイプを決定できなかったときに、ここで指定したMIMEタイプが適応される
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.

    upstream unicornapp {
        server unix:/var/www/rails/coq-api/tmp/sockets/unicorn.sock;
    }
    # nginxでバーチャルホストの運用をする際に利用する
    # - httpブロック内に記述
    server {
        # IPアドレスまたはポート番号を指定
        listen       80;
        server_name  localhost;

        # 指定のロケーション（パスやファイル）の場合にのみ適用される
        location / {
            proxy_pass http://unicornapp;
        }
    }
}
```

## 7. nginx起動
`$ sudo /etc/init.d/nginx start`

**※ Amazone Linux2の場合は、以下の操作**
`$ sudo systemctl start nginx.service`

## 8. unicorn起動
`$ cd var/www/myapp`
`$ unicorn -c config/unicorn.conf -D`

※ unicornを停止するときはmasterプロセスをkillする
`$ ps ax|grep unicorn|grep -v grep`
`$ kill **** `

## 9. 動作確認
EC2のパブリックDNSで確認してみる。
<img width="723" alt="スクリーンショット 2018-06-30 16.19.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/191919/bb18f991-a37b-b768-5e18-3e127d77da2b.png">

# PostgreSQLのインストールと設定
※ RDSの設定に関して、postgresqlのwell knouwn portは`5432`なので注意する。

## 1. PostgreSQLのインストール
[こちらのページ](https://yum.postgresql.org/repopackages.php)を参考に、Amazon Linux AMIに対応しているPSQLのrpmをインストールする。

`$ sudo yum localinstall -y https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-6-x86_64/pgdg-ami201503-96-9.6-2.noarch.rpm`
`$ sudo yum install -y postgresql96 postgresql96-server postgresql96-libs postgresql96-contrib postgresql96-devel`

## 2. バージョンを確認
`$ psql --version`

## 3. gemのインストール
`$ vi /var/www/myapp/Gemfile`
Gemifileに`gem 'pg'`を追加。
`$ gem install pg;rbenv rehash`
`$ cd myapp`
`$ bundle install`

## 4. database.ymlの修正
postgresqlを利用するように修正する。
参考 : http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html

```/var/www/myapp/config/database.yml
default: &default
  adapter: postgresql
  encoding: utf8
  pool: 5
  username: [user] # RDS側で設定したユーザーを指定
  password: [pass] # RDS側で設定したパスを指定
  host: [****.rds.amazonaws.com] # RDSのエンドポイントを指定
  timeout: 5000

development:
  <<: *default
  database: my_development # RDS側で作成したDBを指定

test:
  <<: *default
  database: my_test # RDS側で作成したDBを指定

production:
  <<: *default
  database: my_production # RDS側で作成したDBを指定
```

## 5. EC2 → RDSの接続確認
AWS側で正しく設定ができているか確認する。
`$ psql -h [RDSのエンドポイント] -U [RDSのUsername] -d [RDSのDB NAME]` で接続確認

`$ psql -l` で作成したDBが確認できれば接続確認OK。

psqlコマンドについては[PostgreSQLの基本的なコマンド](https://qiita.com/H-A-L/items/fe8cb0e0ee0041ff3ceb)を参考。
※ localで開発するような場合は[こちら](https://qiita.com/yh2020/items/8be3087004d100fe752b)が参考になりそう。

### 5-1. エラー対応
#### DB実行時の環境を環境変数に自動設定する
- http://source.hatenadiary.jp/entry/2014/01/27/202806
- `$ export RAILS_ENV=default`

#### `config.eager_load = true`のとき
- https://qiita.com/muramurasan/items/305bd3d78d67b646bc06
- https://stackoverflow.com/questions/40848567/postgresql-gemloaderror-pg-is-not-part-of-the-bundle-add-it-to-gemfile

#### pgのgemが無い
バージョンを落とすことで解決することがある。

- Gemファイルに `gem 'pg', '< 0.21.0'` を追記
- `$ gem install pg`
- `$ bundle install`

## 6. Rails -> RDSでの動作確認
試しに、名前と年齢を持つUserモデルを作成してみる。

### 6-1. scaffoldの実行
自動でModel, Controller, Viewの雛形が作成される。
`$ rails generate scaffold user name:string age:integer`

### 6-2. migrateの実行
マイグレーションファイルを利用してDBにテーブルを作成する。
`$ rake db:migrate`

最後に、作成したテーブルを確認する。
`$ psql -h [RDSのエンドポイント] -U [RDSのUsername] -d [RDSのDB NAME]` で接続
`myRDS=> \c myRDS` でDBを選択
`myRDS=> \dt;` でテーブルを確認
`myRDS=> select * from users;` で中身を見て、以下のようになっていれば成功。

```
 id | name | age | created_at | updated_at 
----+------+-----+------------+------------
(0 行)
```

※ 画面アクセス時に`Migrations are pending`と怒られた時は、uniconとrailsを再起動すれば解決すると思います。

おわり。