Header image

クラッソーネの開発者がエンジニアリングに関することもそうでないことも綴っています!

Rails7.1アプリをAWS Copilot CLIでAWS Fargateにデプロイする

Rails7.1アプリをAWS Copilot CLIでAWS Fargateにデプロイする

Rails7.1アプリを爆速でECSにデプロイするぞ!

こんにちは。プロダクト開発部の小木です。

今期のイチオシ特撮はウルトラマンブレーザーです。(個人的に冷めがちだった)近年のヒーロー特撮によくあるコレクションアイテム商法を廃した骨太な作品で毎週楽しみに観ています。

ところで、去る2023年10月5日にRails7.1がリリースされましたね。7.1からは rails new をするとDockerfileがデフォルトで生成されるようになりました。

そこで今回はこれを使ってRailsアプリをAWS Fargateにデプロイしていこうと思います。

検証目的なのでデータベースはデフォルトSQLiteで。コンテナもrailsコンテナひとつのみ起動する単純な構成でいきます。

Railsアプリについて

Railsアプリは docked を使ってセットアップしました。

dockedを使うと手元の開発環境にRubyやRailsをインストールせずにRailsアプリを立ち上げることができます。

docked rails new myapp
docked rails server

アプリケーションが起動できたのを確認後、以下の変更をしました。

docked rails g scaffold post title:string contents:text author:string
docked rails db:migrate
Rails.application.routes.draw do
  resources :posts
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Defines the root path route ("/")
  root "posts#index" # 追記
end

ALBからのヘルスチェックをhttpで受けるため force_ssl をfalseにしています(理由は後述)。検証目的なので雑にfalseに変更していますが、業務等で実装する場合は ALBからのリクエストのみfalseにする、等の工夫が必要そうです。

config.force_ssl = false # falseに変更

ヘルスチェック用のURL(http://0.0.0.0:3000/up )にアクセスして一面緑のページが確認できました。Railsアプリ側の準備はこれでOKです。

Copilot CLIについて

copilotというと GitHub Copilotが有名ですが、AWS Copilot CLIというツールがAWSから公開されています。AWS Copilot CLIは、アプリケーションのデプロイを簡素化するためのツールです。このCLIを使用すると、Railsアプリを簡単にAmazon ECSにデプロイできます。

AWS Copilot CLIを使用すると、以下のようなAWSのリソースを作成できます。

  • Amazon ECSクラスター
  • タスク定義
  • コンテナイメージのビルドとデプロイ
  • ロードバランサー
  • ロググループ
  • アプリケーションのスケーリング設定

(インストール方法や初期設定は省略します。公式ドキュメントで丁寧に説明されているのでそちらを見てください。ローカライズもされています。)

copilot cliを使ったデプロイ

以下のコマンドを実行後にいくつかの質問に回答します。

copilot init

Use existing application: No
Application name: myapp

  Which workload type best represents your architecture?  [Use arrows to move, type to filter, ? for more help]
    Request-Driven Web Service  (App Runner)
  > Load Balanced Web Service   (Public. ALB by default. Internet to ECS on Fargate)
    Backend Service             (Private. ALB optional. ECS on Fargate)
    Worker Service              (Events to SQS to ECS on Fargate)
    Static Site                 (Internet to CDN to S3 bucket)
    Scheduled Job               (Scheduled event to State Machine to Fargate)

Service name: myapp

  Which Dockerfile would you like to use for myapp?  [Use arrows to move, type to filter, ? for more help]
  > ./Dockerfile
    Enter custom path for your Dockerfile
    Use an existing image instead

このまま回答内容に問題なければそのままAWS側にアプリケーションがデプロイされます。

が、最初はヘルスチェックに失敗してコンテナが起動・停止を繰り返してデプロイに失敗してしばらくハマりました。

原因は前述した force_ssl 設定で、ALBからのヘルスチェックはhttpで送信されるため、Railsアプリ側がhttpsではないそのリクエストを弾いてしまっていたことが原因でした。

大量にログが表示されますが、デプロイに成功すると以下のような出力がされます。ここに記載されているURLにアクセスすると

✔ Deployed service myapp-service.
Recommended follow-up action:
  - You can access your service at http://myapp--Publi-bAUWZB7S2oKu-1675675588.ap-northeast-1.elb.amazonaws.com over the internet.

ログの最後に表示されるURLにアクセスすると以下のようにアプリが表示されました。
いいですね!

Myapp.png

最終的に以下のような copilot/myapp-service/manifest.ymlになりました。

healthcheckRAILS_MASTER_KEY あたりを追記しています。RAILS_MASTER_KEYについても本来は以下のようにベタ書きすべきではないですが、検証目的ということで。manifest.yml内でのシークレットの取り扱いについても公式ドキュメントでは解説されていますので、それに従うと良いでしょう。

name: myapp-service
type: Load Balanced Web Service

http:
  path: '/'
  healthcheck:
    path: '/up'
    port: 3000

image:
  build: Dockerfile
  port: 3000

cpu: 256
memory: 512
count: 1
exec: true
network:
  connect: true

variables:
  RAILS_MASTER_KEY: # config/master.keyに記載されているマスターキーを記述

routes.rbにヘルスチェック用のパスが用意されているため、healthcheckの項でそのパスを指定しています。ここに対してALBからヘルスチェックのリクエストが飛んできます。

  get "up" => "rails/health#show", as: :rails_health_check
I, [2023-11-04T13:50:35.619248 #1]  INFO -- : [1c63372e-4e4d-48c4-a386-afacaf5b4672] Started GET "/up" for 127.0.0.1 at 2023-11-04 13:50:35 +0000
I, [2023-11-04T13:50:35.620205 #1]  INFO -- : [1c63372e-4e4d-48c4-a386-afacaf5b4672] Processing by Rails::HealthController#show as HTML
I, [2023-11-04T13:50:35.620705 #1]  INFO -- : [1c63372e-4e4d-48c4-a386-afacaf5b4672] Completed 200 OK in 0ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 114)

まとめ

ということでRails7.1アプリを copilotを使うことで簡単にFargateにデプロイすることができました。

今回DBはSQLiteになっていますが本運用時にはMySQLやPostgreSQLを利用することになると思います。そういう場合はRDSを同じVPC内に別途用意しておいて接続情報を環境変数で渡すとかやり方を考える必要がありますね。

「VPCやセキュリティグループの設定をイチからやるのは今の時代ちょっとな、、だけどRailsアプリをクラウドに乗せたいな」という場合に有力な選択肢になるのではないでしょうか。
また、Railsでデータベースを使わないというのはあまりないかもしれませんが、データを加工して右から左に受け流すようなマイクロサービスとか、APIモードでVPCの閉域網の中に生息するマイクロサービスなどアイデア次第で便利に使えるのではないかと思います。

是非是非こちらのcopilotのほうもよろしくお願いします。


静岡県浜松市のアプリケーションデベロッパー。ITコミュニティやシビックテックにも興味があります。