Rails 8で本番利用可能になったSQLiteをつかうとGoogle Cloudでほぼ無料で運用ができそうな件
はじめに
Rails 8が新たにリリースされ、SQLiteを本番環境でも使用できるようになりました。
これまで、Google CloudでRailsアプリを運用する際、Cloud RunとCloud SQLを使うと簡単に構築できて便利だったんですが、Cloud SQLだけで最低月2,000円弱かかってしまっていました。
SQLiteは、PostgreSQLやMySQLと違いライブラリとして動作するため、データファイルを保存できるストレージがあれば別途CloudSQLなどのサービスを使う必要がなくなります。
なので、Cloud StorageのバケットにSQLiteのデータを置いて、Cloud RunでGCSバケットをボリュームマウントとして設定することで、ほぼ無料のRails環境が作れそうと考えて試してみました。
インフラ構成
Cloud RunとCloud Storageの構成にしています。Secret Managerは、Railsのmaster keyのために使っていますが、直接設定する場合は不要になります。
Railsアプリ作成
- 確認用にmyAppという最小限のRailsアプリを作成します。
mkdir myApp
cd myApp
bundle init
- GemfileにRails 8を指定
# frozen_string_literal: true
source "https://rubygems.org"
gem "rails", "8.0.1"
- Rails new
bundle exec rails new .
- 投稿機能を作成します
bundle exec rails g scaffold post content:text
...
root "posts#index"
...
データベースの設定は、下記のようにstorageディレクトリ以下にsqliteのデータを保存するようになります。
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default
database: storage/development.sqlite3
test:
<<: *default
database: storage/test.sqlite3
production:
primary:
<<: *default
database: storage/production.sqlite3
cache:
<<: *default
database: storage/production_cache.sqlite3
migrations_paths: db/cache_migrate
queue:
<<: *default
database: storage/production_queue.sqlite3
migrations_paths: db/queue_migrate
cable:
<<: *default
database: storage/production_cable.sqlite3
migrations_paths: db/cable_migrate
- DB作成&マイグレート
bundle exec rails db:create
bundle exec rails db:migrate
- ローカル環境で動作確認
bundle exec rails s
ブラウザでlocalhost:3000を開くとPost一覧が表示されます。
Google Cloudの準備
Cloud Storageバケット作成
SQLiteのデータを保管するストレージ用のバケットを作成します。
Secret ManagerにMasterKeyを保存
"rails_master_key"という名称で、config/master.keyの値を登録します。
デフォルトのサービスアカウントに権限を付与
Cloud RunからSecret Managerにアクセスするために、デフォルトのサービスアカウントにSecret Managerアクセサーロールを付与しておきます。
Artifact Repositoryの準備
- Artifact Repository APIを有効にする
リポジトリを作成
"リポジトリの作成"をクリックして、リポジトリを作成しておきます。
リポジトリ名とリージョンを設定
最小コストを確認したいので、脆弱性スキャンは無効にしてます。
イメージをデプロイ
gcloudコマンドとdockerコマンドを使って、Docker imageをArtifact Repositoryにプッシュします。
- Google Cloudにログイン
gcloud auth login
- ArtifactiRepositoryのホストにasia-northeast1を追加
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
- 定数設定
PROJECT_ID=myapp
REPOSITORY_NAME=myapp-repository
SERVICE_NAME=myapp
- イメージをビルド
docker build -t asia-northeast1-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/$SERVICE_NAME:1 .
- イメージをプッシュ
docker push asia-northeast1-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/$SERVICE_NAME:1
- イメージがプッシュされたことを確認する
デプロイ
Cloud Runにデプロイ
- コンテナをデプロイからサービスを選択します。
- 「既存のコンテナイメージから1つのリビジョンをデプロイする」を選択します。
コンテナイメージのURLには、先ほどArtifact Repositoryに登録したコンテナイメージを選択します。
サービス名、リージョンをそれぞれ設定します。
- 認証を「未認証の呼び出しを許可」にします。
- コンテナタブのコンテナポートを3000ポートにします。
-
ボリュームタブの新しいボリュームで、以下を設定します。
- ボリュームタイプ:Cloud Storageバケット
- ボリューム名:database
- バケット: [先ほど作成したバケット]
- コンテナタブに戻って、「ボリュームのマウント」を開き先ほど設定したボリュームを選択します。
マウントパスは、"/rails/storage"を設定します。
- 続けて、「変数とシークレット」タブのシークレットにRAILS_MASTER_KEYを登録します。
- リビジョンスケーリングのインスタンスの最小数を0、インスタンスの最大数を1にします。
- 「作成」をクリックして、デプロイします。
しばらく待って、緑のチェックがつくとデプロイ成功です。
エラーになってしまう場合は、「新しいリビジョンの編集とデプロイ」から確認修正し再度デプロイしてください。
デプロイが成功すると、サービス詳細に表示されるURLからRailsアプリを開くことができます。
これで、最小限の構成のRailsアプリをデプロイできました。
確認
アプリの動作確認
Cloud RunのデフォルトURLからRailsアプリを確認します。
簡単な確認しかしていませんが、問題なく動作しました。
Cloud Storageのバケットを確認
Cloud Storageのバケットを確認するとsqliteのファイルが作成されます。
バケットの設定を間違えて公開しないよう注意しましょう。
費用確認
1週間くらい放置して費用の確認をしました。1日あたり0.1円ぐらい掛かっているので、月3円ぐらい課金されそうです。
内訳は、すべてArtifact Repositoryでした。Artifact Repositoryのストレージの無料枠(0.5GB)を少し超えているために課金されていると思われます。
その他
コールドスタートについて
Cloud Runの最小インスタンスを0にしているので、しばらくアクセスがないとコールドスタートの状態になります。体感としては、20秒程度かかった気がします。
今回は、実施しませんが稼働時間チェック(Cloud Monitoringによるヘルスチェック)を使っていると、ほとんど無視できると思いますが、インスタンスの稼働時間が増えるので費用が発生する可能性があります。
スケールアウトの制限
SQLiteが単一ホストが前提というところがあるので、Cloud Runのメリットであるスケールアウト(水平スケール)については諦める必要があります。もし、スケールアウトして複数インスタンスで稼働したい場合は、今のところPostgresqlやMySQLを使う方が良さそうです。
まとめ
Rails8 + SQLiteをCloud RunとCloud Storageで、月3円ぐらいで運用できそうです。
デモ用の小規模なアプリやポートフォリオ用アプリを安く運用したいというニーズには合うかもと思いました。
Discussion
ありがとうございます。こちらの記事の通りに構築を進めることでRails環境を構築することができました。
3点ほど設定につまづく箇所がありましたので、共有いたします。
イメージをプッシュする際
Unauthenticated request. Unauthenticated requests do not have permission "artifactregistry.repositories.uploadArtifacts"
エラーが発生gcloud auth configure-docker asia-northeast1-docker.pkg.dev
を実行することで解決いたしましたdocker push時に
terminated: Application failed to start: "/rails/bin/docker-entrypoint": exec format error
エラーが発生Dockerイメージがビルドされたシステムと実行されるシステムのCPUアーキテクチャの不一致で発生するようです(こちらの環境はM1 Macでした)。以下のようにターゲットプラットフォームを指定することで解決できました。
FROM --platform=linux/x86_64 docker.io/library/ruby:$RUBY_VERSION-slim AS base
Cloud runのデプロイ時にArgumentError: No database file specified. Missing argument: database (ArgumentError)
が発生database.ymlでproduction環境のdatabaseの設定が不足していたため発生したようですdatabase: storage/production.sqlite3
のように設定を追加することで解決できました。上記、参考になりましたら幸いです。
docker imageをpushする設定などで不足している部分があったようです。コメントを参考に追記させていただきました。
コメントありがとうございます。
改めて読み返したところ、databaseの設定は見逃しておりました。すみません 🙏