AWSのEC2インスタンスにアプリケーションサーバーを作成する方法

# はじめに
AWSはAmazon Web Servisesの略で、Amazonが提供しているクラウドサーバーのサービスです。
AWSへの理解度を高めるために、学んだことをアウトプットします。


# 今回の流れ
* アプリケーションにUnicornをインストール
* AWSのEC2で作成したインスタンスにアプリケーションサーバーを作成
* 本番環境の設定
* アプリケーションの起動

インスタンスは作成していることを前提とします。
※インスタンスの作り方が分からない方はこちらを参照ください。
「AWSに仮想サーバーを構築する方法」https://qiita.com/daisuke30x/items/91307c94e8f5a702a42f

# アプリケーションにUnicornをインストール

①自身のアプリケーションにUnicornのインストール

アプリケーション内のGemfileに以下の記述を加える。

```:Gemfile
group :production do
    gem 'unicorn', '5.4.1'
end
```
※もし、Herokuでデプロイ済みの場合は、rails_12factorのGemがあると思いますが、そちらのGemは削除かコメントアウトを行ってください。

```:ターミナル
% bundle install
```

②Unicornの設定

アプリケーションのconfig内にunicorn.rbを作成します。
そして、以下の設定を行います。

```:config/unicorn.rb
#サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../', __FILE__)

#アプリケーションサーバの性能を決定する
worker_processes 1

#アプリケーションの設置されているディレクトリを指定
working_directory app_path

#Unicornの起動に必要なファイルの設置場所を指定
pid "#{app_path}/tmp/pids/unicorn.pid"

#ポート番号を指定
listen 3000

#エラーのログを記録するファイルを指定
stderr_path "#{app_path}/log/unicorn.stderr.log"

#通常のログを記録するファイルを指定
stdout_path "#{app_path}/log/unicorn.stdout.log"

#Railsアプリケーションの応答を待つ上限時間を設定
timeout 60

#以下は応用的な設定なので説明は割愛

preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true

check_client_connection false

run_once = true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.connection.disconnect!

  if run_once
    run_once = false # prevent from firing again
  end

  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exist?(old_pid) && server.pid != old_pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH => e
      logger.error e
    end
  end
end

after_fork do |_server, _worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end
```

リモートリポジトリにpushし、反映させます。


# AWSのEC2で作成したインスタンスにアプリケーションサーバーを作成
③インスタンスにアプリケーションをインストール

```:ターミナル
インスタンスにログイン
% ssh -i キーペア名.pem ec2-user@<パブリックIP>

インスタンス内に/var/www/ディレクトリの作成
[インスタンス]$ sudo mkdir /var/www/

作成したwwwディレクトリの権限をec2-userに付与
[インスタンス]$ sudo chown ec2-user /var/www/

wwwディレクトリに移動
[インスタンス]$ cd /var/www/

# GitHubからアプリケーションをクローン
[インスタンス www]$ git clone <GitHubのデプロイしたいアプリケーションのCodeボタンから取得したHTTPSのURL>
```

④Gemのインストール

* Gemのインストールを行う前にSwapファイルでメモリの増強（Gemのインストール時にメモリ不足でエラーにならないようにするため）

```:ターミナル
インスタンス内のホームディレクトリに移動
[インスタンス www]$ cd ~

Swapファイルの設定
[インスタンス]$ sudo dd if=/dev/zero of=/swapfile1 bs=1M count=512
[インスタンス]$ sudo chmod 600 /swapfile1
[インスタンス]$ sudo mkswap /swapfile1
[インスタンス]$ sudo swapon /swapfile1
[インスタンス]$ sudo sh - -c 'echo "/swapfile1  none        swap    sw              0   0" >> /etc/fstab'
```

* Gemのインストール

```:ターミナル
インスタンス内のアプリケーションディレクトリに移動
[インスタンス]$ cd /var/www/<アプリケーション名>

bundlerをインストール(bundlerのバージョンはターミナルで「command + T」で別タブを作成し、新たに作成したタブ内で「bundler -v」を実行すると確認できます)
[インスタンス<アプリケーション名>]$ gem install bundler -v <bundlerのバージョン>

Gemをインストール
[インスタンス<アプリケーション名>]$ bundle install
```

# 本番環境の設定
⑤環境変数の設定

データベースのパスワードなどはセキュリティのため、GitHubにアップロードしません。
そのような情報は「環境変数」を利用して、設定します。

