(ECS,Rails6)ロードバランサー ALB と データベース RDS とECRを使ってコマンドラインから ECSにRails6のアプリをデプロイ

# はじめに

ECSの概要とECS CLIを使ってアプリのデプロイの仕方について書いていきます。

ECSやその他のサービスに関して、そこまで理解できてないので間違ってる部分も多いと思います。

とりあえず記事通りに作業したらRails6のアプリケーションをECSにデプロイできる様に記事を書きたいと思います。

もしうまくいかなかったらおそらく権限関係なので権限を見直してみてください。

# ECSの概要

ECSはdockerのコンテナをそのままaws上にデプロイできるサービスです。

docker-hubやecrなどのdockerイメージのレジストリからイメージを持ってきてコンテナを作成してデプロイできます。

自分がやった場合、下記の様になりました。

![ecs-chart.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/8698227e-d83d-1bcf-ff8f-708938f90607.png)

ECSはクラスターと言うEC2のインスタンス群みたいなのをまず作ります。

このクラスター内にEC2があり、その中にECSのタスクと言うのを作ります。

このタスク上でdockerのコンテナが起動される形になります。

dockerコンテナの起動に関してはecsにタスク定義を作成してこのタスク定義を元にdockerコンテナを作成していくことになります。

dockerコンテナの起動に必要なdockerイメージに関してはdockerイメージレジストリから取ってきます。

docker hubの場合は誰でも見れてしまうのでecrを使うのが良いと思います。

作成したコンテナはALBを通して公開されます。

ALBを使うことで負荷がかかった時に新しくタスクの作成し負荷を分散できます。

作成したコンテナに関してはec2インスタンスにkeypairを使ってそこからdockerコンテナにアクセスする形になります。

ちなみにFargateを使う場合は確かコンテナにアクセスできなかった気がします。


# デプロイ

前提として
- PCはMac
- ローカルPCにdockerとmysqlがインストールされていること

## Railsアプリの作成

まずRailsアプリの作成をします。

適当なフォルダを作成してそこに Dockerfile docker-compose.yml Gemfile Gemfile.lock を作成します。

内容は下記の様にします。

Dockerfile

```Dockerfile
FROM node:13.5-alpine as node

RUN apk add --no-cache bash curl && \
    curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.21.1


FROM ruby:2.6.5-alpine

COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /opt/yarn-* /opt/yarn
RUN ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn
RUN apk add --no-cache git build-base libxml2-dev libxslt-dev postgresql-dev postgresql-client tzdata bash less && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

RUN apk add --no-cache alpine-sdk \
    mysql-client \
    mysql-dev

ENV APP_ROOT /app
RUN mkdir $APP_ROOT
WORKDIR $APP_ROOT

RUN gem update --system && \
    gem install --no-document bundler:2.1.4

ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

RUN bundle install

ADD . $APP_ROOT
```


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

services:
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_PASSWORD: password
      MYSQL_USER: myuser
      MYSQL_DATABASE: ecs_development
    ports:
      - "3306:3306"
  web:
    build: .
    command: bash -c "yarn install && rails db:migrate && rails s -p 3000 -b '0.0.0.0'"
    ports:
      - "3000:3000"
    volumes:
      - .:/app
    tty: true
    links:
      - db


```

Gemfile

```Gemfile
# frozen_string_literal: true

source "https://rubygems.org" 

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'rails', '~> 6.0.2', '>= 6.0.2.2'
```

Gemfile.lockは中身は空でOK


ファイルが作成できたら

```
docker-compose run --rm web rails new . --database=mysql --skip-bundle --skip-test
```

このコマンドを実行します。

そうするとrailsのファイルがローカルに作成されていると思います。


config/database.ymlのdevelopmentの部分を下記の様に変更します。

```database.yml
development:
  <<: *default
  host: db
  username: myuser
  password: password
  database: ecs_development
