AWS ECS のハンズオンを参考に Rails を ECS にデプロイする例



# 前提

前提としてECSの公式ハンズオンを完了しており、Hello Worldと表示されるWebアプリが動作したところまで進めていること

https://catalog.us-east-1.prod.workshops.aws/workshops/7ffc4ed9-d4b3-44dc-bade-676162b427cd/ja-JP/5-ecs/2-ecs

VPC/セキュリティグループ/ECSクラスタなどはそれぞれ作成できていること

# rails new でプロジェクトを作成する

localで作成

`rails new example`
`cd example`

# hostsの設定

development環境でデプロイさせる場合は、Railsアプリにアクセスできるようにhostsを編集する
(本来であればproduction環境でデプロイすべきだろうが、本記事ではRailsの起動が目的なので省略する)

`config/environments/development.rb`

```rb
require "active_support/core_ext/integer/time"

Rails.application.configure do
  # 大雑把に指定する場合
  config.hosts << ".ap-northeast-1.elb.amazonaws.com"

  # ちゃんと指定する場合
  # config.hosts << "lb-*************.ap-northeast-1.elb.amazonaws.com"

...
```

よく分からなければ何も設定せずにデプロイすると、エラー画面でhostsの設定例が表示されるので、そちらを参考にして編集すると良い

# Dockerfile

rails プロジェクトのディレクトリと同じ場所にDockefile を作成する

ここではRailsの起動ポートを80にしておく
(ECSのハンズオンでもポート80を利用しているため、手間が少ないそちらに合わせた。他ポートでのデプロイは自分がまだ試していない)

```dockerfile
FROM ruby:3.1.2

RUN mkdir /api
WORKDIR /api
COPY Gemfile /api/Gemfile
COPY Gemfile.lock /api/Gemfile.lock

# Bundlerの不具合対策(1)
RUN gem update --system
RUN bundle update --bundler

RUN bundle install --jobs=4
COPY . /api

# developmentの場合の例
CMD bundle exec rails s -p 80 -b '0.0.0.0'

# productionの場合の例
# CMD RAILS_LOG_TO_STDOUT=1 RAILS_ENV=production bundle exec rails server -p 80 -b '0.0.0.0'
```

# local での起動

local で動くかどうか確認する

```
docker build -t rails-ex .
docker run -it -p 3000:80 rails-ex
```

 # ECR に push する

具体的なコマンド例はECRの画面に丁寧に表示されるのでそちらを利用する

コマンド例:

```
docker tag rails-ex:latest 3049353535252.dkr.ecr.ap-northeast-1.amazonaws.com/rails-ex:latest
docker push 3049353535252.dkr.ecr.ap-northeast-1.amazonaws.com/rails-ex:latest
```

Dockerfile などを更新してpushし直すときも全く同じ手順で良さそう ( build -> tag -> push )

ちなみに tag を省略すると最新イメージは反映されない様子
イメージが反映されたかどうかは AWS ECR の一覧で「プッシュされた日時」を見れば分かる

# ECS のタスク定義

RDSにpushしたイメージをコンテナに指定してタスクを定義する

コンテナのイメージURIはRDSのイメージ名と同じ

例: 3049353535252.dkr.ecr.ap-northeast-1.amazonaws.com/rails-ex:latest

# ECSサービスを作成

- ファミリーに先程作成したrails用のタスクを指定する
- ハンズオンで作成したネットワーキング設定を使う
- ロードバランサの指定をする
- ターゲットグループはてきとうな文字列を指定してOK
- Listnerのポートは80を利用

など公式ハンズオンと同じ感じで進めていく

サービスを作成すると、CloudFormationでサービス作成のログが閲覧できる

![image](https://user-images.githubusercontent.com/13635059/216900140-06f74be1-0871-4824-9d09-e8ebe6f5b62c.png)

Railsがうまく起動するとサービスのログも出力される
自分が試した実感だとサービス作成から5分-10分ほどの時間はかかっていた

![image](https://user-images.githubusercontent.com/13635059/216900130-859139d3-5972-4304-8008-e3c075ccf648.png)

# dockerイメージ変更の反映

「サービスの更新」からデプロイし直しができるようだ
自分も最初は1回1回サービスを削除したり作成し直していたがdockerのイメージを変更した時はこちらが手軽かも

ただECRイメージをpushし直して「サービスの更新」を走らせても何も変化が起きない場合もがあった
うまくデプロイが走らない時はタスク定義のリビジョンを変えて試してみると良いかも知れない

ちなみにデプロイ状態はCloudFormationではなく サービス > 「デプロイとイベント」で見られる模様

![image](https://user-images.githubusercontent.com/13635059/216974493-80afae9b-836d-4035-81df-2fa19fe013b7.png)

![image](https://user-images.githubusercontent.com/13635059/216974497-722ec46b-6acf-42b6-8f52-07cb1449d47d.png)


# ブラウザでアクセス

サービスの「ネットワーキング」タブにリンクがあるのでそちらを開く
リンクがない場合はロードバランサの指定が抜けているかもしれないのでサービスを削除して作成し直す

![image](https://user-images.githubusercontent.com/13635059/216900617-bc0efbd0-39f6-45de-aca1-c6b107b9b2f0.png)

URLの例:

`http://lb-*************.ap-northeast-1.elb.amazonaws.com/`

ブラウザのキャッシュ残り対策としてはURL末尾に適当なパラメータをつけてアクセスするのが手軽

`http://lb-*************.ap-northeast-1.elb.amazonaws.com/?35353`


# ハンズオンのはまりどころ

Railsとは直接関係ないがメモ

- セキュリティグループでアクセスを許可出来ていないともちろんWebからアクセスは出来ない
- 同じような手順でサービス作成しているにも関わらず謎にデプロイが失敗する場合が多かった。CloudFormationにも具体的なログが表示されない。サービスの作成・終了を繰り返していたからだろうかと思うが気のせいかもしれない。最悪クラスタを作り直しながら試した。
- サービス作成・削除などを短いスパンで繰り返すと、前回のサービス削除が完了しないままに新しいサービスを作成している場合がある。前回の削除が完了してからサービスを新規作成する方が良いかも知れない。削除にも数分の時間はかかる様子。クラスタも作り直して試すほうが手っ取り早くクリーンな環境で試行錯誤できるかもしれない(気のせいでなければ)。もしくはひとつのクラスタでサービスの削除が完了するのを待ちながら、複数のクラスタを並行して使ってみるとか。
- サービス作成時にロードバランサの設定を飛ばすとグローバルIPからアクセスできない。そういうものなのだろうか。
- VPCの設定でサブネットの片方をプライベートにしていた影響でうまくサービスが動かない場合があったかもしれない。気のせいかもしれないが。ハンズオンではサブネットの2個ともパブリック設定なのでそちらに合わせておくと良い。
- サービス作成時に「既存のロードバランサ」が選べたり選べなくなったりする。理由は不明。



# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura