Shu Web Creation

Articles

投稿記事

Top > Articles > プログラミング > RailsのXクローンをAWSに手動デプロイしてみた

RailsのXクローンをAWSに手動デプロイしてみた

プログラミング AWS Fargate Rails インフラ デプロイ
公開日 更新日
RailsのXクローンをAWSに手動デプロイしてみた

前に作ったRailsのXクローンを、
AWSに手動デプロイすることが出来たので、記事にまとめました。

目次

仕様

・ルートユーザーで作業せず、専用ユーザーを作成してポリシーを付与する。
・ECSとFargateでデプロイする
・DBはRDSを使う
・ロードバランサーを使用する。
・Route53でドメインを購入して、それを使う。
・SESを使ってメールを送る。
・S3で画像を扱う。
・アーキテクチャ図は以下のようになる。

Fargateを最初はプライベートサブネットにおいていたのですが、
その場合はNATゲートウェイを使う必要があり、
実際使ってみて高かったので、Fargateをパブリックサブネットにおいて、
セキュリティグループで、アクセスを制御する設計にしました。

バージョン情報

以下が開発環境のバージョンになります。

  • Ruby 3.2.1
  • Rails 7.0.8.4
  • Postgress 14.17
  • Docker 27.5.1

命名規則

プロジェクト名-サービス名-詳細
のように命名していこうと思います
例えば、「xclone-subnet-private-1a」のように揃えていきます

作業の流れ

・実行用ユーザーを作成
・ロールを作成
・ドメインの取得
・ネットワークの設定
・ロードバランサーを作る
・アプリサーバーをデプロイ
・DBをデプロイ
・Rails側の調整
・ドメインの有効化
・SESでメールを有効化させる
・画像を有効化させる
・残りの作業をする

以上のような流れで作業を行っていきます。

実行用のユーザーを作る

ルートユーザーで実際に、アプリを構築することは、非推奨なので、
IAMで、実行用のユーザーを作って、ポリシーを付与します。
AdministratorAccess」を付与して、以降このユーザーでログインして作業します。

ロールの作成

ロールは、ポリシーをサービスに付与して使います。

タスク実行ロールを作る

タスク実行ロールは、ECSがタスクを実行するためのロールです。

信頼されたエンティティタイプで、「AWSサービス」を選択して、
ユースケースで、「Elastic Container Service」を選んで、
更に、「Elastic Container Service Task」を選択します。

次に、検索窓から「AmazonECSTaskExecutionRolePolicy」を出して、チェックを入れて、
次へを押します。
ロールに「xCloneEcsTaskExecRole」と名前をつけます。

タスクロールを作る

タスクロールはタスクが他のAWSサービスを操作するためのロールです。
タスクからSESやS3を実行するのはここで設定します。
これがちゃんと設定していると、Railsのクレデンシャルを使わず、
タスクロールの権限で、SESやS3を操作できるようになります。

ここもタスク実行ロール同様に、
AWSのサービス」、「Elastic Container Service」、
Elastic Container Service Task」を選択し、
sesで検索して、「AmazonSESFullAccess」にチェックを入れ、
s3で検索して、「AmazonS3FullAccess」にチェックを入れて、
この2つの権限を付与し、
名前を、「xCloneEcsTaskRole」とします。

Railsのクレデンシャルを使わず、ロールで制御します。

今回に必要なロールは以上の2つです。

ドメインの取得

Route53でドメインを買います。
ドメインは、後でロードバランサーと紐づけて、
HTTPSでロードバランサーにアクセスするために必要です。
自分は、最初よくわからず、.comを買ったのですが、
練習用なので.link等の安いもので大丈夫です。

ACM(AWS Certificate Manager)を使って、無料のSSL証明書を発行し、
https(SSL)に対応できるようにしておきましょう。
ドメイン認証が必要ですが、Route53と連携していれば数分で完了します。

ネットワークの設定

