EC2内の環境構築【デプロイ】/ 初めてのAWS忘備録③

前回の続き→[AWSインフラ構築【デプロイ】/ 初めてのAWS忘備録②](https://qiita.com/mmaumtjgj/items/922e85c3f88d8db40cdc)

EC2インスタンス内にRailsアプリを動かす環境を作っていく。
最初にPCを購入した際にRailsが動かせるように環境構築をしたことと同様にインスタンス内も同じくRailsが動くように環境構築をしていく。
EC2にログインして、その中のターミナルでコマンドを実行する。

## 利用パッケージの追加
まずはパッケージをアップデートする。
```
[ec2-user@ip... ~]$ sudo yum -y update
```
EC2の初期状態では最低限のパッケージしかインストールされていないので、利用するパッケージをインストールする。
下記のコマンドを``\``を含めて全て実行。

```
[ec2-user@ip... ~]$ sudo yum -y install \
git make gcc-c++ patch curl \
openssl-devel \
libcurl-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
```

## nodejsインストール
まずは``node.js``のパッケージをダウンロードする。
```
[ec2-user@ip... ~]$ curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash -
```
次に``node.js``をインストール。
```
[ec2-user@ip... ~]$ sudo yum install -y nodejs
```
下記コマンドでインストールされたか確認する。
```
[ec2-user@ip... ~]$ which node
/usr/bin/node
```

## yarnインストール
``yarn``も利用するのでインストールする。
``nodejs``と同じように``yarn``のパッケージをダウンロードします。
```
[ec2-user@ip... ~]$ curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
```

yarnをインストール。
```
[ec2-user@ip... ~]$ sudo yum -y install yarn
```

## rubyインストール
``ruby``を利用できるように、``rbenv``と``ruby-build``をインストールしていく。

```
# rbenvインストール
[ec2-user@ip... ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
[ec2-user@ip... ~]$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
[ec2-user@ip... ~]$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
[ec2-user@ip... ~]$ source ~/.bash_profile


# ruby-buildインストールしてrubyをインストール
[ec2-user@ip... ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
[ec2-user@ip... ~]$ rbenv install -v 2.6.5
[ec2-user@ip... ~]$ rbenv global 2.6.5
[ec2-user@ip... ~]$ rbenv rehash
[ec2-user@ip... ~]$ ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
```

## アプリのデプロイ
EC2内にアプリのコードを送る。``GitHub``利用をしてコードをクローンできるようにするため、EC2上で``公開鍵``と``秘密鍵``のペアを作成する。

#### 鍵作成
下記のコマンドで公開鍵と秘密鍵を作成する。

```
[ec2-user@ip... ~]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
# 何も入力せずそのままエンター
Enter same passphrase again: 
# 何も入力せずそのままエンター
Your identification has been saved in /home/ec2-user/.ssh/id_rsa.
Your public key has been saved in /home/ec2-user/.ssh/id_rsa.pub.
The key fingerprint is:
~~省略~~
```

#### GitHubに公開鍵を登録
GitHubと接続できるように作成した公開鍵を登録する。

```
[ec2-user@ip... ~.]$ cd .ssh
[ec2-user@ip... .ssh]$ cat id_rsa.pub
# ここに表示された文字列を全てコピーする(ssh-rsaから)
```

自分のGitHubにログインし``Settings->SSH and GPG keys->New SSH key``に進んで登録。

```
Title：わかりやすい名前
Key：先ほどコピーした公開鍵
```
これでEC2とGitHubでSSH通信ができるようになる。以下確認コマンド。
```
[ec2-user@ip... ~]$ ssh -T git@github.com
# 下記メッセージが出たらyesを入力する
Are you sure you want to continue connecting (yes/no)?
```
「Hi GitHubアカウント名....」が出れば接続完了。

#### GitHubからコードクローン
コードの保存ディレクトリを作成する。
```
[ec2-user@ip... ~]$ cd /
[ec2-user@ip... /]$ sudo mkdir /var/www/
[ec2-user@ip... /]$ sudo chown ec2-user /var/www/
[ec2-user@ip... /]$ cd /var/www
[ec2-user@ip... www]$ git clone git@github.com:アカウント名/アプリ名.git
```

## 本番用のアプリの設定
#### master.key設定
``master.key``は``gitignore``されているため、自分でサーバーに用意する必要がある。ローカルからファイルをコピーする。

下記のコマンドを参考にvimで``config/master.key``というファイルを作り、ローカルの``master.key``の中身をコピーすればOK。
```
[ec2-user@ip... ~]$ cd /アプリ名
[ec2-user@ip... `アプリ名`]$ vim config/master.key
```
>``master.key``は``rails new``した際に作成される。この``master.key``に記載されているキーを利用して``credentials.yml.enc``は暗号化・復号されている。
>
>この``master.key``と``credentials.yml.enc``の組み合わせが合っていないと、``ActiveSupport::MessageEncryptor::InvalidMessage``というエラーメッセージが出る。

#### database.ymlの設定
RDS作成した際のdatabase接続設定をする。
```
[ec2-user@ip... `アプリ名`]$ vim config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci
  username: 設定したユーザ名
  password: 設定したパスワード
  host: RDSのエンドポイント
  pool: 5
  timeout: 5000

production:
  <<: *default
  database: runteq_production # なんでもOK
```
#### gemのインストール
``gem list bundler``と打つと``bundler``のバージョンが確認できるので打ち込んでみる。
```
[ec2-user@ip... `アプリ名`]$ gem list bundler

***LOCAL GEMS***

bundler (default: 1.17.2)
```
デプロイで必要なローカルと同じbundlerのバージョンをインストールする必要がある。
```
[ec2-user@ip... `アプリ名`]$ gem install bundler `ローカルと同じバージョン`
```
準備ができたのでgemをインストールする。
```
[ec2-user@ip... `アプリ名`]$ bundle install
```
※ここでエラーとなりファイルの編集やgemの追加が必要になった場合は、EC2上で作業せずにローカルで作業して``push``したものをEC2上で``pull``する。
リポジトリのソースコードを更新しないと、別環境でデプロイした際に同じ内容のエラーが再発するため注意。

#### DB作成
```
# DBを作成
[ec2-user@ip... `アプリ名`]$ rails db:create RAILS_ENV=production
# テーブル作成
[ec2-user@ip... `アプリ名`]$ rails db:migrate RAILS_ENV=production
```
※デフォルトではdevelop環境への実施になるので、production環境に対して実施。

#### Nginxのインストール
``Nginx``とは、WEBサーバーの１つ。
本番環境は開発環境とは異なり、複数のユーザーから大量のアクセスが発生するので、アプリケーションサーバーだけではリクエストを処理しきれない。

そのためにリクエストごとに発生する処理をさばいてくれるwebサーバーが必要になる。
今回はwebサーバーのnginxを利用する。

```
[ec2-user@ip... ~]$ sudo amazon-linux-extras install nginx1
```
``Is this ok [y/d/N]:``を聞かれた場合は``y``を入力して進む。

下記コマンドで``nginx``を起動して、ブラウザで``nginx``が起動しているか確認する。

```
[ec2-user@ip... ~]$ sudo systemctl start nginx
```
EC2に設定した``ElasticIP``をブラウザのアドレスバーに打って``nginx``のページが表示がされたらok。

今はNginxデフォルトのページに飛ばされているのでこれをRailsアプリに飛ぶように設定をする。

#### Nginxの設定ファイルを作成
Nginxの設定ファイルはインストールした時に生成されている。
``/etc/nginx/nginx.conf``がデフォルトの``Nginx``の設定ファイル。
新しく設定ファイルを作成して``/etc/nginx/nginx.conf``に読み込ませて設定する。

```
[ec2-user@ip... ~]$ cd /etc/nginx/conf.d
[ec2-user@ip... conf.d]$ sudo vi runteq-infra.conf
```
下記を参考にファイルを作成。
※複数箇所にディレクトリ名を指定する箇所があるので自分のアプリと合わせる必要があるので注意。

```
error_log  /var/www/アプリ名/log/nginx.error.log;
access_log /var/www/アプリ名/log/nginx.access.log;

client_max_body_size 2G;
upstream runteq-app {
  server unix:///var/www/アプリ名/tmp/sockets/puma.sock fail_timeout=0;
}
server {
  listen 80;
  server_name xxx.xxx.xxx.xxx; # 作成したEC2の ElasticIPアドレス。
  keepalive_timeout 5;
  root /var/www/アプリ名/public;
  try_files $uri/index.html $uri.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://runteq-app;
  }

  error_page 500 502 503 504 /500.html;
  location = /500.html {
      root /var/www/アプリ名/public;
  }
}
```

#### Pumaを起動する
``Puma``とはアプリケーションサーバーの１つ。（Webサーバとしての機能も併せ持つ）
pumaの本番設定ファイルを確認する(ない場合は作成する)。
```config/puma/production.rb
environment "production"

tmp_path = "#{File.expand_path("../../..", __FILE__)}/tmp"
bind "unix://#{tmp_path}/sockets/puma.sock"

threads 3, 3
workers 2
preload_app!

pidfile "#{tmp_path}/pids/puma.pid"
# stdout_redirect "#{tmp_path}/logs/puma.stdout.log", "#{tmp_path}/logs/puma.stderr.log", true

plugin :tmp_restart
```

下記で起動。
```
[ec2-user@ip... ~]$ cd /var/www/アプリ名/
[ec2-user@ip... アプリ名]$ bundle exec puma -C config/puma/production.r
```

Pumaがうまく起動できたらNginxを再起動する。
```
[ec2-user@ip... アプリ名]$ sudo nginx -s stop
[ec2-user@ip... アプリ名]$ sudo service nginx start
```
エラーログを確認したい場合、EC2のアプリ内、``config/log``の本番環境の中にある。
>PumaはWebサーバとしての機能を兼ねることもできるが、通常の**本番運用ではPumaはアプリケーションサーバーとしての機能に特化**させて、NginxやApache等の**Webサーバーと連携させた構成にすることが多い**。

## 終わりに
クラウドサービスなので、構築内容によっては起動していなくても金額が発生する可能性があるので完了したら、作成した環境を削除しておく。

・EC2は停止して削除する。
・ElasticIPを「アドレスの関連付けの解除」して、ElasticIPアドレスを解放する。
・RDSは停止と削除する。RDSは停止をしても7日後には自動的に起動する仕様なので「削除」までやる。
・その他のVPCやらサブネットやらは残したままでもOK。