Railsアプリのデプロイ方法について（手動編）

自身で作成したアプリ（Rails）をデプロイする流れを学習したので、備忘録もかねて纏めます。

＊今回まとめた大変な手動でのデプロイ作業を Capistrano というライブラリを使うと自動化できます。
こちらも別途記事投稿します。

#全体の流れ
1. EC2のサーバに作成したコードをクローンする準備
まずはEC2のサーバに作成したコードを設置します。
そのために、Githubにあるコードのリモートリポジトリからコードをクローンしてくる準備をします。

2. アプリケーションサーバの設定
Railsを動かすためには、アプリケーションサーバと呼ばれる種類のソフトを起動する必要があります。
その後、コードをEC2へクローンします。

3. 本番環境でのRailsの設定
最後に、開発環境と本番環境での差異を埋めるための作業を行い、しっかりと公開できているか確認します。


##1. EC2のサーバに作成したコードをクローンする準備
まず、アプリケーションのコードをGithubからEC2サーバへクローンします。
全世界に公開できるIPアドレスを持ったEC2サーバ上でアプリを動かすためです。
ただし、現状のままEC2サーバにアプリケーションのコードをクローンしようとしても'permission denied'とエラーが出てしまいます。
これはGithubからみた時に、EC2インスタンスが何者なのかわからないため、発生しているエラーです。
このエラーを解消するためにGithubにSSH鍵を登録しましょう。

###GithubにSSH鍵を登録
EC2インスタンスからGithubにアクセスするためには、作成したEC2インスタンスのSSH公開鍵をGithubに登録する必要があります。
SSH鍵をGithubに登録すると、Githubはそれを認証に利用し、コードのクローンを許可してくれるようになります。
それでは、ターミナルで以下のコードを入力しましょう。
*EC2にしっかりログインしていることを確認してください。
*途中で入力を求められることがありますが、全て何も入力せずにEnterキーで進んでください。

```:terminal（EC2サーバ）
$ ssh-keygen -t rsa -b 4096
```
上記コードを入力することでEC2サーバのSSH鍵ペアが作成できます。

続いて、作成したSSH鍵の内容を確認します。

```:terminal（EC2サーバ）
$ cat ~/.ssh/id_rsa.pub
```
ここで表示された鍵の内容をコピーし、Github上に登録します。
下記URLから'New SSH Key'ボタンから登録をしてください。
名称は自由で、'key'のスペースに先ほどコピーした鍵の内容を貼り付けて登録します。
https://github.com/settings/keys

鍵をしっかりと登録できているかチェックします。

```terminal（EC2サーバ）
$ ssh -T git@github.com
=>ここで、このまま接続していいか質問されますので、yesとすると、以下のコメントが出てきます。
　以下のコメントが出てくれば、無事、登録できてます！
Hi ユーザー名! You've successfully authenticated, but GitHub does not provide shell access.
```

##2. アプリケーションサーバの設定
###単語
まず、以下の単語について、確認します。

-アプリケーションサーバ(Appサーバ)
ブラウザからの「リクエスト」を受け付けRailsアプリケーションを実際に動作させるソフトウェアのこと。

-Unicorn
全世界に公開されるサーバ上で良く利用されるアプリケーションサーバ。
unicorn_railsコマンドで起動することができます。

###Unicornに関する設定
それでは、作成したアプリでUnicornを使用できるようにしましょう。

```rb:Gemfile
group :production do
  gem 'unicorn', '5.4.1'
end
```
*group :production do ~ endの間に記述されたgemは本番環境のみで読み込まれます。

bundle installを忘れずに。

```:terminal
$ bundle install
```

続いて、configディレクトリに'unicorn.rb'というファイルを作成し、Unicornの設定を記載していきましょう。
記載に関しては、各々の内容になると思われますので、割愛いたしますが、重要な単語を以下に記載しておきます。
＊参照に記述方法の書かれたページを貼り付けておきますので、気になる方は別途参照ください。

-worker(ワーカー)
Unicornは、プロセスを分裂させることができ、この分裂したプロセス全てをworkerと呼びます。
プロセスを分裂させることで、リクエストに対してのレスポンスを高速にすることができ、
worker_processesという設定項目で、workerの数を決定します。


| 設定項目| 詳細 |
|:-:|:-:|
|worker_processes| リクエストを受け付けレスポンスを生成するworker(ワーカー)の数を決めます。|
| working_directory|UnicornがRailsのコードを動かす際、ルーティングなど実際に参照するファイルを探すディレクトリを指定します。|
| pid | Unicornは、起動する際にプロセスidが書かれたファイルを生成します。その場所を指定します。 |
| listen | どのポート番号のリクエストを受け付けることにするかを決定します。今回は、3000番ポートを指定しています。 |