ネットワークを作ります。
VPCを1つ、
パブリックサブネットを2つ、
プライベートサブネットを2つ、
セキュリティグループを3つ、
インターネットゲートウェイを1つ作ります。
その後、ルートテーブルを書き換えます。

VPCの作成

「VPCのみ」を選択した状態で、
名前を「xclone-vpc」とします。
IPv4 CIDRは「10.0.0.0/16」とします

サブネットの作成

これから作るサブネットはすべて、「clone-vpc」に紐づけるように作ります。

パブリックサブネット1

名前: xclone-subnet-public-1a
アベイラビリティーゾーン:ap-northeast-1a
IPv4 CIDR : 10.0.10.0/24

パブリックサブネット2

名前: xclone-subnet-public-1c
アベイラビリティーゾーン:ap-northeast-1c
IPv4 CIDR : 10.0.20.0/24

プライベートサブネット1

名前: xclone-subnet-private-1a
アベイラビリティーゾーン:ap-northeast-1a
IPv4 CIDR : 10.0.30.0/24

プライベートサブネット2

名前: xclone-subnet-private-1c
アベイラビリティーゾーン:ap-northeast-1c
IPv4 CIDR : 10.0.40.0/24

セキュリティグループを作成する

セキュリティグループは、
ロードバランサー用、
Fargateのアプリケーションサーバー用、
DB用の3つを作ります。
xclone-vpcにすべて、紐づけるように作ります

ロードバランサー用SG

名前: xclone-sg-alb
インバウンドルール: HTTPSで、すべてのIPv4を許可する
開発中に、HTTPでも通信を受け入れられるようにしておきます。

アプリサーバー用SG

名前: xclone-sg-app
インバウンドルール: HTTPで、先程作ったロードバランサーのsgからの通信許可をする

DBサーバー用SG

名前: xclone-sg-db
インバウンドルール: PostgreSQLで、一つ前のアプリサーバーのsgからの通信を許可する

インターネットゲートウェイを作る

名前:xclone-ig
作ったあとに、アクションからVPCにアタッチします。

ルートテーブルを設定する

ルートテーブルはVPCを作成したときに、
自動で作成されます。
これがデフォルトルートテーブルになります。
しかし、2つのパブリックサブネットに、
インターネットゲートウェイを追記したいので、
改めてもう一つ作ります。

VPCをxclone-vpcにした状態で作ります。
ルートを追加して、
0.0.0.0/0に対して、先程のインターネットゲートウェイを選びます。

その次に、サブネットを関連づけます。
先程作った2つのパブリックサブネットを関連付けます。
これで、パブリックサブネットから、Fargateが外に出れるようになります。

残りの2つのプライベートサブネットは、ルートテーブルに明記してないので、
デフォルトルートに従います。

ロードバランサーを作る

ロードバランサーとは、
2つアプリサーバーがあるので、自動で振り分けて、
負荷を分散します。

ロードバランサーはEC2から入って、
左のサイドバーにあります。

ターゲットグループの作成

名前は「xclone-target」とします。
ターゲットタイプは「IPアドレス」を選択します。
プロトコル:ポートでは、HTTPの80を選びます。
これはすでにロードバランサーをHTTPSで入った後なので、
HTTPの80です。
IPアドレスタイプはv4のままで、
プロトコルバージョンも初期値の、HTTP1のままでいいです。

ヘルスチェックは
HTTPで
/のままでOKです

/だとアプリのルートでヘルスチェックをします。
自分のアプリに応じて変えてください。

次の、「ターゲットを登録」画面では、
デフォルトのままでOKです。
ターゲットグループを作成します。

ロードバランサー自体を作る

名前は「xclone-alb」とします。
ロードバランサーのタイプは、
「アプリケーションロードバランサー」を選択します。

「インターネット向け」「IPv4」はそのままにします。
作ったVPCを選択して、
アベイラビリティーゾーンから1aと1cで、
それぞれ作ったパブリックサブネットを選びます。

