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にアクセスすると以下のようにアプリが表示されました。
いいですね!
最終的に以下のような copilot/myapp-service/manifest.ymlになりました。
healthcheck
と RAILS_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のほうもよろしくお願いします。