Docker+Rails+AWS(EC2+RDS)を使ってデプロイした

#概要
AWSを使ってアプリケーションを公開している人は多く、私も実際に体験してみたくタイトル通りDockerで環境構築し、Railsでアプリケーションを開発し、AWSで実際にデプロイすることにチャレンジしました。
AWSは無料枠内にてデプロイまで目指します。

#前提
* アプリケーションはGithubを使って管理する。
* 各サービスのアカウント登録は済ませている。(AWS、Github等)
* AWSはIAMユーザーを作成し、必要なセキュリティ、ポリシーを割り当てている。
* アプリケーションはDockerを使用して環境構築している。

#AWSの設定

AWSのサービスの設定は極力IAMユーザーにて行います。まず、AWSにIAMユーザーでログインする。

* VPCの作成

まずはVPCの作成を行います。VPCサービスのページを開き、メニューからVPCを選択。VPCの作成を選択します。
VPC名、CIDRブロックを設定し、VPCを作成します。

* サブネットの作成

サブネットの作成を行います。
サブネットはEC2用のサブネットを1つ、RDS用のサブネットを2つ用意します。
メニューからサブネットを選択し、**サブネットの作成**を選択します。
先ほど作成したVPCを選択し、サブネット名、アベイラビリティゾーン、CIDRブロックを設定し、**サブネットを作成**を選択します。
※RDS用のサブネットはアベイラビリティゾーンを別々にします。

* インターネットゲートウェイの作成

インターネットに接続するためにインターネットゲートウェイを設定します。
メニューから**インタネットゲートウェイ**を選択し、**インターネットゲートウェイの作成**を選択します。
名前を設定し、**インターネットゲートウェイの作成**を選択します。
**アクション**から**VPCにアタッチ**を選択し、作成したVPCにアタッチします。

* EC2インスタンスの作成

EC2ダッシュボードに開き、**インスタンスを起動**を選択し、EC2インスタンスを作成します。
EC2インスタンスは無料枠で使えるマシンイメージとインスタンスタイプを選択します。
セキュリティグループは新しく作成します。
SSH接続を行いたいのでSSHの設定を行います。

| タイプ | プロトコル | ポート範囲 | ソース |
|:-:|:-:|:-:|:-:|
| HTTP  | TCP  | 80  | 0.0.0.0/0  |
| SSH  | TCP  | 22  | マイIP  |

設定に問題がなければ**起動**を選択します。
インスタンスを作成するときにキーペアを作成します。
キーペアはEC2にSSH接続する際必要となります。**新しいキーペアの作成**を選択し、キーペア名を設定しローカルにダウンロードしておきます。

インスタンスメニューの[ステータスチェック]が終了し、[インスタンスの状態]が[実行中]になれば、無事インスタンスが起動したこととなります。

続いてElastic IPを設定します。
メニューより**Elastic IP**を選択します。**Elastic IP アドレスの割り当て**を選択し、新しいアドレスの割り当てを選択し、**割り当て**を選択します。
作成したElastic IPを選択し、**アクション**から**Elastic IPアドレスの関連付け**を選択します。
作成したEC2インスタンスを設定してElastic IPアドレスをEC2に関連付けます。

* RDSの作成

RDSを作成する前に、RDS用のサブネットグループの作成を行います。
RDSメニューより**サブネットグループ**を選択し、**DBサブネットグループの作成**を選択します。

サブネットグループ名、VPCを選択し、RDS用に作成したサブネット2つを設定し、作成をします。
RDSサービスを開き、**データベースの作成**を選択します。
データベースはMySQLを選択します。基本的には既存の設定で大丈夫ですが、**無料利用枠**という箇所は忘れずに選択します。バージョン等は自身の使用するバージョンを設定します。

データベース名、マスターユーザー名、マスターパスワードは適宜設定します。
後に、Railsの設定に必要となります。（作成後、確認することができます）
作成したVPC、サブネットグループを選択します。セキュリティグループは**新規作成**を選択し、新しいセキュリティグループを作成します。
**データベースの作成**を選択し、データベースを作成します。