セキュリティグループは、
ロードバランサー用に作った、
「xclone-sg-alb」を選択します。

リスナーとルーティングでは
HTTPSを選び、先程作ったターゲットグループを選びます。

デフォルト SSL/TLS サーバー証明書では、
「ACMから」で、
自分の買ったドメインのものを選択します。

他の設定はそのままで、決定します。

アプリケーションをデプロイする

土台ができたので、次にアプリケーションをデプロイします。
ここからは、ECSやECRを使って、作業していきます。
手元にあるRailsのプロジェクトは、
Dockerのイメージをビルドしてから、
ECRにアップロードして、
ECSのタスクから読み込ませます。

dockerfileの書き換え

CMD ["rails", "server", "-b", "0.0.0.0", "-p", "80"]

HTTPの標準ポートが80番なので、
AWS内部の通信を標準化するように、
Railsに80番で起動するように命令します。

ECRのリポジトリを作る

名前は「xclone-ecr」とします。
イミュータブルにすると、同じバージョンで上書きできなくなります。
今回はミュータブルでlatestで上書きできるように作ります。
暗号化の設定はそのままで、リポジトリを作ります。

プッシュコマンドに従ってイメージを扱う

リポジトリに入って、右上にプッシュコマンドがあるので、
それに従ってイメージをビルドします。

ステップ1で認証します。

ステップ2で、ビルドをします。
自分はM1なので、

docker build -t xclone-ecr --platform linux/amd64  .

このように–platfromオプションを付けます。
これでlinuxとintel系チップ用にビルドできます。

ステップ3でタグを付けます。

ステップ4で、リポジトリにアップロードします。

ECSでタスクを作る

名前は「xclone-task」とします
AWS Fargateを選択します。
Linux/X86_64を選択
ネットワークモードは選択できないのでそのまま。
小さめのCPUとメモリを選びます。
0.25vCPUと0.5GBのメモリを選択しました。

タスクロールに自分で作った「xCloneEcsTaskRole」
タスク実行ロールに自分で作った「xCloneEcsTaskExecRole」を選びます。

コンテナ1の設定

このコンテナ1で、アプリサーバーの設定をします。
名前は「xclone」として、
ECRに登録したイメージのURIを貼ります。
vCPUとメモリを、さっきのタスクで設定したものがあります。
タスクに対してコンテナは1つなので、そのまま割り当てます。

CloudWatchのログは有効化させます。

環境変数は後で必要に応じて設定します。

クラスターを作成する

名前は「xclone-cluster」とします。
Fargateを選択します。

サービスを作成する

クラスターの中に入ってサービスを作ります。
デプロイ設定から、先程作ったタスクを選びます。
最新のリビジョンで、
名前は、「xclone-service」とします。
必要なタスクは、とりあえず1つです。

ネットワーキング

作ったVPCを選んで、
その中にあるパブリックサブネットの2つを選びます。
既存のセキュリティグループから、
アプリケーション用の、
「xclone-sg-app」を選択します。
パブリックIPはオンのままにします。

ロードバランシング

ロードバランシングを有効にする
アプリケーションロードバランサーで、
既存から、先程作ったものを選びます。
リスナーも、既存のHTTPSを選びます。
ターゲットグループも既存のもので、
自作したものを選びます。

開発中ように、リスナーにHTTPの許可も追記するいいと思います。

その他の設定はそのままで、
これでサービスを作成します。

コンテナ内のエラーログの読み方

サービスを作って、タスクを実行すると
早速コンテナが立っては、
コンテナが閉じて、
タスクが終了して、
新しく立ち上がろうとしていますが、
なにかうまく行っていないようです。

なので、コンテナの中で何がうまくいってないのか、
調べてみようと思います。

クラスターからサービスに入って、
サービスパネルのタスクタブに飛ぶと、
動いていたタスクを確認できるので、
青い字のところを、クリックして、その中に入ります。