* secret_key_baseの作成

### secret_key_baseとは

Cookieの暗号化に用いられる文字列のことです。
Railsアプリケーションを本番環境で動作させる際に必須です。

```:ターミナル
[インスタンス<アプリケーション名>]$ rake secret
=> secret_key_baseとなる文字列が表示される

インスタンス内でホームディレクトリへ移動する
[インスタンス<アプリケーション名>]$　cd ~

/etc/environmentを編集する
[インスタンス]$ sudo vim /etc/environment

「i」を打ち込み、入力モードへ

設定済みデータベースのrootユーザーのパスワードを入力
DATABASE_PASSWORD='データベースのrootユーザーのパスワード'
SECRET_KEY_BASE='さきほど作成したsecret_key_base'

※AWSのS3を使用している場合は以下を入力（S3の設定の際にダウンロードしたCSVファイルを参考に値を入力）
AWS_ACCESS_KEY_ID='ここにCSVファイルのAccess key IDの値をコピー'
AWS_SECRET_ACCESS_KEY='ここにCSVファイルのSecret access keyの値をコピー'

※Basic認証を導入している場合は以下を入力（設定したユーザー名とパスワードを入力）
BASIC_AUTH_USER='設定したユーザー名'
BASIC_AUTH_PASSWORD='設定したパスワード'

「:wq」で保存

環境変数を反映させるためにインスタンスからログアウトし、再度ログインする
[インスタンス]$ exit
% ssh -i キーペア名.pem ec2-user@<パブリックIP>

環境変数の確認
[インスタンスリ]$ env
=> 上で設定したものが表示されたらOK。
```

⑥セキュリティグループにポートの解放

AWSマネジメントコンソールにてインスタンスのセキュリティグループのインバウンドルールに「カスタムTCP」の「ポート3000」、「ソース0.0.0.0/0」を追加します。


⑦Railsアプリケーションの設定変更

上でインスタンス内に設定した環境変数をアプリケーションにも設定する必要があります。

```：config/database.yml
production:
  <<: *default
  database:（※ここは編集しない）
  username: root
  password: <%= ENV['DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock
```

設定を変更後、GitHubへpushします。

その後、インスタンスにアプリケーションの変更を反映させます。

```:ターミナル
インスタンス内でアプリケーションディレクトリへ移動
[インスタンス]$ cd /var/www/<アプリケーション名>

アプリケーションの設定変更を反映させる
[インスタンス<アプリケーション名>]$ git pull origin master
```

# アプリケーションの起動
⑧データベースを作成（インスタンスにデータベースサーバーが存在している場合）

データベースサーバーの作成の方法はこちらを参考にどうぞ　https://qiita.com/daisuke30x/items/7501d724a0727ad9f2e4

```:ターミナル
データベースの作成
[インスタンス<アプリケーション名>]$ rails db:create RAILS_ENV=production
[インスタンス<アプリケーション名>]$ rails db:migrate RAILS_ENV=production

データベースの起動
[インスタンス<アプリケーション名>] $ sudo systemctl start maradb
```
RAILS_ENV=productionは本番環境を指定するオプション

⑨アプリケーションの起動

* アプリケーションの起動

```:ターミナル
[インスタンス<アプリケーション名>]$ bundle exec unicorn_rails -c config/unicorn.rb -E production -D
```

* アセットファイルのコンパイル

アプリケーションのアセットファイル(CSSやJavaScript、画像など)をコンパイルします。
本番環境では必要な作業です。

```:ターミナル
[インスタンス<アプリケーション名>]$ rails assets:precompile RAILS_ENV=production
```

* アプリケーションの再起動

```:ターミナル
プロセスの確認
[インスタンス<アプリケーション名>]$ ps aux | grep unicorn
=> 左から2つ目の数字がプロセスID

プロセスの停止
[インスタンス<アプリケーション名>]$ kill <unicorn_rails masterのプロセスID>

再度アプリケーションの起動
[インスタンス<アプリケーション名>]$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D
```
「RAILS_SERVE_STATIC_FILES=1」はコンパイルしたアセットファイルを見つけられるように指定する役割

**ブラウザで 「http://＜パブリックIP＞:3000/」 や　「http://＜Elastic IP＞:3000/」を入力し、アプリケーションが表示されれば成功です。**


# 最後に
本投稿が初学者の復習の一助となればと幸いです。