**エラー対策**
-Uglifier(gem)/JavaScriptでテンプレートリテラル記法を使用している時。
元々このgemはJavaScriptを軽量化するためのものが、テンプレートリテラル記法に対応していません。
そのため、デプロイ時にエラーの原因となるので、この部分をコメントアウトすることで対策します。

```rb:config/environments/production.rb
config.assets.js_compressor = :uglifier
=>コメントアウトしましょう。
```

###変更修正をリモートリポジトリに反映
ローカルのフォルダ内で変更修正を行ったので、こちらをリモートリポジトリへ反映します。
変更修正をGitHub Desktopからコミットしてプッシュしましょう。
この時必ず、**masterブランチ**で行うようにしてください。
別のブランチでコミット&プッシュした場合は、リモートリポジトリでプルリクエストを作成し、ブランチをmasterへマージしてください。

###Githubからコードをクローン
続いて、Unicornの設定を済ませたコードをEC2インスタンスにクローンしましょう。

まず、以下のコマンドを入力して、ディレクトリを作成します。
*今回は、ここで作成したディレクトリにアプリケーションを設置することにします。

```:terminal（EC2サーバ）
#ディレクトリを作成
$ sudo mkdir /var/www/

#作成したwwwディレクトリの権限をec2-userに変更
$ sudo chown ec2-user /var/www/
```

続いて、Githubから「リポジトリURL」を取得します。
リポジトリURLはGithubのページ上、緑色の'clone or download'というボタンを押すと出てきます。

```:terminal（EC2サーバ）
#先ほど作成したディレクトリに移動
$ cd /var/www/
$ git clone　[コピーしたURL]
```

以上でアプリケーションのコードをEC2サーバにクローンすることができました。

###クローンしたアプリを起動するためにGemを使用可能に
まずローカルで設計したアプリのbundlerのバージョンをチェックします。

```:terminal(ローカル）
#バージョンチェック
$ bundler -v
Bundler version 2.0.２
```

バージョンがわかりました。今回は2.0.2ですね。
それでは、本番環境でgemを実装しましょう。

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

#ローカルで確認したbundlerのverを導入します。
$ gem install bundler -v 2.0.2

$ bundle install
```

###環境変数の設定
環境変数：データベースのパスワードなどセキュリティのためにGithubにアップロードすることができない情報がある時に、環境変数を利用して設定します。
環境変数は、Railsからは ENV['<環境変数名>'] という記述でその値を利用することができます。

ローカルで作成したアプリにおける「config/secrets.yml」 と 「config/database.yml」 をチェックしてみましょう。
<%= ENV["SECRET_KEY_BASE"] %> と書かれている部分があります。
これはSECRET_KEY_BASE という環境変数の値になります。

secret_key_base
Cookieの暗号化に用いられる文字列です。Railsアプリケーションを動作させる際は必ず用意する必要があります。また、外部に漏らしてはいけない値であるため、こちらも環境変数から参照します。

secret_key_baseは以下のコマンドを打つことで生成できます。

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

#secret_key_baseを生成します。
$ rake secret
69619d9a75b78f2e1c87ec5e07541b42f23efeb6a5........
↑これがsecret_key_baseです。メモしておきましょう。
```

続いて環境変数の設定をしましょう。
環境変数は '/etc/environment' というファイルに保存することで、サーバ全体に適用されます。
環境変数の書き込みはvimコマンドを使用して行います。

```:terminal（EC2サーバ）
$ sudo vim /etc/environment

#MySQLのパスワードと先ほどのsecret_key_baseを入力します。（＝の前後にスペースは入れないでください。）
DATABASE_PASSWORD='MySQLのrootユーザーのパスワード'
SECRET_KEY_BASE='先程コピーしたsecret_key_base'
```

書き込みができたら 「esc(エスケープキー)」→「:wq」と入力して内容を保存します。
保存できたら環境変数を適用するために一旦ログアウトします。

```:terminal（EC2サーバ）
$ exit
logout
Connection to 52.xx.xx.xx closed.
```

念のため、しっかり保存できているか確認しましょう。