データベース用のセキュリティグループのインバウンドルールを変更します。

| タイプ | プロトコル | ポート範囲 | ソース |
|:-:|:-:|:-:|:-:|
| MYSQL/Aurora | TCP | 3306 | EC2のセキュリティグループのID |

#Railsの設定
* RDSの設定

RDSの情報をcredentials.yml.encに書き込みます。

```terminal:
docker-compose run -e EDITOR="vim" app rails credentials:edit
```

```yml:credentials.yml.enc
rds:
  host: RDSのエンドポイント
  database: RDSのデータベース名
  username: RDSのマスターユーザの名前
  password: RDSのマスターパスワード
```

config/database.ymlのproductionの箇所にRDSの情報を挿入します。

```yml:database.yml
#省略
production:
  <<: *default
  host: <%= Rails.application.credentials.rds[:host] %>
  database: <%= Rails.application.credentials.rds[:database] %>
  username: <%= Rails.application.credentials.rds[:username] %>
  password: <%= Rails.application.credentials.rds[:password] %>
```

* Docker-compose.ymlの編集

データベースはRDSを使用するためdb:の箇所は全てコメントアウトします。volumes:のmysql-data:もコメントアウトします。
アプリケーションを本番環境で立ち上げるため、app:のcommand:に-e productionを追加します。

```yml:docker-compose.yml
version: '3'

services:
  # db:
  #   image: mysql:5.7
  #   command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  #   env_file:
  #     - ./.env
  #   volumes:
  #     - mysql-data:/var/lib/mysql
  #   ports:
  #     - "4306:3306"

  app:
    build: .
    env_file:
      - ./.env
    command: bundle exec puma -C config/puma.rb -e production
    init: true
    volumes:
      - .:/myproject
      - public-data:/myproject/public
      - tmp-data:/myproject/tmp
      - log-data:/myproject/log
    # depends_on:
    #   - db

  web:
    build:
      context: containers/nginx
    init: true
    volumes:
      - public-data:/myproject/public
      - tmp-data:/myproject/tmp
    ports:
      - 80:80
    # depends_on:
    #   - app

volumes:
  # mysql-data:
  public-data:
  tmp-data:
  log-data:
```

* nginx.comfの編集

server_nameのxx.xx.xx.xxの箇所にElastic IPアドレスを入力します。

```conf:nginx.conf
upstream myproject {
  server unix:///myproject/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name xx.xx.xx.xx [or localhost];

  #省略
  }
}
```

* その他の設定

その他Github等外部に知られたくないKEYや環境変数などは.envファイルを作成し、そちらに書き込みます。dotenv-railsを使うとGitを使用する際管理をし易くなります。

```:gemfile
gem 'dotenv-rails'
```

```terminal:
$ bundle install
```

.gitignoreファイルに.envファイルを記述することで、githubにpushされなくなります。

```:.gitignore
/.env
```

config/environmentsのproduction.rbを編集します。
下記内容を編集すると、本番環境で立ち上げ時に便利です。

```ruby:production.rb
config.assets.js_compressor = Uglifier.new(harmony: true)
config.assets.compile = true
```

**以上、完了したらアプリケーションをgithubへpushします。**

#AWSでアプリケーションをデプロイ
* EC2のSSH接続する。

EC2にSSH接続するための設定を行います。

```terminal:
$ mkdir ~/.ssh
$ mv ~/Downloads/myapp.pem ~/.ssh/
$ chmod 600 ~/.ssh/myapp.pem
$ ssh -i ~/.ssh/myapp.pem ec2-user@xxx.xxx.xxx.xxx
```

myapp.pemはダウンロードしたキーペアの名前です。chmodで権限を変更後、キーペアを使用してEC2にログインします。ec2-userはAWS linuxのデフォルトユーザーです。xxx.xxx.xxx.xxxはEC2インスタンスのElastic IPを入力します。

まず初めにyumをアップデートします。