```

そしたら

```
docker-compose up --build
```

コマンドを実行します。

コンテナが起動したらrails6はwebpackerを使っているので下記コマンドを実行します。

```
docker-compose exec web rails webpacker:install
```

そうするとlocalhost:3000にアクセスすると見慣れた画面が表示されると思います。

![スクリーンショット 2020-05-16 22.23.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/0f64d104-eb55-4359-a82e-d0c65ca39f2a.png)

scaffoldでuserのcrudを作ります。

```
docker-compose exec web rails generate scaffold user name:string age:integer
docker-compose exec web rails db:migrate
```

config/routes.rbにroutesを追加します。

```routes.rb
root to: "users#index"
```

これでlocalhost:3000にアクセスすると

![スクリーンショット 2020-05-17 10.13.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/d0d33327-8989-62d5-84ee-c98a99fcb9c8.png)

この画面が表示されると思います。

とりあえずこれでアプリのベースの作成は終わりです。

## aws-cli ecs-cliのインストール

デプロイに使うツールをインストールします。

```
brew install awscli
brew install amazon-ecs-cli
```

## aws configureの設定

aws configureを設定します。

まずIAMユーザーを作る必要があるのでAWSコンソールにアクセスします。

IAMの画面を表示してユーザーの追加ボタンをクリックします。

![スクリーンショット 2020-05-17 10.17.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/a19696a2-3a97-d848-64a4-5223dfbfa622.png)

プログラムによるアクセスにチェックをつけ次へボタンを押します。

![スクリーンショット 2020-05-17 10.18.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/4d62d175-824f-22e7-d24c-0170b4132383.png)

ポリシーのアタッチ画面にて

- AmazonECS_FullAccess
- AmazonEC2ContainerRegistryFullAccess

を追加します。

そのまま次へボタンを押していくと最後にユーザーのアクセスキーとシークレットキーが表示されるので保存しておきます。

そしたらaws configureの設定をします。

コマンドラインで

```
aws configure
```

と入力して

access_keyとsecret_keyの入力

regionはap-northeast-1と入力します。


これで aws configureの設定は終わりです。

IAMユーザーの権限は不足しているので後々追加していきます。

## クラスターの作成

クラスターの作成時にec2のキーペアを設定するのでまずキーペアの作成を行います。

EC２の画面を表示するとそのサイドメニューにキーペアと言うものがあります。

![スクリーンショット 2020-05-17 15.11.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/86eb89c8-aa12-8053-157c-4f24efbe09b3.png)


このキーペアをクリックしてキーペアを作成し、pemファイルをローカルpcにおきます。

自分の場合はホーム直下にaws-keypairフォルダを作成してそこに置いています。

ファイルに関しては権限を変更しておく必要があります。

自分の場合は下記の上になります。

```
chmod 400 aws-keypair/qiita-tutorial.pem
```

ファイルのパスは自分の環境のものに変更してください。

それではECSのクラスターを作成していきます。

クラスターの作成に関してはECS_FullAccessで付与されないが必要な権限が結構あるので別途追加する必要があります。

IAMのポリシーからポリシー作成をクリックします。

ビジュアルエディタでサービス、アクションを選択していきます。

まずIAMに関しては

- AddRoleToInstanceProfile
- CreateInstanceProfile
- CreateRole
- DeleteInstanceProfile
- DeleteRole
- PassRole
- RemoveRoleFromInstanceProfile
- AttachRolePolicy
- DetachRolePolicy

を追加します。

次にEC2に関して

- DeleteInternetGateway
- DeleteRouteTable
- DeleteSecurityGroup

を追加します。

最終的に下記の画像の様になればOKです。

![スクリーンショット 2020-05-17 16.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/c2ba0473-c015-b44b-ce2b-84a1e456ccd9.png)


ポリシーの作成をしたら作成したポリシーをユーザーにアタッチします。

IAMのユーザーから作成したユーザーをクリックしてアクセス権限の追加をクリックします。

![スクリーンショット 2020-05-17 15.44.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/e9fadb67-a692-7142-5157-18654beb6c01.png)

そしたら既存のポリシーを追加をクリックして作成したポリシーを追加します。

ポリシーの追加が終わったら

下記のコマンドを実行します。

アクセスキーとシークレットキーに関してはIAMユーザーを作成したときの物を利用してください。

キーペアの名前も自分が作成したキーペアの名前に変更してください。

```
ecs-cli configure --cluster qiita-tutorial --default-launch-type EC2 --config-name qiita-tutorial --region ap-northeast-1
ecs-cli configure profile --access-key access_key --secret-key secret_key --profile-name qiita-tutorial-profile
ecs-cli up --keypair qiita-tutorial --capability-iam --size 1 --instance-type t2.micro --cluster-config qiita-tutorial --ecs-profile qiita-tutorial-profile
```

実行するとクラスターの作成が行われます。

--size 1 と設定しているのでec2インスタンスが一つ立ち上がっているはずです。

![スクリーンショット 2020-05-17 16.40.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/47f1d4b6-837a-59d1-1416-784f0dcf9fb7.png)


これでクラスターの作成は終わりです。

## RDSの作成

デプロイの準備は整いましたがデータベースにrdsを使うのでrdsの作成を行なっていきます。

rdsの画面を表示してデータベースの作成をクリックします。

![スクリーンショット 2020-05-17 16.44.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/bab7c980-efec-389b-4164-d9dba3517fb9.png)

簡単作成、MySQL,無料利用枠を選択します。

DB インスタンス識別子、マスターユーザー名、パスワードを設定して作成します。

作成できたら作成したrdsをクリックすると接続とセキュリティの部分にエンドポイントが記載されています。

ローカルPCからrdsへアクセスができる様にするためにRDSの変更をクリックして

パブリックアクセシビリティ をありにします。

![スクリーンショット 2020-05-17 22.08.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/2eda5d4a-6035-b098-5e64-d77d7a508872.png)


またセキュリティーグループの設定も必要なので行います。

作成したrdsのセキュリティーと接続の部分にVPC セキュリティグループの欄があります。

クリックできる様になっているのでクリックします。

rdsのセキュリティーグループが表示されると思います。

インバウンドルールの部分をクリックします。

タイプはMYSQL/Auroraを選択します。

ソースの設定は下記の画像の様にしましたが細かい設定を行いたい場合は適宜行なってください。

![スクリーンショット 2020-05-17 22.13.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/5d0c8971-cfad-79e1-9867-04efc3643c8b.png)


そしたらローカルPCから下記コマンドでrdsに接続します。

```
mysql -h rdsのエンドポイント -u rdsのユーザー名 -p
```

これでパスワードを入力するとrdsに接続できるはずです。

そしたらデータベースの作成を行います。

```
CREATE DATABASE ecs_development default character set utf8;;
```

これでrdsにデータベースの作成ができました。


## database.ymlの修正

host,username,passwordを環境変数を見る様に修正します。

```config/database.yml
development:
  <<: *default
  host: <%= ENV['MYSQL_HOST'] %>
  username: <%= ENV['MYSQL_USER'] %>
  password: <%= ENV['MYSQL_PASSWORD'] %>
  database: ecs_development