```:terminal（EC2サーバ）
$ env | grep SECRET_KEY_BASE
SECRET_KEY_BASE='secret_key_base'

[ec2-user@ip-172-31-23-189 ~]$ env | grep DATABASE_PASSWORD
DATABASE_PASSWORD='MySQLのrootユーザーのパスワード'
```
'secret_key_base'と'MySQLのrootユーザーのパスワードが先ほど入力したものと同じであれば、OKです！

###ポートの解放
立ち上げたばかりのEC2インスタンスはSSHでアクセスすることはできますが、HTTPなどの他の通信方法では一切つながらないようになっています。
そのため、WEBサーバとして利用するEC2インスタンスは事前にHTTPがつながるように「ポート」を開放する必要があります。

セキュリティグループ
EC2サーバが属するまとまりのようなもので、複数のEC2インスタンスのネットワーク設定を一括で行うためのものであり、ポートの設定をするためにはEC2の「セキュリティグループ」という設定を変更する必要があります。

AWSを使用している場合は、AWSにログインいただき、EC2=>インスタンス=>セキュリティグループから設定できます。
そこで、’インバウンド'=>'編集'=>'ルールの追加'で追加できます！


##3. 本番環境でのRailsの設定
それでは、いよいよ本番環境でのRailを起動しましょう。
とその前に作成したローカルファイルにおける「database.yml」の本番環境の設定を編集しましょう。

```rb:config/database.yml（ローカル）
#username以下を下記のように変更しましょう。
production:
  <<: *default
  database: ~~~（それぞれのアプリケーション名によって異なっているので、こちらは編集しないでください）
  username: root
  password: <%= ENV['DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock
```

上記編集が完了次第、ローカルでの編集をコミットして、GitHubにプッシュしましょう。
リモートリポジトリが更新されたため、サーバ上のアプリケーションにも反映させましょう。
先ほどはgit cloneコマンドを利用しましたが、今回はすでにEC2とGithubは接続できているため、git pullコマンドを利用します。

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

$git pull origin master
```

続いて、データベースを作成しマイグレーションを実行し直しましょう。

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

$ rails db:create RAILS_ENV=production
Created database '<データベース名>'
$ rails db:migrate RAILS_ENV=production
```
*Mysql2::Error: Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'というエラーが起こった場合、mysqlが起動していない可能性があります。
sudo service mysqld startというコマンドをターミナルから打ち込み、mysqlの起動を試してみましょう。


これでデータベースの準備が整いました。Railsを起動しましょう。
unicorn_railsコマンド
$ unicorn_rails -c config/unicorn.rb -E production -D
-c config/unicorn.rb は設定ファイルの指定。
-E production は環境を「本番モードとして動作させる」。
-Dは「Daemon(デーモン)」の略で、プログラムを起動させつつターミナルで別のコマンドを打てるようにするオプション。


``````:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

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

これで、移管は完了です。
http://＜サーバに紐付けたElastic IP＞:3000/でアクセスしてみましょう。
無事Railsが起動できましたが、今のままではレイアウトが崩れてしまっていると思われます。
これは、開発中はアクセス毎にアセットファイル（画像・CSS・JSファイルの総称）を自動的にコンパイル(圧縮)する仕組みが備わっていますが、本番モードのときにはパフォーマンスのためアクセス毎には実行されないようになっているためです。
ということで、アセットコンパイルを実行しましょう。

###アセットコンパイルを実行

``````:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

rails assets:precompile RAILS_ENV=production
```
コンパイルが成功したら反映を確認するため、Railsを再起動します。
そのために、まずは今動いているUnicornをストップします。
Unicornのプロセスを確認し、プロセスを止めましょう。
ターミナルからプロセスを確認するにはpsコマンドを利用します。

psコマンド
現在動いているプロセスを確認するためのコマンドです。



``````:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

#[aux]と打っているのは、psコマンドのオプションで、表示結果を見やすくしてくれます。
#| grep unicornとしているのはpsコマンドの結果からunicorn関連のプロセスのみを抽出するためです。
$ ps aux | grep unicorn
```

ここで表示される情報ない、左から2番目の列に表示されるのがプロセスのid、つまりPIDになります。
「unicorn_rails master」と表示されているプロセスがUnicornのプロセス本体です。
早速このプロセスを終了させましょう。
ターミナルからプロセスをストップするにはkillコマンドを利用します。

killコマンド
現在動いているプロセスを停止させるためのコマンドです。

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

