Cloud Run 環境での Rails の実行

サンプル Rails アプリケーションを Cloud Run にデプロイする方法、およびマネージド データベース、オブジェクト ストレージ、暗号化されたシークレット、ビルド パイプラインをサーバーレス コンピューティングと統合する方法を学びます。

Rails アプリケーションをデプロイするには、複数のサービスを組み合わせて統合プロジェクトを作成します。このチュートリアルは、Rails ウェブ開発の知識があることを前提としています。

このチュートリアルでは、Ruby 3.0 以降(Ruby 2.7 もサポートされています。「コードを理解する」セクションをご覧ください)と Rails 6 以降が必要です。

デプロイのアーキテクチャを示す図。
Rails サイトは Cloud Run で提供されており、複数のバッキング サービスを使用して、さまざまなデータ型(リレーショナル データベース情報、メディア アセット、構成シークレット、コンテナ イメージなど)を保存します。 バックエンド サービスは、ビルドタスクと移行タスクの一環として Cloud Build により更新されます。

目標

  • Cloud SQL データベースを作成して Active Record に接続する
  • Secret Manager を作成して使用し、Rails マスター鍵に安全に保存してアクセスする
  • Active Storage から Cloud Storage にユーザーがアップロードしたメディアとファイルをホストする
  • Cloud Build を使用してビルドとデータベースの移行を自動化する
  • Rails アプリを Cloud Run にデプロイする

費用

始める前に

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Google Cloud プロジェクトで課金が有効になっていることを確認します

  4. Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine API を有効にします。

    API を有効にする

  5. Google Cloud CLI をインストールします。
  6. gcloud CLI を初期化するには:

    gcloud init
  7. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  8. Google Cloud プロジェクトで課金が有効になっていることを確認します

  9. Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine API を有効にします。

    API を有効にする

  10. Google Cloud CLI をインストールします。
  11. gcloud CLI を初期化するには:

    gcloud init
  12. このチュートリアルで使用するアカウントに十分な権限が付与されていることを確認してください。

環境の準備

デフォルト プロジェクトの設定

gcloud CLI のデフォルトのプロジェクト構成は、次のコマンドを実行して設定します。

gcloud config set project PROJECT_ID

PROJECT_ID を新しく作成した Google Cloud プロジェクト ID に置き換えます。

Rails アプリのクローンの作成

Rails サンプルアプリのコードは、GitHub の GoogleCloudPlatform/ruby-docs-samples リポジトリにあります。

  1. リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/ruby-docs-samples.git
    
  2. サンプルコードが含まれているディレクトリに移動し、次のコマンドを実行して、アプリケーションが必要な gem と依存関係で適切に設定されていることを確認します。

    Linux / macOS

    cd ruby-docs-samples/run/rails
    bundle install
    

    Windows

    cd ruby-docs-samples\run\rails
    bundle install
    

バッキング サービスの準備

このチュートリアルでは、さまざまな Google Cloud サービスを使用して、デプロイ済みの Rails プロジェクトをサポートするデータベース、メディア ストレージ、シークレット ストレージを準備します。これらのサービスは、特定のリージョンにデプロイされます。サービス間の効率を高めるために、すべてのサービスを同じリージョンにデプロイすることをおすすめします。最も近いリージョンの詳細については、リージョン別に提供されるプロダクトをご覧ください。

Cloud SQL for PostgreSQL インスタンスを設定する

Rails は、Cloud SQL で提供されているものを含め、複数のリレーショナル データベースに対応しています。このチュートリアルでは、Rails アプリでよく使用されるオープンソース データベースである PostgreSQL を使用します。

以降のセクションでは、Rails アプリ用に PostgreSQL インスタンス、データベース、データベース ユーザーを作成する方法について説明します。

PostgreSQL インスタンスを作成する

Console

  1. Google Cloud コンソールで Cloud SQL の [インスタンス] ページに移動します。

    Cloud SQL インスタンス ページに移動

  2. [インスタンスを作成] をクリックします。

  3. [PostgreSQL を選択] をクリックします。

  4. [インスタンス ID] フィールドに、インスタンスの名前(INSTANCE_NAME)を入力します。

  5. [パスワード] フィールドに、postgres ユーザーのパスワードを入力します。

  6. 他のフィールドはデフォルト値を使用します。

  7. [インスタンスを作成] をクリックします。

gcloud

  • PostgreSQL インスタンスを作成します。

    gcloud sql instances create INSTANCE_NAME \
        --database-version POSTGRES_12 \
        --tier db-f1-micro \
        --region REGION
    

    次のように置き換えます。

    インスタンスの作成と使用準備が完了するまでに数分かかります。

