概要
RailsアプリケーションをECS on Fargate + RDS にデプロイする手順について説明します。
今回は説明のため、全てのリソースをマネジメントコール画面(2023.9.10)で入力しています。
ECSやALB、RDSなど、複数のAWSサービスを使用しますが、それぞれのサービスに対して説明はしないです。
誰に向けて?
- AWSの知識は豊富だが、経験が浅い人
- マネジメントコンソールからサクッと作成したい人
- 環境構築滅多にしないから、すぐに忘れちゃう人
AWSリソースの構成
基本的にAWS公式のベストプラクティスに則った構成で作成しています。
下記参照ください。
参考: Amazon Elastic Container Service
参考: Task Networking in AWS Fargate
迷いやすい点、少し構成を考えた点があるので、それらも後ほど紹介します。
アーキテクチャー図
diagramsで作成しています。
github Actionsは、自動デプロイ処理をしようと思っていましたが、結構長くなったので、別記事にしました。
必要なリソース
必要なリソースと、ほんの少しの説明だけを書き上 げてみました。
アーキテクチャー図と、下記の情報で、ぽちぽちすれば大体できると思います。
- VPC: 10.1.0.0/16
- subnet
- public: 10.1.0.0/24
- public: 10.1.1.0/24
- private: 10.1.2.0/24
- private: 10.1.3.0/24
- NAT gateway
- セキュリティグループ
- ALB用
- インバウンドルール: port 443/80
- ECS用
- インバウンドルール: port 3000(railsのデフォルト)
- RDS用
- インバウンドルール: port 3306, source: ECSのアクセス
- ALB用
- subnet
- RDS(PostgreSQL14)
- ドメイン
- お名前.com(ドメイン取得)
- Route53(ドメイン管理)
- ACM(SSL/TLS証明書の取得)
- ALB
- ECR
- ECS
- クラスター
- サービス
- タスク定義
迷いやすい点、少し構成を考えた点
個人的に、いつも迷いやすい点、またECSの構成で少し考えた点を紹介します。
VPCやサブネットのIPアドレス構成について
VPCは、サブネットマスクを/16にすることで、65536のIPアドレスが取得できることになります。拡張性を考慮していると言えます。
サブネットは、サブネットマスクを/24にすると、第3オクテットを0,1,2,3で取得した時に、256のIPアドレスに分割できます。管理がしやすく、IPアドレスも十分取得できます。
なので、この構成にしています。
ECSをプライベートサブネットかパブリックサブネットのどちらに配置するか?
今回は、プライベートサブネットにしましたが、AWSのベストプラクティスにはどちらも紹介されています。
ALB経由するなら、プライベートサブネット1択でしょ。という方が多いかもしれません。
ただ、プライベートサブネットにすると、後述のNATゲートウェイ(もしくはVPCエンドポイント)に結構費用がかかります(月に5000円ほどはかかる)。 大規模アプリなら無視できるかもしれませんが、中小規模のアプリケーションでは5000円の差は重要です。
下記の記事が参考になりますが、
AWSによると、プライバシーやセキュアな環境である必要があればプライベートサブネットを、インターネットアクセスを簡単にするためにパブリックサブネットを使用するといいんじゃないか
というふうな感じらしいです。
パブリックサブネットにした場合、下記のような問題が出てきますが、
セキュリティグループを適切に設定していれば、ほとんどのセキュリティリスクは緩和されるはずです。
ただ、人為的ミスが命取りになるので、やはりパブリック層とプライベート層とレイヤー分ける意味でも、ECSをプライベートサブネットにしておくのが、良さそうです。
- 直接の外部アクセス
- DoS/DDoS攻撃のリスク
- データのエクスフィルトレーション
- 設定ミスの影響
(指摘があれば欲しいです。)
参考: AWS ECS の配置は、プライベートサブネットとパブリックサブネットのどっちがいいの?
NATゲートウェイ か VPCエンドポイントか
ECS実行環境をプライベートサブネットに配置した場合、ECSコントロールプレーンやECR,Cloud watch logsなどの通信するために、NATゲートウェイ か VPCエンドポイントを設置する必要があります。
今回は、NATゲートウェイを選定しました。
VPCエンドポイントにした場合、約5個のリソースが必要になり、さらにマルチAZだとその倍のリソースが必要になります。
管理のしやすさ、コストの観点から、今回はNATゲートウェイにしました。ECS用のSGのアウトバンドを適切に設定すると、セキュリティがさらに担保されます。
Railsアプリケーションについて
アプリケーションの詳細については本題から逸れるため割愛します。
Dockerfileとentrypoint.sh(コンテナ起動時コマンド)については紹介しておきます。
Dockerfile
重要なのは、ENTRYPOINT ["entrypoint.sh"]を使って、
ECS内でDokcerを起動した時に、precmpileやマイグレーション等のコマンドを実行できるようにすることです。
FROM node:14.17.6 as node
FROM ruby:3.0.2
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
&& ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
&& ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
RUN apt-get update -qq && \
apt-get install -y build-essential \
libpq-dev \
postgresql-client \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY package.json yarn.lock ./
RUN yarn install
COPY . /myapp
# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
entrypoint.sh
こちらがentrypoint.shです。
開発(ローカル)環境では、起動中のサーバーがあれば削除するだけです。
本番環境では、アセットのプリコンパイルと、db:create, migrateを実行するようにしています。
db:createは初回デプロイ時のみでOKなので、2回目以降はコメントアウトしてください。
seedなども必要であれば、ここに記述します。
#!/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# production環境の場合のみ
if [ "$RAILS_ENV" = "production" ]; then
bundle exec rails assets:precompile
# --------------------------------------
# 本番環境(AWS ECS)への初回デプロイ時に利用
# 初回デプロイ後にコメントアウトして下さい
bundle exec rails db:create
# --------------------------------------
# マイグレーション処理
bundle exec rails db:migrate
fi
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
CSSやJSのレンダリングについて、下記が参考になりました。
【参照】
database.yml
後ほど、postgresSQLを作成などに、この辺りの設定が必要になってきます。
なので、一応載せておきます。(参考にしないでいいと思います。)
default: &default
adapter: postgresql
encoding: unicode
host: db
username: postgres
password: password
pool: 5
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
production:
<<: *default
database: myapp_production
username: myapp
host: <%= ENV["DB_HOST"] %>
password: <%= ENV["DB_PASSWORD"] %>
AWSリソースの作成手順
では、これよりAWSリソースの作成手順を説明してきます。
今回は, ProjectAというのプロジェクトの体で、ProjectAを接頭辞としてつけています。
手順ざっくり
- ネットワーク(VPC等)を作成
- セキュリティーグループを作成
- RDS作成
- ドメイン取得
- ALB作成
- ECR作成
- ECS作成
ドメイン取得に時間がかかるので、最初にやってもいいかもしれません。。
1.ネットワーク(VPC等)を作成
下記の通りです! VPCなどを一括作成できる機能があるので、それを使ってしまいます。
VPC -> VPC作成より、下記の画面の通りです!
上記の設定で、下記のプレビューが表示されると思います。
最後に「VPCを作成」 のボタンで一気にリソースが作成されます。
2.セキュリティグループの作成
3つのセキュリティグループを作成します。いずれも、先ほど作成したVPCを関連づけます。
ソースに関して、Anywhereにしていますが、絞った方がより良いです。
ALB用
ALBはHTTPとHTTPSを受け入れますので、下記設定が必要になります。
ECS用
ECSはRailsデフォルトポートである3000番を許可しておきます。
RDS用
RDSはECSからの通信を許可する必要があります。
ポートは5432(postgresのデフォルト)にして、ソースはECS用のセキュリティグループにします。
3.RDSの作成
今回はマルチAZ構成にしていますが、別にシングルAZで無料枠のdb.t2.microなどでも大丈夫です。
今回はPostgreSQL14を使います。
先述したdb.t2.microを使う場合は、無料枠でOKです。
ユーザー名, DB名は、先ほどのdatabe.ymlに合わせています。
パスワード、ホスト名(RDSエンドポイント)は、タスク定義のところで、環境変数として利用するのでメモを控えてください。
VPCは先ほど作成したものを、DBサブネットグループは新規でOKです。
セキュリティグループは先ほど登録したものを利用してください。
料金がバカ高かったら、何かがおかしいので、必ず確認して、
OKだったらデータベース作成してください。
4.ドメイン取得
ここでは、ドメインを取得し、Route53と紐づけて、ACMで証明書を取得します。
ちなみに、今回取得したドメインは「atelier-sora3.com」です。
ドメイン取得
私は、お名前.comでドメインを取得しましたが、特になんでもOKと思います。
Route53でも取得できますがコストが高いです。管理は楽ですが。
【参照】
Route53でホストゾーンを作成
Route53で「ホストゾーンを作成」をクリック し、ドメイン名に、取得したドメイン名(私の場合はatelier-sora3.com)と入力します。
それ以外はそのままで、「ホストゾーンの作成」をします。
Route53のホストゾーン一覧から、作成したドメインをクリックします。
で、NSレコード(ネームサーバー)の値をメモして、次に進みます。
(画面を開いたままにするだけでOKです)
お名前.domのドメイン情報「ネームサーバー情報」を、Route53と同じにする。
ドメインを取得したサービス(お名前.com)に、先ほどメモしたネームサーバー情報を登録します。
ここまでで、Route53とお名前.comの紐付けができています。
ACMの設定
続いて、ACM(AWS Certificate Manager)の設定です。SSL/TLS証明書の取得ですね。
これによりHTTPS通信が可能になります。料金は無料です。
atelier-sora3.comを例にして、下記が設定例です。
atelier-sora3.com と *.atelier-sora3.com を登録します。
*.atelier-sora3.comはサブドメインです。
登録すると、下記のように取得中になります。30分〜半日も待てば取得できるはずです。
で、この証明書情報をRoute53に登録します。
上記の画面の「Route53でレコードを作成」をクリックすると、下記の画面になるので、そのままレコードを作成してください。
5.ELB(ALB)の作成
ALB作成に、ターゲットグループが必須なので、まずはターゲットグループ(deleteme)を作成します。
最終的には使わない、一時的なターゲットグループなので、適当に作成しちゃいます。
まず、EC2のサービス画面に来てください。
サイドバーにある、ロードバランシング>ターゲットグループ をクリック。
最終的には使わない、一時的なターゲットグループなので、適当でOKです。
私は下記のように設定しました。これで作成してください。
続いて、ロードバランサです。
ロードバランシング>ロードバランサー をクリック。
Application Load Balancer(ALB)を選択してください。
VPCを先ほど作成したものにして、
サブネットは、2つのパブリックサブネットを設定します。
セキュリティグループは、デフォルトに加えて、作成したALB用のものを設定してください。
リスナーとルーティングでは、HTTP(80)とHTTPS(443)を設定します。
デフォルトアクションは、先ほどのdeletemeを一時的に設定しておきます。
ポート番号80番のリスナールールを443にリダイレクトさせるように変更します。
HTTP:80を選択して、リスナーを編集から、下記のように設定してください。
下記の通りです。
6.ECRの作成
ECRは、Dokcerイメージを管理するレポジトリです。
レポジトリの作成とDockerイメージの登録をやっていきます。
ECRレポジトリ作成
名前だけ入力して、あとはそのままでレポジトリ作成します。大文字使えなかったので、小文字にしています。
プッシュコマンド表示より、ローカルからレポジトリに登録するコマンドが記載しています。
あとはこのコマンドをローカルの開発環境で実行するだけです!
ただ、AWS CLIを使う必要があります。
未設定の人は、下記を参考にしてください。
ここまで来たらあとはECSを作成するだけです✊
7.ECSの作成
クラスターを作成し、タスク定義をして、サービス作成(タスク起動設定)という流れで進めていきます。
クラスターの作成
下記の通りに入力して、作成してください。今回はFargateにします。
タスク定義の作成
タスク定義を作成から、下記の作成画面を開きます。
ここでは、コンテナの設定をしてきます。
コンテナの名前はなんでも大丈夫です。
イメージURLは、ECRから確認して、入力してください。
コンテナポートは、3000です。(Dockerfileによりますが)
プロダクトによりますが、環境変数は、おそらく下記のようなものが必要になってきます。
項目 | 値 | 説明 |
---|---|---|
DB_HOST | RDSのエンドポイント | database.ymlに合わせる |
DB_PASSWORD | RDS作成時に設定したもの | database.ymlに合わせる |
RAILS_ENV | production | 本番環境 |
RAILS_LOG_TO_STDOUT | 1 | アプリケーションのログ出力 |
RAILS_MASTER_KEY | master.keyに記述してる値 | 秘匿情報 |
RAILS_SERVE_STATIC_FILES | 1 | 静的ファイルの扱い |
ECSサービスの作成
デプロイ定義のところで、サービス名とタスク定義を設定します。
タスク定義のリビジョンは、最新のものを使用するようにしてください。
(私は何度か作成したので5(最新)になっていますが、スムーズに進められた方は1(最新)になっています。)
ネットワーキングの設定は、最初に作成したVPCやサブネット、ECS用のセキュリティグループを利用してください。
サブネットは、2つのプライベートサブネットのみを登録します。
タスクは直接外部とインターネットを接続しないので、パブリックIPアドレスはオフにしておいてください。
続いて、ロードバランシングの設定です。
先ほど作成したロードバランサーを設定してください。
ターゲットグループは、サービス内のタスクに向けて、新規作成します。
上記の設定で、サービス作成をします。
すると、クラスターに、作成したサービスが表示されます。
サービス詳細画面のタスクのところで、実行中のタスクを確認できます。
最後に、ALBのターゲットグループを作成したターゲットグループ(ProjectA-target)に変更すると、完了です!
EC2>ロードバランシング>ロードバランサ でロードバランサのターゲットグループを変更します。
(最初に作成したターゲットグループdeletemeは編集できないので、削除するために、一度リスナールールを削除する必要があります。)
ProjectA-albの画面より、HTTP:443を選択します。
再度、リスナーを追加します。
ここで先ほど作成したターゲットグループを選択してください。
HTTP:80リスナーは、HTTPSへリダイレクトさせます。
これで設定が完了です!!!🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️
デプロイされたアプリケーションにアクセスしてみましょう。
取得したドメイン(atelier-sora3.com)でアクセスをすると、アプリケーションが見れるはずです!
デプロイ後、エラーになった方へ
私の説明不足かもしれません、申し訳ございません。
ECSのサービス画面からログを確認できますので、ご確認ください。
デバッグできるかと、、、😭
アプリケーションの更新(再デプロイについて)
検索すると色々な方法が出てきますが、一番簡単なのは、サービス内で起動中のタスクを削除することです。(アプリが一時的に停止するので、本番運用では控えた方がいいです。)
- アプリケーションのコードを修正
- ECRに再度プッシュ
- ECSサービス内で起動中のタスクを削除
- ECRに再度プッシュされたDockerイメージ(latestタグ付き)が、ECSサービス内に自動で起動されます。
### リソース削除について
作成した手順の逆を実施すれば、全て綺麗に削除できますので、忘れずに。🍞
自動デプロイの実装について
下記の記事にまとめました。こちらもご覧ください。
以上です。
最後までありがとうございました。
試したけど、無理だった・ここが間違っているなど、ご意見があればください。
なお、ここで作成したリソースは全て削除しています。
下記、関連記事も書いているので、よかったら見てください。