Rails アプリケーションを Cloud Run にデプロイする
Cloud Run(Cloud Run on GKE でないほう)に Rails アプリケーションをデプロイします。
デプロイは下記の流れで進めます。
- Rails アプリの Docker イメージをローカルで作成して、Container Registry に Push
- Cloud SQL インスタンスとデータベースを作成
- ローカルで Cloud SQL Proxy を使って Schema Migration を実行
- Cloud Run にアプリケーションをデプロイ
今回作成したソースコードは下記においてあります。
scripts/
以下に gcloud
コマンドの実行サンプルもおいてあります。
Cloud Run
Cloud Run は GCP での Knative のマネージドサービスです。
Knative は Kubernetes 上で動作するソフトウェアで、Functions as a Service のような機能が使えます。
Cloud Run には Google が管理する Kubernertes 環境で動作する Cloud Run と自身の管理する GKE で動作する Cloud Run on GKE があります。
アプリケーションの構成
Active Record を使うシンプルな Rails アプリケーションを想定しています。
データベースには MySQL(Cloud SQL) を使います。 今回のデプロイ先は Cloud Run on GKE ではないため Cloud SQL へは VPC から接続できません。 Cloud SQL Proxy を使って Cloud SQL へ接続します。
Cloud Memorystore などの Cloud SQL 以外のサービスに接続するには現状 Cloud Run on GKE を使う必要がありそうなので、今回は扱いません。
Rails アプリケーションの作成
rails
コマンドで Rails アプリを作成します。
rails new rails-cloud-run-sample --skip-bundle cd rails-cloud-run-sample bundle install --path vendor/bundle
Cloud SQL の MySQL に接続するので mysql2
をインストールします。
bundle add mysql2
適当にモデルを作成します。
bundle exec rails g scaffold user name:string bundle exec rails db:migrate
config/environments/production.rb
を編集して、Production 環境でのアセットコンパイルを有効にしておきます。
- config.assets.compile = false + config.assets.compile = true
動作を一度確認するために、サーバーを起動して http://localhost:3000/users
にアクセスします。
bundle exec rails s
config/database.yml
を編集します。
default: &default adapter: sqlite3 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000 development: <<: *default database: db/development.sqlite3 test: <<: *default database: db/test.sqlite3 production: adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: <%= ENV['MYSQL_PASSWORD'] %> socket: <%= ENV['MYSQL_SOCKET'] %> database: sample username: root
次に、Dockerfile を作成します。
FROM ruby:2.6 ADD Gemfile Gemfile.lock / RUN bundle install WORKDIR /app ADD . . CMD ["bundle", "exec", "rails", "server"]
あらかじめ Container Registry API を有効にしておき、作成したイメージを Push します。
docker build -t rails-cloud-run-sample . docker tag rails-cloud-run-sample gcr.io/<PROJECT_ID>/rails-cloud-run-sample docker push gcr.io/<PROJECT_ID>/rails-cloud-run-sample
Cloud SQL インスタンスの作成
今回作成したアプリケーションが接続する MySQL サーバーを用意します。 Cloud Run が現時点で us-central1 でしか使えないので、それにあわせて us-central1 にインスタンスを作成します。
gcloud sql instances create cloud-run-sample \ --region us-central1 \ --assign-ip \ --tier db-f1-micro \ --root-password <PASSWORD>
Cloud Run では Cloud SQL Proxy 経由で接続するので、--assign-ip
オプションを付けておきます。
パスワードは適当なものを設定してください。
(コンソールから作成すると乱数文字列を設定できます)
作成に成功すると下記のように出力されます。
Creating Cloud SQL instance...done. Created [https://www.googleapis.com/sql/v1beta4/projects/<YOUR_PROJECT>/instances/cloud-run-sample]. NAME DATABASE_VERSION LOCATION TIER PRIMARY_ADDRESS PRIVATE_ADDRESS STATUS cloud-run-sample MYSQL_5_7 us-central1-a db-f1-micro xx.xx.xx.xx - RUNNABLE
次に、データベースを作成します。
gcloud sql databases create sample --instance cloud-run-sample
作成に成功すると下記のように出力されます。
Creating Cloud SQL database...done. Created database [sample]. instance: cloud-run-sample name: sample project: <YOUR_PROJECT>
Schema Migration の実行
作成した Cloud SQL のデータベースに Rails アプリで使うテーブルを作成します。
まず、Cloud SQL にローカルから接続するために、ドキュメントにしたがってCloud SQL Proxy を用意します。
curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64 chmod +x cloud_sql_proxy sudo mkdir /cloudsql sudo chmod 777 /cloudsql
Cloud SQL Proxy は Docker でも実行できますが、Docker for Mac ではマウントした Unix ソケットを使って接続ができなかったのでローカルで実行するようにしています。 (TCP ソケットは Docker を使っても問題なく接続できました)
試してはいませんが、Linux 環境であれば Docker でも問題ないと思います。
Cloud SQL Proxy を起動します。
-credential_file
や -instances
オプションなしで実行すると自動的に接続可能なインスタンスが選択され、gcloud
でログインしているユーザーで実行されます。
複数のインスタンスが存在していたり、サービスアカウントを指定する場合は適宜設定してください。
./cloud_sql_proxy -dir /cloudsql
Cloud SQL Proxy を起動したままの状態で、Schema Migration を実行します。
export RAILS_ENV=production export MYSQL_SOCKET=/cloudsql/<PROJECT_ID>:us-central1:cloud-run-sample export MYSQL_PASSWORD=<PASSWORD> bundle exec rails db:migrate
MYSQL_SOCKET
にはプロジェクト ID と 次に作成する Cloud SQL インスタンスの名前とリージョンを組み合わせて指定します。
成功すれば下記のように表示されます。
== 20190620142533 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.1815s == 20190620142533 CreateUsers: migrated (0.1816s) =============================
Cloud Run へのデプロイ
ドキュメントの記載に従ってデプロイします。
Rails アプリケーションで使う SECRET_KEY_BASE
を生成します。
export SECRET_KEY_BASE=$(bundle exec rails g secret)
下記のコマンドを実行して、作成したアプリケーションを Cloud Run にデプロイします。
gcloud beta run deploy rails-cloud-run-sample \ --image gcr.io/<PROJECT_ID>/rails-cloud-run-sample \ --add-cloudsql-instances cloud-run-sample \ --allow-unauthenticated \ --region us-central1 \ --memory 512Mi \ --set-env-vars " \ RAILS_ENV=production, \ RACK_ENV=production, \ MYSQL_SOCKET=/cloudsql/<PROJECT_ID>:us-central1:cloud-run-sample,\ MYSQL_PASSWORD=<PASSWORD>, \ SECRET_KEY_BASE=$SECRET_KEY_BASE \ "
このとき、--add-cloudsql-instances
オプションで接続する Cloud SQL インスタンスを指定しています。
デプロイに成功すると、下記のように表示さます。
Deploying container to Cloud Run service [rails-cloud-run-sample] in project [<PROJECT_ID>] region [us-central1] ✓ Deploying... Done. ✓ Creating Revision... ✓ Routing traffic... ✓ Setting IAM Policy... Done. Service [rails-cloud-run-sample] revision [rails-cloud-run-sample-00001] has been deployed and is serving traffic at https://rails-cloud-run-sample-xxxxxxxxx-uc.a.run.app
表示された URL にアクセスしてアプリケーションの動作を確認します。 初回アクセス時はコンテナを起動するのに 10 秒程度かかります。 2回目以降はすぐにレスポンスを返しますが、10 分から 15 分程度アクセスがなくなるとゼロスケールしてしまい、コンテナの起動を待つ必要があります。
アプリケーションがステータス 500 を返す場合は下記のログが残っていないかを確認してください。 エラーメッセージにしたがって、Cloud SQL Admin API を有効にしてみてください。
CloudSQL connection failed: ensure that the account has access to "<YOUR-PROJECT>:us-central1:cloud-run-sample" (and make sure there's no typo in that name). Error during createEphemeral for <YOUR-PROJECT>:us-central1:cloud-run-sample: googleapi: Error 403: Cloud SQL Admin API has not been used in project XXXXXXXX before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/sqladmin.googleapis.com/overview?project=XXXXXXXX then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
まとめ
Cloud Run を使うことで Docker コンテナのデプロイを PaaS 感覚で実行できました。 Cloud Run は Cloud Functions のようにリクエストがあったときの実行時間に対するリソース(メモリとCPU)に対して課金されるので、コスト最適化にも向いていると思います。
ただ現時点では制約が多く、ユースケースはよく検討する必要がありそうです。
1つ目は他のサービスとの連携です。 機能を拡大していくには VPC アクセスができる Cloud Run on GKE が選択肢に上がってきそうです。 Cloud Run on GKE では Cloud Run に加えて GKE のリソースに対する課金が発生するので注意が必要です。
2つ目はリージョンです。 現時点で US リージョンのみでの提供のためレイテンシは少し大きい点にも注意が必要です。
3つ目はオートスケールです。 Cloud Run の特徴でもあるオートスケールによって、アクセスがないときはアプリケーションはゼロスケールしてコンテナはゼロの状態になります。 この状態でアクセスするとコンテナの起動に少し時間がかかるため、ゼロスケールを防ぎたいのであれば Cloud Scheduler で定期的にアクセスさせるなどの工夫が必要です。
最初に Function as a Service のように使えると書きましたが、どちらかというと PaaS に近い気がします。 一般的な PaaS だとリクエストがない時間も課金対象ですが、そういった時間のリソースを効率的に管理してくれるという点で Function as a Service と PaaS のいいとこ取りをしているといってもいいかもしれません。