ログタブをクリックすると、
コンテナの中のログが読めます。
ログによると、Railsのsecret_key_baseがないので、
エラーが出ているようです。
これは、タスクを設定するときに、
環境変数として入れればOKです。

secret_key_baseを取得する

ローカルのコンテナの中で

rails secret

とコマンドをすることで、
値が返ってきます。

タスクで新しいリビジョンを作って、
環境変数に「SECRET_KEY_BASE」として、渡して保存します。

サービス飛んで、タスクのリビジョンを最新のものにして、
「新しいデプロイを強制」にチェックを入れて、サービスを更新します。

デプロイに時間がかかるので、しばらく待ちます。


また、コンテナが落ちました。
セキュリティが複雑なので、
直接、ブラウザで確かめられないけど、
DBが見つからないので落ちたとのことなので、
DBをデプロイしてみます。

DBをデプロイ

RDSのPostgreSQLを使います。

サブネットグループを作る

サブネットグループはRDSの左のサイドバーにあります。
ここで作ったサブネットグループの中に、
DBを置くので、
あらかじめ作ったVPCから、
AZを選択して、
プライベートサブネットの2つを、
グループに入れます。

DB自体を作る

標準作成で、
PostgreSQLを選びます。
バージョンは良さげなものを選んで、
テンプレートは本番稼働を選びます。
マルチAZの2インスタンスを選びます。
名前は、「xclone-db」とします。
マスターユーザー名は、
RailsからDBにアクセスするときのユーザー名になります。
パスワードをセルフマネージドにして、
自分で決めます。

インスタンスの設定では、
バースト可能クラスで、t3.microを選ぶと安いです。
ストレージはgp2の20GBにすると安いです。
下の方で見積もりが見れます。

VPCを自分で作った物を選びます。
先程作ったサブネットグループを選びます。
パブリックアクセスはなしで、
DB用のセキュリティグループを選びます。

下の方の追加設定で、
DBの名前を決めておきます。
「xclone_production」にします

マスターユーザー
マスターパスワード
DBの名前は、覚えておく必要があります。
これをタスクのパネルの環境変数で、
Railsに渡すからです。

RailsとDBを連携できるようにする

Railsのconfig/database.yml配下のように書きます

default: &default
  adapter: postgresql
  encoding: unicode
  host: <%= ENV["DB_HOST"] %>
  username: <%= ENV["DB_USER"] %>
  password: <%= ENV["DB_PASSWORD"] %>
  database: <%= ENV["DB_NAME"] %>
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test
#
production:
  <<: *default

イメージを更新します。

これでタスクの環境変数パネルから
DBのホスト、ユーザー、パスワード、DB名を渡せるようになります。
先ほどDBを作ったときのユーザー、パスワード、DB名を入れます。
ホストはDBのパネルに書いてあります。
4つの環境変数をいれたら。タスクを保存して、
サービスから新しいリビジョンのタスクをデプロイさせます。

RAILS_ENV=production

これを与えることで、
本番環境用の設定を使ってくれます。

これで、RailsとDBが繋がりました。
ロードバランサーの出したURLで、HTTP通信を
セキュリティグループと、ロードバランサーのリスナーで許可すれば、
とりあえず繋がります。
開発時だけ80を許可して、
本番運用ではHTTPSだけを許可するのがいいと思います。

Rails側の調整

アプリサーバーとDBが接続できたので、
細かい調整をします。

Railsにマイグレーションをさせる

初期状態では、Railsは自動でマイグレーションをしないので、
マイグレーションをするために、設定を追加します。

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

dockerfileに上のコードを追加します。
これをやることによって、
コンテナが起動したときに、「entrypoint.sh」が実行されます。
entrypoint.shには以下のように記述します

#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

bundle exec rails db:migrate

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

これでイメージを作り直して、タスクを起動し直すと、
マイグレーションがあるときだけ、実行するようになります。
これでトップの画面が表示されます。

アセットを有効化する

