LoginSignup
4
1

More than 1 year has passed since last update.

M1 MacでGCP Cloud RunへRails6デプロイするtutorial試したので詰まったポイントをメモ

Posted at

前回M1 MacでGCP Cloud RunのQuickstart(ruby版)試したので詰まったポイントをメモで、sinatraアプリをCloud Runにデプロイした。

次はRailsを試してみる。

  • GCP Cloud Runに
  • Ruby v3.0.3で Rails 6のアプリを
  • M1 Macの環境から

デプロイしてみる。

ちょうどRails6を利用したチュートリアルが用意されているので、それをやっていく。

Cloud Run 環境での Rails の実行

準備

今回実験に使うプロジェクトをgcloudのデフォルトプロジェクトとして設定したい。
プロジェクトIDを探しにブラウザを動かすのがめんどくさい場合、CLIでも探すことができる。

$ gcloud projects list
PROJECT_ID                NAME                     PROJECT_NUMBER
test-project-41231412  Test Project              412314......

その上で、見つけたプロジェクトIDをデフォルトプロジェクトに設定する。

# この例ではプロジェクトIDはrails-cloud-run-test
$ gcloud config set project rails-cloud-run-test

Railsのサンプルアプリの取得

$ git clone https://github.com/GoogleCloudPlatform/ruby-docs-samples.git
$ cd ruby-docs-samples/run/rails
$ bundle install

# 前回の学びで、Gemfile.lockにx86 platformを追加しないとCloud Buildのビルド時にコケるため、対応
$ bundle lock --add-platform x86_64-linux

bundle installでsasscのインストールエラー

bundle install中、sasscのインストールでエラーが出る。

An error occurred while installing sassc (2.1.0), and Bundler cannot continue.

In Gemfile:
  sass-rails was resolved to 6.0.0, which depends on
    sassc-rails was resolved to 2.1.2, which depends on
      sassc

どこで落ちてるか。もう少し上のほうのログを確認する。

compiling ./libsass/src/ast.cpp
clang: error: the clang compiler does not support '-march=native'
make: *** [ast.o] Error 1

make failed, exit code 2

解決策

解決策はここに書かれていた。

要するに以下を実行すればよい。

$ gem install sassc -- --disable-march-tune-native

ただ、せっかくGemfileがあるのに別個にgem installするのは面倒である。
これをbundleで実行する方法は以下。

$ bundle config build.sassc --disable-march-tune-native

これを実行後、bundle installするとすんなり通る。

DB(CloudSQLインスタンス)の作成

# インスタンス名はrails-cloud-run-devとする
$ gcloud sql instances create rails-cloud-run-dev \
    --database-version POSTGRES_14 \
    --tier db-f1-micro \
    --region asia-northeast1

インスタンス作成には結構時間がかかる。上記コマンドは12分完了までに必要だった。

CloudSQLインスタンス内へのデータベースの作成

# インスタンス名はrails-cloud-run-dev
# データベース名はmain
$ gcloud sql databases create main --instance rails-cloud-run-dev

CloudSQLインスタンスへ接続できるユーザーの作成

# パスワードを生成し、dbpasswordというファイルに書き込む
$ cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1 > dbpassword
# インスタンス名はrails-cloud-run-dev
# ユーザー名はdevuser
$ gcloud sql users create devuser \
   --instance=rails-cloud-run-dev --password=$(cat dbpassword)

Cloud Storageバケットの作成

gsutilコマンドを使う。

gsutil は、コマンドラインから Cloud Storage にアクセスできる Python アプリケーションです。

awscliでいうaws s3と同じか。GCPではCloud Storageに特化したコマンドとしてgsutilがあるらしい。

# 最後の引数はgs://[PROJECT_ID]-[MEDIA_BUCKET]
# この例のPROJECT_IDはrails-cloud-run-test
# MEDIA_BUCKETはimage-storage
$ gsutil mb -l asia-northeast1 gs://rails-cloud-run-test-image-storage

サブコマンドのmbMake bucketsの略。

このバケットへは、誰でも見られる画像ファイルを配置する予定のため、バケット内を閲覧可能にする。

$ gsutil iam ch allUsers:objectViewer gs://rails-cloud-run-test-image-storage

chchangeの略。gsutil iamはサブコマンドとしてget, set, chを持つ。

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

$ bin/rails credentials:edit

bin/rails credentials:editでbootsnapのエラー発生