データベースの作成

Console

  1. Google Cloud コンソールで Cloud SQL の [インスタンス] ページに移動します。

    Cloud SQL インスタンス ページに移動

  2. INSTANCE_NAME インスタンスを選択します。

  3. [データベース] タブに移動します。

  4. [データベースを作成] をクリックします。

  5. [データベース名] ダイアログで「DATABASE_NAME」と入力します。

  6. [作成] をクリックします。

gcloud

  • 最近作成したインスタンス内にデータベースを作成します。

    gcloud sql databases create DATABASE_NAME \
        --instance INSTANCE_NAME
    

    DATABASE_NAME を、このインスタンス内のデータベースの名前に置き換えます。

ユーザーを作成する

データベース ユーザーのランダムなパスワードを生成し、dbpassword という名前のファイルに書き込みます。

cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1 > dbpassword

Console

  1. Google Cloud コンソールで Cloud SQL の [インスタンス] ページに移動します。

    Cloud SQL インスタンス ページに移動

  2. INSTANCE_NAME インスタンスを選択します。

  3. [ユーザー] タブに移動します。

  4. [ユーザー アカウントを追加] をクリックします。

  5. [組み込み認証] ダイアログで、次の操作を行います。

    1. DATABASE_USERNAME というユーザー名を入力します。
    2. dbpassword ファイルの内容をパスワード PASSWORD として入力します。
  6. [Add(追加)] をクリックします。

gcloud

  • 最近作成したインスタンス内にユーザーを作成し、そのパスワードを dbpassword のコンテンツに設定します。

    gcloud sql users create DATABASE_USERNAME \
       --instance=INSTANCE_NAME --password=$(cat dbpassword)
    

    DATABASE_USERNAME を、このインスタンス内のユーザーの名前に置き換えます。

Cloud Storage バケットを設定する

Rails の静的アセットとユーザーがアップロードしたメディアを、Cloud Storage を使用する高可用性オブジェクト ストレージでホストできます。

Console

  1. In the Google Cloud console, go to the Cloud Storage Buckets page.

    Go to Buckets page

  2. Click Create bucket.
  3. On the Create a bucket page, enter your bucket information. To go to the next step, click Continue.
    • For Name your bucket, enter a name that meets the bucket naming requirements.
    • For Location, select the following: us-central1
    • For Choose a default storage class for your data, select the following: Standard.
    • For Choose how to control access to objects, select an Access control option.
    • For Advanced settings (optional), specify an encryption method, a retention policy, or bucket labels.
  4. Click Create.

gcloud

gsutil コマンドライン ツールは、gcloud CLI のインストールの一部としてインストールされます。

  • Cloud Storage バケットを作成します。一意の Cloud Storage バケット名を作成するには、PROJECT_ID と任意の接尾辞(MEDIA_BUCKET_SUFFIX)を使用します。Cloud Storage では、バケット名は、グローバルに一意でなければなりません。

    gsutil mb -l REGION gs://PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

バケットの作成後、アップロードした画像を公開するには、誰でも閲覧できるように画像オブジェクトの権限を変更します。

Console

  1. Google Cloud コンソールで、Cloud Storage の [バケット] ページに移動します。

    [バケット] に移動

  2. バケットのリストで、公開するバケットの名前をクリックします。

  3. ページ上部にある [権限] タブを選択します。

  4. [メンバーを追加] ボタンをクリックします。

    [メンバーを追加] ダイアログ ボックスが表示されます。

  5. [新しいメンバー] フィールドに「allUsers」と入力します。

  6. [役割を選択] プルダウンで [Cloud Storage] サブメニューを選択し、[ストレージ オブジェクト閲覧者] オプションをクリックします。

  7. [保存] をクリックします。

公開の状態で共有されると、[公開アクセス] 列に各オブジェクトのリンクアイコンが表示されます。このアイコンをクリックすると、オブジェクトの URL を取得できます。

Google Cloud コンソールで失敗した Ruby オペレーションに関する詳細なエラー情報を確認する方法については、トラブルシューティングをご覧ください。

gcloud

  • すべてのオブジェクトを公開するには、gsutil iam ch コマンドを使用します。バケットの作成時に使用した MEDIA_BUCKET_SUFFIX の値を使用します。

    gsutil iam ch allUsers:objectViewer gs://PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

Secret Manager にシークレット値を保存する

バッキング サービスの構成が完了したので、Rails はこれらのサービスにアクセスするためのパスワードなどの安全な情報を必要とします。このチュートリアルでは、これらの値を Rails のソースコードに直接入力せず、Rails 認証情報を使用して、Secret Manager でこの情報を安全に保存します。

