Ruby on Rails を Capistrano を用いて Ubuntu サーバ (with MySQL, Nginx) にデプロイした時のメモ

Ruby on Rails を Capistrano で自動デプロイできるように Ubuntu を設定する機会があったので、作業内容を忘れないようにメモ。 `Rails`、`nginx`、`MySQL`、`Unicorn` の構成。**ゴールは Ubunut 14.04 にCapistrano で Rails プロジェクトをデプロイできる** こと。 


**追記**: 以下の内容を少しリファクタリングして、 [Ansible というツールを利用して自動化](https://github.com/travelist/ansible-rails)しました。( [Capistrano ですぐにデプロイできる Rails 環境を Ansible で自動構築する] (http://qiita.com/shimashima/items/b7c18c4489db3fcb3cbc) ) ほぼ下に書いた内容と同様のことを設定ファイルを10行くらい編集すれば出来ます。[Capistrano の設定ファイルのサンプル](https://github.com/travelist/ansible-rails/tree/master/rails_config) も配置してあるので、時間が無い場合はこちらのほうが早いと思います。



## 内容
以下のことができる人にはこの記事は不要な情報かもしれません。その場合は次の記事に進んでいただくことでライフハックしていただければと思います。

- **パスワードなし sudo の設定** (Capistranoで必要なので)
- **rbenv のインストール** (Rubyのバージョン管理で必要なので)
- **MySQL のインストール** (DBはMySQLで)
- **Nginx の設定** (Apacheよりは今はこっちが主流？)
- **github にデプロイ鍵を登録**
- **unicorn や database.yml や Capistrano 関連のファイルの設定**


## 1. パスワードなし sudo の設定
パスワードなし `sudo` ができないと Capistrano デプロイができない(今はそうでもないかも？)。
**すでにパスワードなしで sudo ができる場合は不要** 

- この次の操作を間違えると二度と sudo ができなくなるので、先に root のパスワード設定

```
sudo su
passwd
# 新規パスワードを入力
```

- visudo でsudo権限の設定を変更

```
sudo visudo # 編集画面へ
# 設定ファイルの最後に以下の一行を追加 username はユーザ名
# username ALL=(ALL:ALL) NOPASSWD:ALL
```

## 2. rbenv を入れる

Ruby 自体のバージョン管理に利用。これで開発環境とデプロイ後の環境で ruby のバージョンが違うから動かない、みたいなことを回避できる。

- レポジトリ情報を更新

```
sudo apt-get update
```


- 必要なパッケージのインストール

```
sudo apt-get install -y git git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev
```

- rbenv のインストール

```
cd # ホームディレクトリへ移動

git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
```

- 正しくインストールされたことを確認

```
rbenv-install -l
# rbenv でインストールできる ruby のバージョンのリストが表示される
``` 

## 3. rbenv を用いて ruby をインストール

```
rbenv-install 2.2.0
# 結構時間がかかる
```

- 正しくインストールできたのを確認

```
rbenv versions
# 2.2.0 と表示されるはず。
```
- おそらく bundle が入ってないのでインストール

```
rbenv global 2.2.0 # インストールしたバージョンを指定
rbenv exec gem install bundle # 'rbenv exec' いらないかも。
# これで rbenv exec bundler が使えるようになったはず
```


## 4. MySQL の準備

- MySQL 関連のパッケージをインストール

```
sudo apt-get install -y mysql-server mysql-client libmysqld-dev 
# MySQL の root ユーザのパスワードを聞かれるので適当に。
```

- Rails で使うユーザと DB を作成

```
mysql -u root -p
# root ユーザのパスワードでログイン

# データベース作成
CREATE DATABASE rails_database;

# ユーザ作成(rails_user の部分とrails_password を変えてください)
CREATE USER 'rails_user'@'localhost' IDENTIFIED BY 'rails_password';

# データベースの権限を作ったユーザに与える
GRANT ALL PRIVILEGES ON rails_database.* TO 'rails_user'@'localhost';

# 以上の設定を反映
FLUSH PRIVILEGES;
```

## 5. database.yml に上記のデータベースの設定を反映

上で設定した MySQL のデータベースやユーザ名、パスワードを設定。

```database.yml
default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

test:
  <<: *default
  database: db/test.sqlite3

production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: rails_database
  pool: 5
  username: rails_user
  password: rails_password
  host: localhost
```

## 6. nginx の準備

- インストール

```
sudo apt-get install nginx
```

```
cp /etc/nginx/sites-available/default /etc/nginx/sites-available/your_site.conf
vim /etc/nginx/sites-available/your_site.conf
# 下の設定ファイルを参考に変更
ln -s /etc/nginx/sites-available/your_site.conf /etc/nginx/sites-enabled/your_site.conf
```
- Nginx の設定ファイル

3行目の`unix:...`の部分の **socket の指定が unicorn の設定と一致するように注意**する。

```nginx:site-enable/your_site.conf
upstream app {
    # Path to Unicorn SOCK file, as defined previously
    server unix:/var/www/your_site.com/shared/tmp/sockets/unicorn.sock fail_timeout=0;
}

server {
    listen 80;
    server_name your_site.com, www.your_site.com;

    # Application root, as defined previously
    root /var/www/your_site.com/current/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}
```

## 7. github にデプロイ鍵を登録
- 鍵がなければ鍵を作成する

```
ssh-keygen
# エンター、エンター、エンター
cat ~/.ssh/id_rsa.pub
# 出てきた文字を右クリックでコピー
# github のプロジェクトの設定から deploy key を選んで登録
```

## 8. Gemfile に必要な Gem を記述

- MySQL、unicorn、Capistrano に必要な Gem を加える

```rb:Gemfile
# 下記のものを追加

# Use Unicorn as the app server
gem 'unicorn'

# MySQL adopter
gem 'mysql2', '~> 0.3.13'

group :development, :test do
  # for Capistrano
  gem 'capistrano'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv', '~> 2.0'
  gem 'capistrano3-unicorn'
end

```

## 9. Unicorn の設定ファイルを記述

- **socket の部分が上記の nginx の設定と噛み合っていないと２回目以降に Capistrano デプロイしたときに、プロセスが新しいものに更新されない**ので注意
- **pid も後述する Capistrano の設定ファイルの unicorn_pid パラメータと一致**させることを忘れずに。

```unicorn.rb
# path
app_path = '/var/www/your_site.com'
current_path = "#{app_path}/current"
shared_path = "#{app_path}/shared"

working_directory current_path
pid File.expand_path('tmp/pids/unicorn.pid', shared_path)
listen File.expand_path('tmp/sockets/unicorn.sock', shared_path), :backlog => 64

# logging
stderr_path "#{app_path}/shared/log/unicorn.stderr.log"
stdout_path "#{app_path}/shared/log/unicorn.stdout.log"

# preload
preload_app true

# Number of processes
worker_processes 4

# Time-out
timeout 30

# use correct Gemfile on restarts
before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = "#{app_path}/current/Gemfile"
end

before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end

  # Before forking, kill the master process that belongs to the .oldbin PID.
  # This enables 0 downtime deploys.
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

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


## 10. Capistrano の設定

`deploy.rb` にすべてのサーバに共通する設定を記述。`config/deploy/production.rb` などに環境ごとの詳細設定を記述するようにする。

```rb:Capfile
# Load DSL and set up stages
require 'capistrano/setup'

# Include default deployment tasks
require 'capistrano/deploy'

require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
```

```deploy.rb
# config valid only for current version of Capistrano
lock '3.4.0'

# Multistage deployment extension
set :application, 'your_site.com'
set :repo_url, 'git@github.com:your_account/your_site.git'
set :rbenv_ruby, '2.2.0'
set :rbenv_type, :user
set :deploy_to, "/var/www/#{fetch(:application)}"

shared_path = "#{fetch(:deploy_to)}/shared"
release_path = "#{fetch(:deploy_to)}/current"

set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
set :keep_releases, 10

# Unicorn
# unicorn.rb で指定した pid と一致するように。
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
set :unicorn_config_path, "#{release_path}/config/unicorn.rb"

set :ssh_options, {
  keys: %w(~/.ssh/id_rsa),
  forward_agent: false,
  auth_methods: %w(publickey)
}

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end
end
```

```deploy/production.rb
server 'yor_site.com', user: 'username', roles: %w{app db web}
set :unicorn_pid, "/var/www/#{fetch(:application)}/shared/tmp/pids/unicorn.pid"
set :stage, :production # これいらないかも。
set :unicorn_rack_env, 'production'
set :unicorn_config_path, "/var/www/#{fetch(:application)}/current/config/unicorn/production.rb"
set :rails_env, 'production'
set :branch, 'master'
```
## 最後: Capistrano でデプロイ！
プロジェクトの `app` などがあるディレクトリで Rails プロジェクトをデプロイできる....かもしれない。

```
bundle exec cap production deploy
```

## 参考サイト
- [How To Deploy a Rails App with Unicorn and Nginx on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-unicorn-and-nginx-on-ubuntu-14-04)
- [How To Install Wordpress with nginx on Ubuntu 12.04](https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-nginx-on-ubuntu-12-04)
- [How To Install Ruby on Rails with rbenv on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-with-rbenv-on-ubuntu-14-04)
- [capistranoでunicornのoldプロセスが殺せない](http://h3poteto.hatenablog.com/entry/2015/05/10/192100)