まだCSSなどのアセットが有効になっていません。
先程の「entrypoint.sh」の
bundle exec rails db:migrateの上辺りに、
以下を追記します。
これで本番用にアセットをビルドしてくれるようになります。

RAILS_ENV=production bundle exec rake assets:precompile

また、config/environments/production.rbの中で、

config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?

となっているので、
タスクのパネルから「RAILS_SERVE_STATIC_FILES」にtrueを与えて、
タスクを更新して、サービスを更新すると、
CSSなどが反映されるようになります。

無事表示されました。

ドメインを有効化

Route53に飛んで、自分の買ったドメインを選んで、
レコードを作成します。
レコード名は空のままにして、
レコードのタイプで「A」を選び、
エイリアスを有効にする。
アプリケーションロードバランサーのエイリアスを選んで、
東京を選ぶと、
自作したロードバランサーが出るので、
それを選んで、レコードを作成します。

ちょっと待ったら、HTTPSで、ドメインからつながるようになります。
ロードバランサーの80の許可を、
セキュリティグループとリスナーから削除してもいいかもしれません。

SESでメールを送れるようにする

最初はサンドボックスに状態なので、
本番で使えるようにする必要があります。

Rails側の設定

Aws::Rails.add_action_mailer_delivery_method(
    :ses,
    region: 'ap-northeast-1'
  )

  config.action_mailer.default_url_options = { host: 'aws-training-shu.com' }
  config.action_mailer.delivery_method = :ses
  config.action_mailer.perform_deliveries = true
  config.action_mailer.perform_caching = false
  config.action_mailer.raise_delivery_errors = true

  config.active_storage.service = :amazon

ホストは自分のドメインを入れてください。
クレデンシャルを入れないのは、
ECSのタスクにロールを付与して、
そちらからコントロールできるからです。

ドメインの認証

左のサイドバーで自分のIDに入って、
自分のドメインを選んで、
認証するところにRoute53に登録するボタンがあるので、
それを押すと、3行ほど、Route53のレコードに追記されるので、
これでOKです。

メールの認証

認証するメールは、
Railsで、Formに設定したユーザーです。
メールボックスに、メールが来るので、
リンクをクリックすれば、認証されます。

サポートケースを申請する

本番環境で使えるように申請します。

S3に画像を保管できるようにする

ユーザーが投稿した画像が、
S3のバケットに表示されるように、
設定していきます。

バケットを作る

自分の場合はなぜか、
買ったドメインのバケットが最初からあったので、
それを使います。
パブリックアクセスは、全てブロックします。
Railsからのみ、画像を扱えるようにします。
権限はECSのタスクロールで付与しました。

Rails側の設定

amazon:
  service: S3
  region: ap-northeast-1
  bucket: <%= ENV['AWS_BUCKET'] %>

/config/storage.ymlで、このように記述して、
タスクの環境変数からバケット名を与えます。
クレデンシャルを入れないのは、
ECSのタスクのロールを使ってコントロールするためです。

残りの作業

サービスを更新してタスクが2つ立ち上がるようにすれば、
アーキテクチャ図のようになります。

まとめ

これで、自分で作ったXクローンを、
AWSに手動デプロイ出来ました。

エラーにつまずいて、その都度修正していけば、
うまくデプロイして、文章化することが出来ました。
やらないといけないものがとても多いと感じました。

次は自動デプロイを学んで、自動化していきたいと思います。

関連記事

Githubに草を生やしそこねたときの救済措置

Githubに草を生やしそこねたときの救済措置

プログラミング

失敗しても大丈夫!救済措置があります。草を生やしまくって、モチベアップしよう!

2025年1月の学習の振り返り

2025年1月の学習の振り返り

プログラミング

Happiness ChainのコースをAceに変更。去年の振り返りと1月の振り返りをしました。

2025年2月の学習の振り返り

2025年2月の学習の振り返り

プログラミング

2月は主にAWSやネットワークを学習しました。また自分のサイトがほぼ完成しました。