暗号化された認証情報ファイルを作成し、鍵を Secret Manager のシークレットとして保存する

Rails は、「config/credentials.yml.enc」という暗号化されたファイルにシークレットを保存します。ファイルは、ローカルの config/master.key または環境変数 ENV[“RAILS_MASTER_KEY”] で復号できます。認証情報ファイルには、Cloud SQL インスタンス データベースのパスワードと、外部 API のその他のアクセスキーを格納できます。

この鍵は Secret Manager に安全に格納できます。次に、それぞれのサービス アカウントへのアクセス権を付与して、Cloud Run と Cloud Build に鍵へのアクセス権を付与します。サービス アカウントは、プロジェクト番号を含むメールアドレスで識別されます。

  1. 次のコマンドを実行して config/credentials.yml.enc ファイルを生成します。

    bin/rails credentials:edit
    

    マスター鍵が定義されていない場合、このコマンドは config/master.key を作成します。ファイルが存在しない場合、config/credentials.yml.enc ファイルを作成します。これによりデフォルトの $EDITOR に一時ファイルが開き、シークレット用に復号されたコンテンツが追加されます。

  2. 新しく作成した PostgreSQL インスタンス データベース パスワードを dbpassword ファイルからコピーして、認証情報ファイルに貼り付けます。

    secret_key_base: GENERATED_VALUE
    gcp:
      db_password: PASSWORD
    

    シークレットには Rails.application.credentials を使用してアクセスできます。たとえば、Rails.application.credentials.secret_key_base はアプリケーションの秘密鍵ベースを返し、Rails.application.credentials.gcp[:db_passsword] はデータベース パスワードを返します。

  3. config/credentials/yml.enc は暗号化されて保存されますが、config/master.key は Secret Manager に保存することができます。

    Console

    1. Google Cloud コンソールで、[Secret Manager] ページに移動します。

      [Secret Manager] ページに移動

    2. [シークレットの作成] をクリックします。

    3. [名前] フィールドに、シークレットの名前 RAILS_SECRET_NAME を入力します。

    4. [シークレットの値] ダイアログで、mater.key の値をボックスに貼り付けます。

    5. [シークレットの作成] をクリックします。

    6. シークレットの [シークレットの詳細] ページで、プロジェクト番号をメモします。

      projects/PROJECTNUM/secrets/RAILS_SECRET_NAME

    7. [権限] タブで、[メンバーを追加] をクリックします。

    8. [新しいメンバー] フィールドに「PROJECTNUM-compute@developer.gserviceaccount.com」と入力し、Enter を押します。

    9. [新しいメンバー] フィールドに「PROJECTNUM@cloudbuild.gserviceaccount.com」と入力し、Enter を押します。

    10. [ロール] プルダウン メニューで [Secret Manager のシークレット アクセサー] を選択します。

    11. [保存] をクリックします。

    gcloud

    1. config/master.key の値を含む新しいシークレットを作成します。

      gcloud secrets create RAILS_SECRET_NAME --data-file config/master.key
      

      RAILS_SECRET_NAME を新しいシークレットの名前に置き換えます。

    2. シークレットの作成を確認するには、次のことを確認します。

      gcloud secrets describe RAILS_SECRET_NAME
      
      gcloud secrets versions access latest --secret RAILS_SECRET_NAME
      
    3. プロジェクト番号の値を取得します。

      gcloud projects describe PROJECT_ID --format='value(projectNumber)'
      
    4. Cloud Run サービス アカウントにシークレットへのアクセス権を付与します。

      gcloud secrets add-iam-policy-binding RAILS_SECRET_NAME \
          --member serviceAccount:PROJECTNUM-compute@developer.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      

      PROJECTNUM は、前述のプロジェクト番号に置き換えます。

    5. Cloud Build サービス アカウントにシークレットへのアクセス権を付与します。

      gcloud secrets add-iam-policy-binding RAILS_SECRET_NAME \
          --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      

      出力で、bindings が 2 つのサービス アカウントをメンバーとしてリストしていることを確認します。

Rails アプリを本番環境のデータベースとストレージに接続する

