FargateにRailsをデプロイする (CI/CDまでの道⑦)
はじめに
前回はEC2にDockerをインストールしてコンテナを直接起動しました。
しかし、AWSでコンテナを扱うのであればCI/CDの観点から考えてECSにデプロイする方が望ましいです。
本日はFargateを利用してデプロイに挑戦していきます。
作成に利用するリポジトリはこちら
注意
このハンズオンでは以下の書籍を用います。

AWSではじめるインフラ構築入門 安全で堅牢な本番環境のつくり方
AWSのインフラ環境構築に利用します。書籍で説明が足りている部分については説明を割愛させていただきます。
本ハンズオンの構成は以下となります。

ci/cdまでの道シリーズ
- rails6+mysqlのdocker環境構築 (ci/cdまでの道①)
- dockerにwebpacker環境構築(jquery, bootstrap5, vue) (ci/cdまでの道②)
- rails(docker)に必要なgemを追加する (ci/cdまでの道③)
- rails(docker)にnginxを導入する (ci/cdまでの道④)
- rails(docker)をproductionモードで起動してみる (ci/cdまでの道⑤)
- ec2にdocker-composeでrailsをデプロイする (ci/cdまでの道⑥)
- fargateにrailsをデプロイする (ci/cdまでの道⑦)
環境
- wsl2 (ubuntu20.04)
- docker 20.10.9
- docker-compose 1.29.1
- git 2.25.1
- vscode
AWSインフラ環境の構築
書籍と以下のサイトを用いてFargateにデプロイするところまでのインフラ構築していきます。
(独自ドメインの設定はデプロイができてから説明します)
【ポートフォリオをECSで!】Rails×NginxアプリをFargateにデプロイするまでを丁寧に説明してみた(VPC作成〜CircleCIによる自動デプロイまで) 前編
【ポートフォリオをECSで!】Rails×NginxアプリをFargateにデプロイするまでを丁寧に説明してみた(VPC作成〜CircleCIによる自動デプロイまで) 後編
VPC
書籍4.2通りに作成します。
サブネット
書籍4.3通りに作成します。
インターネットゲートウェイ
書籍4.4通りに作成します。
ルートテーブル
書籍4.6のsample-rt-publicのみ作成します。
セキュリティグループ
まず書籍4.7のsample-sg-elbだけを作成します。
そのあとは以下を作成します。
■ ECSのセキュリティグループ
| 項目名 | 値 |
|---|---|
| セキュリティグループ | sample-sg-ecs |
| 説明 | ecs security group |
| VPC | sample-vpc |
| インバウンドルール | タイプ: HTTP ソース: 0.0.0.0/0 |
それ以外はデフォルト

■ RDSのセキュリティグループ
| 項目名 | 値 |
|---|---|
| セキュリティグループ | sample-sg-db |
| 説明 | db security group |
| VPC | sample-vpc |
| インバウンドルール | タイプ: MYSQL/Aurora ソース: 0.0.0.0/0 |
それ以外はデフォルト

RDS
書籍8章通りに作成するが2点変更する
- パスワードは本ハンズオンでは
passwordとします。

- セキュリティグループはRDSようにする
書籍でdefaultにしているところを、先ほど作成したsample-sg-dbに変更します。

ECRの設定
ここからは記事を参考にECRの設定を行っていきます。
コンソール画面から「Elastic Container Service (以下
ECS)」のダッシュボードを開きます。
「Amazon ECR」の「リポジトリ」の画面を開き、「リポジトリを作成」をクリックします。
まず、Rails用のリポジトリを作成します。
| 項目 | 値 |
|---|---|
| リポジトリ名 | sample-rails |
それ以外はデフォルトで「リポジトリを作成」ボタンをクリック

次にNginx用のリポジトリを作成します。
| 項目 | 値 |
|---|---|
| リポジトリ名 | sample-nginx |
それ以外はデフォルトで「リポジトリを作成」ボタンをクリック