```

# ALBの設定

次にALBの設定を行なっていきます。

EC2の画面の再度メニューからロードバランサーをクリックします。

そしたらロードバランサー作成をクリックします。

そしたらApplication Load Balancerの作成をクリックします。

![スクリーンショット 2020-05-17 22.37.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/1745a50e-c9b5-e3ed-6584-5046dd8b0dba.png)


まず名前を入力します。

リスナーはそのままで大丈夫です。

![スクリーンショット 2020-05-17 22.39.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/9cafee3e-a659-d630-0581-14717b631c84.png)


vpcの設定ですがecsのec2インスタンスのvpcを選択しなくてはいけません。

確認の仕方はクラスターのタスクのコンテナインスタンスをクリックします。

![スクリーンショット 2020-05-17 22.41.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/3de93aee-f0d6-942c-7650-3737c1594625.png)

ec2インスタンスがあるのでクリックします。

![スクリーンショット 2020-05-17 22.41.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/d610aa20-1833-7b69-6ea2-e6fe4a0263e5.png)


ec2インスタンスが表示されます。

したの説明の部分にvpcのidが記載されています。

確認したらそのvpcをロードバランサーの作成の部分のvpcの選択でそれを選択します。

セキュリティーグループは新しいセキュリティーグループを作成をクリックします。

そして下記の画像の様に設定します。

![スクリーンショット 2020-05-17 22.56.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/45f27a54-310c-6bca-f5cb-66d929aa961f.png)



ターゲットグループの設定の部分で新しいターゲットグループを選択して名前を入力します。

ターゲットはインスタンスを選択します。

![スクリーンショット 2020-05-17 22.44.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/c54242e2-e47c-427e-8950-d429313457bf.png)

次へいくとec2インスタンスの選択画面が出るのでecsで使うec2インスタンスを選択して登録済みに追加ボタンをクリックします。

あとは次へを押して作成すればOKです。

## config/environment/development.rbの修正

rails6ではhost名を明示的に記載しないと起動しません。

なのでconfig/environment/development.rbに下記を追加します。

```development.rb
config.hosts << ALBのdns名を記載
```

## ecrにイメージの登録

ecsで利用するイメージをecrに登録していきます。

ecrの画面を開きレポジトリの作成をクリックします。

適当に名前を入力して作成します。

レポジトリの作成が終わったらローカル環境でdockerイメージをビルドしてpushします。

まず

```
docker build . -t ecrのURI
```

を実行します。

そしたら次に下記コマンドでecrにpushします。

```
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ecrのURI
docker push ecrのURI
```

これでecrへのpushはOKです。

## デプロイ

デプロイの準備ができたのでデプロイします。

まずデプロイ用のdocker-compose.ymlを作成します。

名前はdocker-compose-deploy.ymlとします。


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

services:

  web:
    image: rdsのURI
    command: bash -c "yarn install && rails db:migrate && rm /app/tmp/pids/server.pid && rails s -p 3000 -b '0.0.0.0'"
    environment:
      MYSQL_USER: rdsのuser名
      MYSQL_PASSWORD: rdsのpassword
      MYSQL_HOST: rdsのエンドポイント
      TZ: "Japan"
    ports:
      - "80:3000"
    logging:
      driver: awslogs
      options:
        awslogs-group: ecs-tutorial
        awslogs-region: ap-northeast-1
        awslogs-stream-prefix: web
```

そしたら

```
ecs-cli compose -f docker-compose-deploy.yml up --create-log-groups --cluster-config qiita-tutorial --ecs-profile qiita-tutorial-profile
```

このコマンドを実行します。

そうするとタスクが起動します。

ecsの画面で実行中のタスクに1と書いてあればOKです。

![スクリーンショット 2020-05-17 22.33.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/6529d5ff-74df-5f56-3f1d-bdbc64945d23.png)

もしうまくいかない場合はcloud watchにログが出されていると思うのでそちらを確認してください。

## 確認

これでデプロイできているはずなのでALBのdns名をurlに貼り付けます。

そうすると....


![スクリーンショット 2020-05-17 22.59.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/54b7f7e1-63f5-fa53-4739-dae7af0b1b59.png)

無事開けました。

もちろんデータの登録もできます。

![スクリーンショット 2020-05-17 23.05.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/5852de3f-cb58-0880-2622-7504d266a370.png)

## コンテナにアクセス

とりあえずこれでデプロイはOKですがecsコンテナにアクセスもしてみます。

まずecsで使われているec2インスタンスのセキュリティーグループを確認してインバウンドルールに関して全てのマイIPからの全てのトラフィックを許可します。

![スクリーンショット 2020-05-17 23.11.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/e4c06887-b42e-328a-c078-c0d71e1b0bf6.png)

ecsコンテナへのアクセスにはsshを使用します。

まずecsで起動しているec2インスタンスのプライベートipをコピーします。

そして

```
ssh -i keypairのパス ec2-user@ec2インスタンスのパブリックdns
```

を実行します。

そうするとec2インスタンスにssh接続できるので

```
docker ps
```

を実行します。

そうするとコンテナが起動しているので

```
docker exec -i -t コンテナID bash
```

を実行するとbashに入れます。

試しに

```
rails -v
```

と実行すると

```
bash-5.0# rails -v
Rails 6.0.3
```

こう返ってきました

これでecsで起動したdockerへのアクセスはOKです。




# 終わり

とりあえずこれで終わりです。

いろいろ問題点あるとは思うのでお気づきの点があれば指摘していただけると助かります。