このチュートリアルでは、本番環境データベースとして PostgreSQL インスタンスを使用し、ストレージ バックエンドとして Cloud Storage を使用します。新しく作成したデータベースとストレージ バケットに Rails を接続するには、アクセスするために必要なすべての情報を .env ファイルで指定する必要があります。.env ファイルには、アプリケーションの環境変数の構成が含まれています。アプリケーションは dotenv gem を使用してこのファイルを読み取ります。シークレットは credentials.yml.enc と Secret Manager に保存されるため、.env には機密性の高い認証情報が保持されていないことから、暗号化は不要です。

  1. データベースとストレージ バケットに接続するように Rails アプリを構成するには、.env ファイルを開きます。
  2. .env ファイルの構成を次のように変更します。バケットの作成時に使用した MEDIA_BUCKET_SUFFIX の値を使用します。

    PRODUCTION_DB_NAME: DATABASE_NAME
    PRODUCTION_DB_USERNAME: DATABASE_USERNAME
    CLOUD_SQL_CONNECTION_NAME: PROJECT_ID:REGION:INSTANCE_NAME
    GOOGLE_PROJECT_ID: PROJECT_ID
    STORAGE_BUCKET_NAME: PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

    これで、Cloud Run へのデプロイ時に Cloud SQL と Cloud Storage を使用するように Rails アプリが設定されました。

Cloud Build に Cloud SQL へのアクセス権を付与する

Cloud Build でデータベースの移行を適用するには、Cloud Build に Cloud SQL へのアクセス権を付与する必要があります。

Console

  1. Google Cloud コンソールで、[Identity and Access Management] ページに移動します。

    [Identity and Access Management] ページに移動

  2. PROJECTNUM@cloudbuild.gserviceaccount.com メンバーのエントリを編集するには、 [メンバーを編集] をクリックします。

  3. [別のロールを追加] をクリックします。

  4. [ロールを選択] ダイアログで、[Cloud SQL クライアント] を選択します。

  5. [保存] をクリックします。

gcloud

  • Cloud Build に Cloud SQL へのアクセス権を付与します。

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
        --role roles/cloudsql.client
    

Cloud Run へのアプリのデプロイ

このバッキング サービスを設定すると、アプリを Cloud Run サービスとしてデプロイできます。

  1. 提供された cloudbuild.yaml を使用して、Cloud Build を使用してイメージをビルドし、データベースの移行を実行し、静的アセットにデータを入力します。

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions _SERVICE_NAME=SERVICE_NAME,_INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION,_SECRET_NAME=RAILS_SECRET_NAME
    

    SERVICE_NAME はサービスの名前に置き換えます。最初のビルドが完了するまで数分かかります。 ビルドがタイムアウトした場合は、上記のビルドコマンドに --timeout=2000s を挿入してタイムアウト時間を長くしてください。

  2. ビルドが成功したら、Cloud Run サービスを初めてデプロイし、サービス リージョン、ベースイメージ、接続された Cloud SQL インスタンスを設定します。

    gcloud run deploy SERVICE_NAME \
         --platform managed \
         --region REGION \
         --image gcr.io/PROJECT_ID/SERVICE_NAME \
         --add-cloudsql-instances PROJECT_ID:REGION:INSTANCE_NAME \
         --allow-unauthenticated
    

    サービスの URL とともに、デプロイが成功したことを示す出力が表示されます。

    Service [SERVICE_NAME] revision [SERVICE_NAME-00001-tug] has been deployed
     and is serving 100 percent of traffic at https://SERVICE_NAME-HASH-uc.a.run.app

  3. デプロイされたサービスを確認するには、サービスの URL に移動します。

    猫アルバム アプリケーションのランディング ページのスクリーンショット。
    サービス URL に猫のフォトアルバムが表示されている場合は、アプリのホームページが表示されています。

  4. 新しい写真をアップロードしてみてください。写真が正常にアップロードされると、Rails アプリケーションが正常にデプロイされています。

    猫アルバム アプリケーションのランディング ページと写真のスクリーンショット。

アプリケーションを更新する

最初のプロビジョニングとデプロイの手順は複雑でしたが、更新はより簡単なプロセスです。

  1. Cloud Build のビルドと移行スクリプトを実行します。

    gcloud builds submit --config cloudbuild.yaml \
         --substitutions _SERVICE_NAME=SERVICE_NAME,_INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION,_SECRET_NAME=RAILS_SECRET_NAME
    
  2. リージョンとイメージのみを指定して、サービスをデプロイします。

    gcloud run deploy SERVICE_NAME \
         --platform managed \
         --region REGION \
         --image gcr.io/PROJECT_ID/SERVICE_NAME
    

コードについて

Rails サンプルアプリが標準の Rails コマンドを使用して作成されました。次のコマンドは cat_album アプリを作成し、scaffold コマンドを使用して Photo リソースのモデル、コントローラ、ビューを生成します。

rails new cat_album
rails generate scaffold Photo caption:text

データベースへの接続

