Rails5アプリケーションのAWSによるネットワーク構築 Nginx+Puma+Capistranoな環境とAWS構築(VPC EC2 RDS CloudFlont Route53 etc)

# はじめに

数ヶ月前に書いたRails5のAWS構築をもう少し項目を増やしてから公開しようと思っていたのですが、なかなか出来ないのでもう公開します。(ELBとかCloudFlontとかもっとちゃんと書けそう、、)
項目の追加や修正は適宜入れると思います。

Railsは5.0を使っています。（記事を書いたときの最新）

4系は以前書きました↓

- [AWS VPCによるネットワーク構築とEC2によるサーバー構築](http://qiita.com/shizuma/items/396ad15ff574717dfa1d)
- [RailsアプリケーションのAWSによる公開｜unicorn + nginx](http://qiita.com/shizuma/items/d4ae7d4374aeb90df648)
- [AWS RailsアプリケーションのCapistranoによるデプロイ](http://qiita.com/shizuma/items/c23518a1934c738ec3b0)

# 構築する環境

- VPC
- EC2
 - Nginx + Puma
 - Capistrano
- (S3)
- ELB
- RDS
- CloudFlont
- Route53
- (CloudWatch)

# VPC、EC2のインスタンスの作成

AWSのデザインが多少変更はありますが、以下を参考に作成出来ます。

[AWS VPCによるネットワーク構築とEC2によるサーバー構築](http://qiita.com/shizuma/items/396ad15ff574717dfa1d)

これに追加でElastic IPの設定もしておきました。

# EC2内の環境構築

作成したEC2にログインして環境を構築します。
ec2-userでまずログインしましょう。

## ユーザーの作成

デフォルトではec2-userなので新たなユーザーを追加し、そのユーザーでsshログイン出来るように設定します。

```
$ sudo adduser shizuma #ユーザーの追加 「shizuma」の部分は好きなユーザー名にします。以後「shizuma」の部分は各自のユーザー名になります。
$ sudo passwd shizuma
#ここで、新規ユーザーのパスワードを設定します。
$ sudo visudo
-----------------------------
#vimが起動するので新規ユーザーにroot権限を与える。
root    ALL=(ALL)       ALL
shizuma ALL=(ALL)       ALL #この行を追加
-----------------------------
$ sudo su - shizuma #ユーザー切り替え
#先ほど設定したパスワード
```

ここでローカルに一旦戻り、鍵を作成します。

```
$ cd .ssh
$ ssh-keygen -t rsa
-----------------------------
Enter file in which to save the key ():first_aws_rsa #ここでファイルの名前を記述して、エンター
Enter passphrase (empty for no passphrase): #何もせずそのままエンター
Enter same passphrase again: #何もせずそのままエンター
-----------------------------
$ vi config
-----------------------------
# 以下を追記
Host first_aws
  Hostname 54.64.22.197 #自分の設定に合わせて
  Port 22
  User shizuma #先ほどのユーザー名
  IdentityFile ~/.ssh/first_aws_rsa #秘密鍵の設定
-----------------------------
```

次に、サーバーに戻り作成した `公開鍵` をサーバーに設定します。

```
$ mkdir .ssh
$ chmod 700 .ssh
$ cd .ssh
$ vi authorized_keys
-----------------------------
#localの「first_aws_rsa.pub」の中身のコピペ。(localで　$ cat first_aws_rsa.pubとかすると良い)
ssh-rsa sdfjerijgviodsjcIKJKJSDFJWIRJGIUVSDJFKCNZKXVNJSKDNVMJKNSFUIEJSDFNCJSKDNVJKDSNVJNVJKDSNVJKNXCMXCNMXNVMDSXCKLMKDLSMVKSDLMVKDSLMVKLCA shizuma@shizuma-no-MacBook-Air.local
-----------------------------
$ chmod 600 authorized_keys
$ exit
$ exit
```

これで設定が完了したので、以降作成したユーザーでアクセスするようにします。

## 基本ライブラリとrubyの環境構築 

```
$ sudo yum install \
git make gcc-c++ patch \
openssl-devel \
libyaml-devel libffi-devel libicu-devel \
libxml2 libxslt libxml2-devel libxslt-devel \
zlib-devel readline-devel \
mysql mysql-server mysql-devel \
ImageMagick ImageMagick-devel \
epel-release
$ sudo yum install nodejs npm --enablerepo=epel
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source .bash_profile
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ rbenv rehash
$ rbenv install -v 2.3.1
$ rbenv global 2.3.1
$ rbenv rehash
$ ruby -v
```

## gitの設定

最低限の設定だけしておきます。

```
$ vi .gitconfig
```

```bash:.gitignore
[user]
  name = your_name #自分の名前
  email = hoge@hoge.com #自分のメアド

[alias] #これはお好きに
  a = add
  b = branch
  ch = checkout
  st = status

[color] #色付け
  ui = true

[url "github:"] #pull、pushのための設定
    InsteadOf = https://github.com/
    InsteadOf = git@github.com:
```

## アプリケーションフォルダの設置

`/var/www/rails` にアプリケーションフォルダを設置します。

```
$ cd /
$ sudo mkdir -p /var/www/rails
$ sudo chown -R shizuma var/www
```

## GitHubの接続とアプリケーションのclone

```
$ cd ~/.ssh
$ ssh-keygen -t rsa
-----------------------------
Enter file in which to save the key ():aws_git_rsa #ここでファイルの名前を記述して、エンター
Enter passphrase (empty for no passphrase): #何もせずそのままエンター
Enter same passphrase again: #何もせずそのままエンター
-----------------------------
$ chmod 744 config
$ vi config
-----------------------------
# 以下を追記
Host github github.com
  Hostname github.com
  User git
  Port 22
  IdentityFile ~/.ssh/aws_git_rsa #秘密鍵の設定
-----------------------------
$ cat aws_git_rsa.pub
-----------------------------
ssh-rsa sdfjerijgviodsjcIKJKJSDFJWIRJGIUVSDJFKCNZKXVNJSKDNVMJKNSFUIEJSDFNCJSKDNVJKDSNVJNVJKDSNVJKNXCMXCNMXNVMDSXCKLMKDLSMVKSDLMVKDSLMVKLCA shizuma@ip-10-0-1-10
-----------------------------
```

ここで、これをコピペしてgithubに公開鍵を登録する。
githubへの鍵の登録がよくわからない方は以下の記事を参考に。
[gitHubでssh接続する手順~公開鍵・秘密鍵の生成から~](http://qiita.com/shizuma/items/2b2f873a0034839e47ce#%E5%85%AC%E9%96%8B%E9%8D%B5%E3%82%92github%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%81%99%E3%82%8B)

そして、git clone する。

```
$ cd /var/www/rails
$ git clone https://github.com/kuboshizuma/cheerfull # 自分のアプリケーション
```

# RDSの設定

EC2の環境構築に一区切りついたのでRDSの設定を行います。

## DBサブネットの登録

RDSで使うサブネットを登録します。
この設定には異なるアベイラビリティゾーンにあるサブネットが最低1つずつ計2つ必要になります。
また、Railsアプリケーションをおいたサブネットはゲートウェイに繋がったpublicなサブネットなので、privateなサブネットを異なるアベイラビリティゾーンに一つずつ作成します。

<img width="1061" alt="スクリーンショット_2017-03-04_11_56_01.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/84d1ca73-f1f3-0821-9a64-fc500dc6bb2a.png">

## パラメータグループの設定

パラメータグループを設定します。
`mysql`を使用していると設定なので、ここではmysqlを選択します。バージョンは適宜選択。
「chara...」で検索すると出て来るcharasetを`utf-8`に変更。

<img width="1425" alt="スクリーンショット_2017-03-04_12_00_57.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/9bea6bab-11a1-600c-f081-58c8f60711b3.png">

設定が完了した後に、パラメータの編集を押してパラメーター変更。

<img width="1201" alt="スクリーンショット_2017-03-04_12_04_29.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/12977c22-e76d-4ac3-d3ec-725ad8d0fe2a.png">

## セキュリティグループの作成


VPCでセキュリティグループを作成します。

<img width="1425" alt="スクリーンショット_2017-03-04_12_08_19.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/72941c4b-2eca-b57b-45a5-d7773be49a3e.png">

インバウンドルールに`MySQL`を設定し、MySQLのアクセスのみ許可します。
「送信元」は`0.0.0.0/0`で設定。特定のRailsアプリケーションサーバーがあるサブネットに限定するほうがよさそう。

<img width="785" alt="スクリーンショット 2017-03-04 12.55.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/b12189f7-c135-6368-52c9-df1d1743f7fe.png">

## RDSインスタンスの作成

### エンジンの選択

<img width="1050" alt="スクリーンショット_2017-03-04_12_12_05.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/3f66d48e-7a4d-494e-56ab-2035717d613f.png">

### 本番稼働用？

<img width="1090" alt="スクリーンショット_2017-03-04_12_12_45.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/b8b9f348-61bc-41c4-c4b3-47b4241de7a2.png">

### DB詳細設定の指定

無料枠はt2.micro

<img width="539" alt="スクリーンショット_2017-03-04_12_17_35.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/fc87dd53-5afd-dd74-9083-7f4e0a581e77.png">

### 詳細設定の設定

各種作成したもので設定。

<img width="553" alt="スクリーンショット_2017-03-04_12_19_44.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/97963c11-8fa7-9fdf-4ced-8ad3f013ec7c.png">

<img width="542" alt="スクリーンショット_2017-03-04_12_19_53.png" src="https://qiita-image-store.s3.amazonaws.com/0/40796/ada701b5-abaa-2228-4689-b44b2e27eed5.png">

## 接続確認

hostは各自の作成したRDSインスタンスのエンドポイントをみる。

```
$ mysql -h hogepoge.ap-northeast-1.rds.amazonaws.com -u shizuma -P 3306 -p
```

接続出来たら完了！


## 絵文字の扱い

絵文字も登録出来るようにする場合。
以下を参考に　`utfmb4`を採用する。

[ActiveRecordをutf8mb4で動かす](http://qiita.com/kamipo/items/101aaf8159cf1470d823)

MAMPでは `/Applications/MAMP/conf/my.cnf` に`my.cnf` を設置した。


# Railsアプリケーションの起動のための準備

## puma setting

以下を追記する。

```rb:cofig/puma.rb
# add setting for production
_proj_path = "#{File.expand_path("../..", __FILE__)}"
_proj_name = File.basename(_proj_path)
_home = ENV.fetch("HOME") { "/home/#{ENV['PUMA_USER']}" }
pidfile "#{_home}/run/#{_proj_name}.pid"
bind "unix://#{_home}/run/#{_proj_name}.sock"
directory _proj_path
# add end
```

`ENV['PUMA_USER']` にサーバーのユーザー名が入るように環境変数を設定。

## database setting

以下のように編集。

```yaml:config/database.yml
production:
  <<: *default
  database: cheerfull
  username: <%= ENV['DATABASE_USER_NAME_PRODUCTION'] %>
  password: <%= ENV['DATABASE_PASSWORD_PRODUCTION'] %>
  host: <%= ENV['DATABASE_HOST_PRODUCTION'] %>
```

それぞれ該当の環境変数を設定。

## rake secret

以下のsecret keyの箇所に値が入るように環境変数を設定。
`rake secret` とコマンドを打つと出て来る文字列を設定する。

```yaml:config/secrets.yml
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
```



```
$ gem install bundler
$ bundle install
```

```
$ rake db:create RAILS_ENV=production
$ rake db:migrate RAILS_ENV=production
```

# PumaとNginxの起動

## Pumaの起動

アプリケーションディレクトリにて以下を実行。

```
$ RAILS_SERVE_STATIC_FILES=true RAILS_ENV=production puma -w 4
```

「Ctr+c」でプロセス消さなければ生き残る。
プロセス消す必要があれば以下のようにする。

```
$ ps aux | grep puma # プロセスIDを探す
$ kill -9 (ID)
```

## Nginx の起動

「cheerfull」の部分は自分のアプリケーションディレクトリ名に変更する。

```bash:/etc/nginx/conf.d/cheerfull.conf
  # log directory
  error_log  /var/www/rails/cheerfull/log/nginx.error.log;
  access_log /var/www/rails/cheerfull/log/nginx.access.log;
  upstream app_server {
    # for UNIX domain socket setups
    server unix:/home/shizuma/run/cheerfull.sock fail_timeout=0;
  }
  server {
    listen 80;
    server_name 12.134.156.178; # 自分のIP
    # nginx so increasing this is generally safe...
    # path for static files
    root /var/www/rails/cheerfull/public;
    # page cache loading
    try_files $uri/index.html $uri @app_server;
    location / {
      # HTTP headers
      proxy_pass http://app_server;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
    }
    # Rails error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/rails/cheerfull/public;
    }
    client_max_body_size 4G;
    keepalive_timeout 5;
  }
```

ユーザーをnginxから自分のユーザー名に変更しておく。

```bash:/etc/nginx/nginx.conf
#user nginx;
user shizuma;
```

nginxを起動します。

```
$ sudo service nginx restart
```

これで、IPアドレスでアクセスするとアプリケーションが表示されるようになりました。

# Capistranoの設定

諸々動くことが確認出来たのでデプロイが出来るように設定します。
デプロイのためにCapistranoの設定をします。
慣れてきたら、いきなりCapistranoの設定をしていけばいいと思います。

socketの場所もアプリケーションディレクトリに変更するのでnginxの設定もそれに合わせて変更します。

「cheerfull」の部分は自分のアプリケーション名に変更する。

```rb:deploy.rb
require 'dotenv'
Dotenv.load

lock "3.7.1"

set :application, "cheerfull"
set :repo_url, "git@github.com:your_gitname/cheerfull.git"
set :branch, 'master'
set :deploy_to, '/var/www/rails/protospacce'
set :linked_files, fetch(:linked_files, []).push('.env')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
set :keep_releases, 5
set :rbenv_ruby, '2.3.1'

# puma setting
set :puma_threads,    [4, 16]
set :puma_workers,    0
set :pty,             true
set :use_sudo,        false
set :stage,           :production
set :deploy_via,      :remote_cache
set :deploy_to,       "/var/www/rails/#{fetch(:application)}"
set :puma_bind,       "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
set :puma_pid,        "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{shared_path}/log/puma.error.log"
set :puma_error_log,  "#{shared_path}/log/puma.access.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true

namespace :deploy do
  desc 'Create database'
  task :db_create do
    on roles(:db) do |host|
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rake, 'db:create'
        end
      end
    end
  end
end
```

実際にdeployする前に、共有ファイルは用意しておきましょう。この場合、`.env`。

ここでpumaのセッティングをしたので、`config/puma.rb` はもう不要になります。
また、dotenvを使用する場合は変更を読み込むために `config/deploy/templates/puma.rb.erb` を用意します。以下を用意。

```erb:config/deploy/templates/puma.rb.erb
#!/usr/bin/env puma

directory '<%= current_path %>'
rackup "<%=fetch(:puma_rackup)%>"
environment '<%= fetch(:puma_env) %>'
<% if fetch(:puma_tag) %>
  tag '<%= fetch(:puma_tag)%>'
<% end %>
pidfile "<%=fetch(:puma_pid)%>"
state_path "<%=fetch(:puma_state)%>"
stdout_redirect '<%=fetch(:puma_access_log)%>', '<%=fetch(:puma_error_log)%>', true


threads <%=fetch(:puma_threads).join(',')%>

<%= puma_bind %>
<% if fetch(:puma_control_app) %>
activate_control_app "<%= fetch(:puma_default_control_app) %>"
<% end %>
workers <%= puma_workers %>
<% if fetch(:puma_worker_timeout) %>
worker_timeout <%= fetch(:puma_worker_timeout).to_i %>
<% end %>

<% if puma_preload_app? %>
preload_app!
<% else %>
prune_bundler
<% end %>

on_restart do
  puts 'Refreshing Gemfile'
  ENV["BUNDLE_GEMFILE"] = "<%= fetch(:bundle_gemfile, "#{current_path}/Gemfile") %>"
  ENV.update Dotenv::Environment.new('.env')
end

<% if puma_preload_app? and fetch(:puma_init_active_record) %>
before_fork do
  ActiveRecord::Base.connection_pool.disconnect!
end

on_worker_boot do
  ActiveSupport.on_load(:active_record) do
    ActiveRecord::Base.establish_connection
  end
end
<% end %>
```

```
$ bundle exec production cap puma:config 
```

これでこの設定を本番に反映。

あとはデプロイコマンドを打つ。

```
$ bundle exec cap production deploy
```

デプロイdone！


nginxのlogの位置やsocketの位置を変更します。

```bash:/etc/nginx/conf.d/cheerfull.conf
  # log directory
  error_log  /var/www/rails/cheerfull/shared/log/nginx.error.log;
  access_log /var/www/rails/cheerfull/shared/nginx.access.log;
  upstream app_server {
    # for UNIX domain socket setups
    server unix:/var/www/rails/cheerfull/shared/tmp/sockets/cheerfull-puma.sock fail_timeout=0;
  }
  server {
    listen 80;
    server_name 12.134.156.178; # 自分のIP
    # nginx so increasing this is generally safe...
    # path for static files
    root /var/www/rails/cheerfull/current/public;
    # page cache loading
    try_files $uri/index.html $uri @app_server;
    location / {
      # HTTP headers
      proxy_pass http://app_server;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
    }
    # Rails error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/rails/cheerfull/current/public;
    }
    client_max_body_size 4G;
    keepalive_timeout 5;
  }
```

```
$ sudo service nginx restart
```

これで、デプロイ環境が整いました。
以前起動したpumaのプロセスはきっておきましょう。

# 複数サーバーへのデプロイ

`multiconfig` を使用します。

```:Gemfile
gem 'capistrano-multiconfig', '~> 3.1', require: false
```

Capfileの`capistrano/setup`から変更。

```rb:Capfile
# require "capistrano/setup"
require 'capistrano/multiconfig'
```

`config/deploy/production.rb` から `config/deploy/app1/production.rb` に変更する。
以下のようにdeploy出来るようになる。

```
$ bundle exec cap app1:production deploy
```

違うサーバーにデプロイするときは同じように `config/deploy/app2/production.rb` を準備すればオーケー。

```
$ bundle exec cap app2:production deploy
```

これで違うサーバーにデプロイ出来る。

これまでと同様にec2サーバーをもう一度用意して2箇所にデプロイ出来るようにする。

# ロードバランサー ELB

ロードバランサーをhttpsで運用したいとき`admin@hoge.com`のようなアドレスへのメールを受信可能のしないといけない。

以下でとりあえずzoneファイルのインポートまでする。
[Amazon Route53 ネームサーバへの移行手順（お名前.comからの）](https://cloudpack.media/12226)

その後にこの記事にあるようにお名前.com側の設定をする。
[お名前.com + Route53で独自ドメインのメールをGmailに転送する](http://marketing-web.hatenablog.com/entry/onamae_mail_domain_route53)

メールの転送が出来ていることを確認する。
そして、ELBの指示に従って設定。
下記参考。
[【初心者向け】ELBにSSL証明書をインストールする](http://dev.classmethod.jp/cloud/aws/aws-beginner-elb-ssl/)

そこまでしたら
[Amazon Route53 ネームサーバへの移行手順（お名前.comからの）](https://cloudpack.media/12226)
の続きでELBとつなげる。

注意点は、ターゲットグループにトラフィックをルーティングするときのプロトコルをHTTPにすること。これでELBへはHTTPSでアクセス出来、それをアプリケーションではHTTPで扱うことが出来る。

また、以下のように nginxのサーバーネームにドメイン名を追加する必要がある。

```
server_name hogepoge.com 00.000.00.00 sakamichi-app-elb-000000000.ap-northeast-1.elb.amazonaws.com
```

# CloudFront

[Amazon CloudFront + ACM 独自ドメインで HTTPS (SSL) 配信設定メモ](https://blog.apar.jp/web/6366/)

# CloudWatchとか

必要の応じて加筆。