$ kill <確認したunicorn rails masterのPID>
=>再度、$ ps aux | grep unicornで状態を確認しましょう。
　うまくプロセスをストップできていない場合は、下記コマンドで強制終了できます。
$ kill -9 [プロセスID]
```


それでは、再びunicornを起動しましょう。
このとき RAILS_SERVE_STATIC_FILES=1 という指定を先頭に追加してください。
これは、コンパイルされたアセットをRailsが見つけられるような指定になります。

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D
```

以上で、設計したアプリがデプロイできているはずです！
長々と説明して参りましたが、以上がデプロイの概要となります。


##番外編
###EC2の能力拡張について
特に何もしていないEC2のインスタンスではコンピューターの能力が足りず、Gemのインストール時などにエラーが発生する可能性があります。具体的には、コンピューターの処理能力に関係するメモリというものが足りません。
そこで、今後の設定を行う前にメモリを増強する処理を行います。
具体的にはSwap領域をうまく使うことで処理落ちを防ぎます。

Swap(スワップ)領域とは。。。
メモリが使い切られそうになった時にメモリの容量を一時的に増やすために準備されるファイルのこと。
コンピュータが処理を行う際、メモリと呼ばれる場所に処理内容が一時的に記録されますが、メモリの容量は決まっており、容量を超えてしまうとエラーで処理が止まってしまいます。
そこで、Swap領域を使うことで処理落ちを防ぎます。
＊EC2はデフォルトではSwap領域を用意してません。

```:terminal（EC2サーバ）
#まず、ホームディレクトリに移動
＄cd

#コマンドオプションの「of」にファイルのパスを指定して「bs」に基準となる容量の単位を指定して「count」にbsの量を指定します。
#dd if=/dev/zero of=作成するファイル名 bs=ブロックサイズ(byte) count=ブロック数
$ sudo dd if=/dev/zero of=/swapfile1 bs=1M count=512
# しばらく待って、以下のように表示されれば成功
512+0 レコード入力
512+0 レコード出力
536870912 バイト (537 MB) コピーされました、 7.35077 秒、 73.0 MB/秒

#パーミッションの設定をします。
$ sudo chmod 600 /swapfile1

#mkswapコマンドでスワップ領域を作成します。
$ sudo mkswap /swapfile1
# 以下のように表示されれば成功
スワップ空間バージョン1を設定します、サイズ = 524284 KiB
ラベルはありません, UUID=74a961ba-7a33-4c18-b1cd-9779bcda8ab1

#swaponコマンドを使って，スワップ領域を有効にします。
$ sudo swapon /swapfile1

#リダイレクトの設定をします。
$ sudo sh -c 'echo "/swapfile1  none  
```

###unicorn操作時のエラーについて
Unicorn関係で発生したエラーはlog/unicorn.stderr.logに記録されます。
そのため、log/unicorn.stderr.logを確認して発生しているエラーを確認しましょう

```:terminal（EC2サーバ）
#該当のファイルの場所に移動します。
$ cd  /var/www/[リポジトリファイル名]

$ less log/unicorn.stderr.log
```
上記コマンドでログが確認できますので、エラーの内容をチェックして対応しましょう。
lessコマンドはq, Q, :q, :Q, ZZのいづれかを入力すれば、とまります。

###エラーチェック
-pushのし忘れ、またはEC2サーバ側でのpullのし忘れは無いか
-EC2サーバ側で、/var/www/<リポジトリ名>/log/unicorn.stderr.logをlessまたはcatコマンドで確認し、
エラーが出ていないか確認する（下に行くほど最新のログです。時刻表記がUTCであることに注意してください）
-ローカルでの編集のpushやEC2でのgit pullを忘れていないか
-mysqlの起動は正しく行えているか
-EC2サーバ側のSECRET_KEY_BASE等は正しく設定できているか
-EC2インスタンスの再起動を行ってみる

以上となります。最後までご覧いただき、ありがとうございました！
今後も学習した事項に関してQiitaに投稿していきますので、よろしくお願いします！
記述に何か誤りなどございましたら、お手数ですが、ご連絡いただけますと幸いです。

##参照
Unicorn設定のまとめ
https://qiita.com/syou007/items/555062cc96dd0b08a610

Swap fileの作り方
https://gist.github.com/koudaiii/0ed6a8558aa297af463e

sudo で書き込みをしたい
https://qiita.com/jiz/items/d4f05ffacedefc11f14d

What does `sudo sh -c` means in linux?
https://www.reddit.com/r/learnprogramming/comments/3bsct5/what_does_sudo_sh_c_means_in_linux/

lessコマンド（よく使いそうなものだけ版）
https://qiita.com/inokou/items/a73bac8c7e03951d07be