上記コマンドを実行すると以下のエラーが出た。

/Users/ryan5500/.anyenv/envs/rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/bootsnap-1.7.5/lib/bootsnap/compile_cache/iseq.rb:13:in `to_binary': wrong argument type false (expected Symbol) (TypeError)

Ruby3.0.3で発生するバグらしく、bootsnapの1.9.2で修正されている。

解決策

Gemfileの該当箇所を修正。

gem "bootsnap", ">= 1.9.2", require: false
$ bundle update bootsnap

これで解決する。

Secret Managerにsecretを登録する。

# RAILS_SECRET_NAMEはここではrails-secret
$ gcloud secrets create rails-secret --data-file config/master.key

Cloud Runサービスにsecretへのアクセス権を付与する。

# RAILS_SECRET_NAMEはここではrails-secret
# ここでのプロジェクト番号は565343841115
$ gcloud secrets add-iam-policy-binding rails-secret \
    --member serviceAccount:565343841115-compute@developer.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor

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

# RAILS_SECRET_NAMEはここではrails-secret
# ここでのプロジェクト番号は565343841115
$ gcloud secrets add-iam-policy-binding rails-secret \
    --member serviceAccount:565343841115@cloudbuild.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor

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

.envを作成する。

PRODUCTION_DB_NAME: main
PRODUCTION_DB_USERNAME: devuser
CLOUD_SQL_CONNECTION_NAME: rails-cloud-run-test:asia-northeast1:rails-cloud-run-dev
GOOGLE_PROJECT_ID: rails-cloud-run-test
STORAGE_BUCKET_NAME: rails-cloud-run-test-image-storage

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

これは、Cloud Buildでアプリケーションイメージをビルドした後、DBマイグレーションを実行するので、予めCloud BuildにCloud SQLへのアクセス権を付与している。

# この例ではプロジェクトIDはrails-cloud-run-test
# ここでのプロジェクト番号は565343841115
$ gcloud projects add-iam-policy-binding rails-cloud-run-test \
    --member serviceAccount:565343841115@cloudbuild.gserviceaccount.com \
    --role roles/cloudsql.client

Cloud Buildで何を実行するかの指定は、gcloud builds submitする際に設定ファイルを指定できて、今回の例では、railsのフォルダ内のcloudbuild.ymlにて行っている。

Cloud Buildでビルドを行う

以下で、cloudbuild.ymlを使ってビルドを依頼する。

# 今回の例ではSERVICE_NAMEはrails-test-service
# INSTANCE_NAMEは、上で決めたrails-cloud-run-dev
# REGIONは、asia-northeast1
# RAILS_SECRET_NAMEは、rails-secret
$ gcloud builds submit --config cloudbuild.yaml \
    --substitutions _SERVICE_NAME=rails-test-service,_INSTANCE_NAME=rails-cloud-run-dev,_REGION=asia-northeast1,_SECRET_NAME=rails-secret

このコマンドを打つと、何かアップロードしているログが出る。
CloudBuildのログやストレージの内容を見る限り、以下の処理を行っている。

  1. gcloud builds submitを実行しているパスのファイル群をtarファイルにまとめ、Cloud Buildの実行用に作成されたCloud Storageにアップロードしている。
  2. Cloud Buildの実行用に作成されたCloud Storageから、tgzファイルを取得し、cloudbuild.ymlで指定されたdocker build等のstepを順次実行している。

ビルドされたRailsアプリイメージをCloud Runへデプロイする

$ gcloud run deploy rails-test-service \
     --platform managed \
     --region asia-northeast1 \
     --image gcr.io/rails-cloud-run-test/rails-test-service \
     --add-cloudsql-instances rails-cloud-run-test:asia-northeast1:rails-cloud-run-dev \
     --allow-unauthenticated

デプロイ完了後、アクセスできるURLが表示される。

URLにアクセスしたところ、レスポンスが戻るのに5秒ぐらいはかかる。(初回起動かつRailsであるため)
前回Sinatraだと3秒程度だったので、倍ほどかかる。

気になった点

database.ymlのhost設定が謎

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"] %>"

hostが/cloudsql/rails-cloud-run-test:asia-northeast1:rails-cloud-run-devこれになるのだが、どうやって接続しているのか。

adapterがmysqlだとsocketという設定値でソケットを使うが、adapterがpostgresqlだとhostの設定値でソケットを指定できるらしい。つまりhostの値はパスである。

ここにも記載があった。

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

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1