ファイルの修正
Fargateにデプロイするにあたってdocker-compose.production.ymlを利用しないので、必要な設定(コマンドなど)をDockerfileに記述していきます。
FROM ruby:alpine3.13
ARG UID
RUN adduser -D app -u ${UID:-1000} && \
apk update \
&& apk add --no-cache gcc make libc-dev g++ mariadb-dev tzdata nodejs~=14 yarn
ENV RAILS_ENV=production
WORKDIR /myapp
COPY Gemfile .
COPY Gemfile.lock .
RUN bundle install
COPY . /myapp
RUN yarn install
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
RUN chmod +x ./bin/webpack
RUN NODE_ENV=production ./bin/webpack
RUN mkdir -p tmp/sockets
RUN mkdir -p tmp/pids
VOLUME /myapp/public
VOLUME /myapp/tmp
CMD /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
変更点は
- 環境変数の設定(本番)
- rootユーザーに変更 (本番ではファイル編集をしない)
- コンパイルをwebpackに変更 (前回はSprockets(asset:precompile)でコンパイルしてました)
- volumeの追加 (FargateでNginxにボリュームを共有するため)
- CMDで起動コマンドを実行
ここでポイントとなるのは、
VOLUME /myapp/public
VOLUME /myapp/tmp
の部分です。Fargateではdocker-compose(ymlファイル)を利用しないので、ボリュームのマウントをするのにVolumeで利用したところをNginxに共有できるように後ほどFargateに設定します。
この設定がないとデプロイしたときにRailsで以下のようなエラーが発生したり、CSSやJSが適応されなかったり、Nginxで500エラーになったりします。
no such file or directory @ realpath_rec - /myapp/tmp/sockets
connect() to unix:///myapp/tmp/sockets/puma.sock failed (2: no such file or directory) fargate
また、ENV RAIlS_ENV=productionを設定しないと、
RUN NODE_ENV=production ./bin/webpack
でJSしかコンパイルされないようでCSSはコンパイルされずにエラーになりました。Nginxで500エラーになるので必要です。また、pumaの起動にも環境変数が必要なので設定しておいて損なしです
次にcontainers/nginx/Dockerfileを以下に変更します。
FROM nginx:alpine
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*
# Nginxの設定ファイルをコンテナにコピー
ADD /containers/nginx/nginx.conf /etc/nginx/conf.d/myapp.conf
# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
ADDのパスを変更しました。
変更しないとbuildで引っかかるためです。
次にcontainers/nginx/nginx.confを修正します。
# プロキシ先の指定
# Nginxが受け取ったリクエストをバックエンドのpumaに送信
upstream myapp {
# ソケット通信したいのでpuma.sockを指定
server unix:///myapp/tmp/sockets/puma.sock;
}
server {
listen 80;
# ドメインもしくはIPを指定
server_name localhost; # 修正
(省略)
server_nameが前回利用したパブリックIPになっているのでlocalhostに変更します。
次にentrypoint.shを以下に変更します。
#!/bin/sh
set -e
rm -f /myapp/tmp/pids/server.pid
bundle exec rails db:create RAILS_ENV=production
bundle exec rails db:migrate RAILS_ENV=production
bundle exec rails db:seed RAILS_ENV=production
exec "$@"
ENTRYPOINT ["entrypoint.sh"]のタイミングでDB作成などを行います。
注意としてはdb:createは1度のみしか行わないので、1度実行したらコメントアウトして再度ECRにPushします。(あとでやります)
最後にconfig/credential.enc.ymlがあれば削除して、config/credential.enc.ymlとconfig/master.keyを用意してください。こちらについては第5回で説明しています。
ECRにPushする
以下の手順でRailsとNginxのDockerイメージをECRにPushします。
RailsのPush
作成したsample-railsを開きます。

右上のプッシュコマンドを開き、4つのコマンドが出てくるのでコピーしてWSL2上で実行します。

ECR(sample-rails)で登録されていることが確認できました。

NginxのPush
やり方はほとんど同じですが、build(2つ目のコマンド)を以下に変更します。
docker build -f ./containers/nginx/Dockerfile -t sample-nginx .
Dockerfileの場所がカレントディレクトリにないため-fをつけます
あとはRails同様に行います。
ECSの設定
コンソール画面から「Elastic Container Service (以下
ECS)」のダッシュボードを開きます。
■ タスク定義の作成
「Amazon ECR」の「タスク定義」の画面を開き、「新しいタスク定義の作成」をクリックします。
デフォルト値から変更する値を表に示します。
| 項目名 | 値 |
|---|---|
| 起動タイプの互換性の選択 | Fargate |
| タスク定義名 | sample-ecs |
| タスクロール | ecsTaskExecutionRole |
| タスクメモリ (GB) | 0.5GB |
| タスク CPU (vCPU) | 0.25 vCPU |
| コンテナの追加 | コンテナ名: rails イメージ: [ECRのsample-railsのURL] ポートマッピング: 3000 環境変数 DB_USERNAME: admin DB_PASSWORD: password DB_DATABASE: myapp DB_HOST: [RDSのエンドポイント] -------------------------------------- コンテナ名: nginx イメージ: [ECRのsample-nginxのURL] ポートマッピング: 80 ボリュームソース: rails |

Fargateを選択
「次のステップ」をクリック

タスク定義名、タスクロール、タスクメモリ(GB)、タスクCPU(vCPU)を設定
「コンテナの追加」をクリック
- Railsコンテナの追加


コンテナ名、イメージ、ポートマッピング、環境変数を入力
イメージはECRで確認可能

「追加」をクリック
- Nginxコンテナの追加


コンテナ名、イメージ、ポートマッピング、ボリュームソースを設定して「追加」をクリック
ボリュームソースを設定することでrailsのDockerfileで設定したVolumeの内容を共有することができます
「作成」をクリックしてタスク定義を作成します。
クラスター設定
「Amazon ECR」の「クラスター」の画面を開き、「クラスターの作成」をクリックします。
デフォルト値から変更するものを表に示します。
| 項目名 | 値 |
|---|---|
| クラスターテンプレートの選択 | ネットワークのみ |
| クラスター名 | sample-cluster |

ネットワークのみを選択

クラスター名を入力して「作成」をクリック

作成したクラスター(sample-cluster)をクリック
「サービス」→「作成」をクリック
デフォルト値から変更するものを表に示す
| 項目 | 値 |
|---|---|
| 起動タイプ | FARGATE |
| サービス名 | sample-service |
| タスクの数 | 1 |
| クラスターVPC | sample-vpc |
| サブネット | sample-subnet-public01 |
| セキュリティグループ | sample-sg-ecs |

「次のステップ」をクリック


クラスターVPC、サブネット、セキュリティグループを設定
「次のステップ」→「次のステップ」→「作成」をクリック
デプロイの確認
「クラスター」→「sample-cluster」→「sample-service」からFargateのステータスを確認 (RUNNINGになればOK)

「タスク(英数字列)」をクリックして「パブリックIP」を確認

ChromeでパブリックIP/testにアクセスします。

/testを忘れないようにしてください。
※ 500エラーの場合はコンパイルがうまくいっていない事が多いのでローカルで起動して、/log/production.logを確認していきます
独自のドメインでアクセスできるようにする
ドメインの取得
書籍10.4を参考に取得します
SSLサーバー証明書発行
書籍10.7.1を行います。
ECRの更新
entrypoint.shでdb:createをしましたが、既に1度行っており、再度実行するとエラーになってしまうのでコメントアウトしておきます。また、migrateとseedも現在必要ではないので合わせてコメントアウトしています。
#!/bin/sh
set -e
rm -f /myapp/tmp/pids/server.pid
# bundle exec rails db:create RAILS_ENV=production
# bundle exec rails db:migrate RAILS_ENV=production
# bundle exec rails db:seed RAILS_ENV=production
exec "$@"
ECRの既に登録したイメージを削除してください。
そのあと先ほど実行したプッシュコマンドでrails-sampleを更新します。
ロードバランサーの設定
「ロードバランサー」→「ロードバランサーの作成」を行い、書籍の7.2.4の流れで作成します。修正箇所だけ表に示します。
| 項目 | 値 |
|---|---|
| Listener | Protocol: HTTPS Port: 443 Forward to: sample-tg |
| Default SSL certificate | from ACM: 取得したドメイン |
ターゲット作成は以下で設定します。
| 項目 | 値 |
|---|---|
| choose a target type | IP address |
| Target group name | sample-tg |
| Network | sample-vpc |
| IPv4 address | ECSのプライベートIP |


Include as pending belowを押して登録します。
「crate target group」で作成します。
ロードバランサーの設定には作成したターゲットグループを指定します。HTTPSも加えます。

Default SSL certificateに先ほど追加した証明書を選択します。
ここではwww.our-aws.link
設定したら「create load balancer」をクリックします。
Route53の設定
「Route53」から「ホストゾーン」画面に移動して取得したドメインをクリックします。
「レコードを作成」をクリックします。
「シンプルルーティング」を選択して「次へ」
「シンプルなレコード定義」をクリックします。
| 項目 | 値 |
|---|---|
| レコード名 | www |
| 値/トラフィックのルーティング先 | Application Loadbalancer |
| リージョン | ap-northeast-1 |
| ロードバランサーを選択 | 作成したロードバランサー |
値を入れていきます。

「シンプルなレコードを定義」→「レコードを作成」をクリックします。
ECSの再作成
先ほど作成したsample-serviceを削除します。

そして、「作成」をクリックして先ほどの手順でサービスを作成していきます。
今回は「ロードバランシング」の設定を行います。
| 項目 | 値 |
|---|---|
| ロードバランサーの種類 | Application Load Balancer |
| ロードバランサー名 | sample-elb |
| コンテナ名:ポート | nginx:80:80 (ロードバランサーに追加クリック) |
| ターゲットグループ | sample-tg |

それ以外は前回と同じ手順になります。
デプロイの確認
ドメイン/testにアクセスします。

/testが必要です。表示されれば成功です。
コンテナの共有の問題で表示まで時間がかかるかもしれないです(502エラーやエラー画面になる)
おわりに
作成したものはこちらのリポジトリに用意しています。
一度Fargateにデプロイに成功してそこから記事完成までに7時間もかかりましたがなんとか完成させることができました。
やっとCI/CDの道のスタート地点に立てました。次回はCodeBuildに挑戦していこうかなと思います。
Discussion