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