config/database.yml ファイルには、さまざまな環境(開発環境、テスト環境、本番環境)でデータベースにアクセスするために必要な構成が含まれています。たとえば、本番環境のデータベースは、Cloud SQL for PostgreSQL で実行するように構成されています。データベース名とユーザー名は .env ファイル内の環境変数を介して設定されますが、データベース パスワードは config/credentials.yml.enc ファイル内に保存されます。このため、RAILS_MASTER_KEY で復号する必要があります。

アプリは、Cloud Run(フルマネージド)で実行されると、Cloud Run 環境から提供されるソケットを使用して PostgreSQL インスタンスに接続されます。アプリは、ローカルマシンで実行されると、Cloud SQL Auth プロキシを使用して PostgreSQL インスタンスに接続されます。

production:
  <<: *default
  database: <%= ENV["PRODUCTION_DB_NAME"] %>
  username: <%= ENV["PRODUCTION_DB_USERNAME"] %>
  password: <%= Rails.application.credentials.gcp[:db_password] %>
  host: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["CLOUD_SQL_CONNECTION_NAME"] %>"

クラウドに保存されたユーザーがアップロードしたメディア

Rails は Active Storage を使用してクラウド ストレージ プロバイダにファイルをアップロードします。config/storage.yml ファイルと config/environments/production.rb ファイルでは、本番環境でサービス プロバイダとして Cloud Storage を指定します。

google:
  service: GCS
  project: <%= ENV["GOOGLE_PROJECT_ID"] %>
  bucket: <%= ENV["STORAGE_BUCKET_NAME"] %>
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :google

Cloud Build による自動化

cloudbuild.yaml ファイルは、一般的なイメージ ビルドステップ(コンテナ イメージを作成して Container Registry に push する)だけでなく、Rails データベースの移行も行います。これにはデータベースへのアクセスが必要です。これは、app-engine-exec-wrapperCloud SQL Auth プロキシのヘルパー)を使用して実行されます。

steps:
  - id: "build image"
    name: "gcr.io/cloud-builders/docker"
    entrypoint: 'bash'
    args: ["-c", "docker build --build-arg MASTER_KEY=$$RAILS_KEY -t gcr.io/${PROJECT_ID}/${_SERVICE_NAME} . "]
    secretEnv: ["RAILS_KEY"]

  - id: "push image"
    name: "gcr.io/cloud-builders/docker"
    args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"]

  - id: "apply migrations"
    name: "gcr.io/google-appengine/exec-wrapper"
    entrypoint: "bash"
    args:
      [
        "-c",
        "/buildstep/execute.sh -i gcr.io/${PROJECT_ID}/${_SERVICE_NAME} -s ${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME} -e RAILS_MASTER_KEY=$$RAILS_KEY -- bundle exec rails db:migrate"
      ]
    secretEnv: ["RAILS_KEY"]

substitutions:
  _REGION: us-central1
  _SERVICE_NAME: rails-cat-album
  _INSTANCE_NAME: cat-album
  _SECRET_NAME: rails-master-key

availableSecrets:
  secretManager:
  - versionName: projects/${PROJECT_ID}/secrets/${_SECRET_NAME}/versions/latest
    env: RAILS_KEY

images:
  - "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"

この構成では代入変数が使用されます。ファイルの値を直接変更すると、移行時に --substitutions フラグを省略できます。

この構成では、db/migrate ディレクトリ内の既存の移行のみが適用されます。移行ファイルを作成するには、Active Record Migrations をご覧ください。

イメージをビルドして移行を適用するには、Cloud Build 構成が Secret Manager の RAILS_MASTER_KEY シークレットにアクセスする必要があります。availableSecrets フィールドには、シークレットに使用するシークレットのバージョンと環境変数を設定します。マスター鍵のシークレットは、ビルドイメージの手順で引数として渡され、イメージのビルド時に Dockerfile 内で RAILS_MASTER_KEY に設定されます。

ARG MASTER_KEY
ENV RAILS_MASTER_KEY=${MASTER_KEY}

2 つのコマンドを実行せずに 1 つの構成にデプロイを含めるように Cloud Build の構成を拡張するには、Cloud Build を使用した git による継続的デプロイをご覧ください。説明されているように、これには IAM の変更が必要です。

Ruby 2.7 のサポート

このチュートリアルでは Ruby 3.0 を使用していますが、Ruby 2.7 もサポートできます。Ruby 2.7 を使用するには、Dockerfile 内の Ruby ベースイメージを 2.7 に変更します。

# Pinning the OS to buster because the nodejs install script is buster-specific.
# Be sure to update the nodejs install command if the base image OS is updated.
FROM ruby:3.0-buster

クリーンアップ

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.