```terminal:
[ec2-user@ip-xxx-xxx-xxx-xxx ~]$ sudo yum update -y
```

* EC2にDocker環境を作る

EC2にDockerとdocker-composeをインストールします。

```terminal:
# Dockerをインストール
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo yum install -y docker
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo service docker start
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo usermod -G docker ec2-user
[ec2-user@xxx.xxx.xxx.xxx ~]$ exit
$ ssh -i ~/.ssh/myapp.pem ec2-user@xxx.xxx.xxx.xxx
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo chkconfig docker on
# docker-composeをインストール
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo curl -L "https://github.com/docker/compose/releases/download/インストールするバージョン/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo chmod +x /usr/local/bin/docker-compose
```

* gitをインストール

Githubからアプリケーションをクローンするためにgitをインストールします。

```terminal:
[ec2-user@xxx.xxx.xxx.xxx ~]$ sudo yum install -y git
[ec2-user@xxx.xxx.xxx.xxx ~]$ git clone [Githubのパス]
```

* ローカルから必要ファイルをコピー

credentialsの解読するためのmaster.keyと.envファイルをローカルからコピーします。

```terminal:
[ec2-user@xxx.xxx.xxx.xxx ~]$ exit
$ scp -i ~/.ssh/myapp.pem master.key ec2-user@xx.xxx.xxx.xx:myapp/config/
$ scp -i ~/.ssh/myapp.pem .env ec2-user@xx.xxx.xxx.xx:myapp/
```

* コンテナの起動

```terminal:
$ ssh -i ~/.ssh/myapp.pem ec2-user@xxx.xxx.xxx.xxx
[ec2-user@xxx.xxx.xxx.xxx ~]$ cd myapp
[ec2-user@xxx.xxx.xxx.xxx ~/myapp]$ docker-compose build
[ec2-user@xxx.xxx.xxx.xxx ~/myapp]$ docker-compose up -d
[ec2-user@xxx.xxx.xxx.xxx ~/myapp]$ docker-compose exec app bin/rails db:create db:migrate RAILS_ENV=production
```

Elastic IPアドレスにアクセスして正しく表示されれば完了です。

#まとめ
AWSを使ってデプロイをすることは、初学者にはかなりハードルが高い内容だと思っておりましたが、素晴らしい文献等が豊富にあったため乗り越えることができました。
所々修正したり、試行錯誤しながらデプロイまで行ったので、全く同じやり方でできるかはわかりませんが、現場私ができる最適な方法だと考えております。

**気になった点**
参考記事によっては「この方法では開発環境で動いてしまうのでは？」「本番環境でないためRDSが使用されていないのではないか？」と思う部分もあり、いくつかの記事を参考にさせて頂きました。database.ymlのproduction:をRDSに指定したのであれば、本番環境でアプリケーションを立ち上げなければRDSは機能しないと考えております。（勘違いであれば申し訳ありません）
docker-composeの設定次第でなんとかできるのかもしれませんが、もう少しリサーチをしてみようと思いました。

**課題**
せっかくDockerを使用したのでAWSのECSやFargateを駆使してデプロイすることを考えるべきだったと思いました。
次の機会があれば挑戦したいと思います。

**その他**
私はS3も取り入れました。そちらは別記事にてまとめております。
[AWS_S3+Ruby on Rails_Active Storageで画像データを保存](https://qiita.com/u-ten/items/c2cf5ebfd4dbeab7bde7)

#参考記事
[無料！かつ最短？で Ruby on Rails on Docker on AWS のアプリを公開するぞ。](https://qiita.com/at-946/items/1e8acea19cc0b9f31b98#2-1-ec2%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)
[EC2上でRailsアプリケーションにDockerを導入する(Rails、Nginx、RDS)](https://qiita.com/Yusuke_Hoirta/items/959f91a61ac1e607c0b1)
[見ながらやろう! AWSを始めよう -VPC構築編-](https://qiita.com/nago3/items/f5badeb4f7e5c32b0540)
[Rails5.2から追加された credentials.yml.enc のキホン](https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39)