[GCP]Cloud Buildの中からCloud SQLにrake db:migrateしてみた話

Tweet

RailsアプリをCloud Runにデプロイするにあたり、Cloud Buildの中でrake db:migrateがしたくなった。
サクッといけるかと思ったら半日以上時間がかかったのでブログにまとめておく。

注意事項

実験的に作成しただけで本番稼働させたものではありません。 ご注意ください。

ザックリとした図

最終的に書いたcloudbuild.yml(一部伏せてます)

substitutions:
  _CLOUD_SQL_INSTANCE: '【Cloud SQLの接続名】'
  _CLOUD_SQL_PROXY_VERSION: 'v1.24.0'
steps:
- id: 'image-build'
  name: 'gcr.io/kaniko-project/executor:latest'
  args:
    - --destination=gcr.io/$PROJECT_ID/image:$COMMIT_SHA
    - --dockerfile=Dockerfile
    - --cache=true
    - --cache-ttl=24h
  waitFor:
    - '-'
- id: 'proxy-install'
  name: 'gcr.io/cloud-builders/wget'
  args:
    - "-P"
    - "/workspace"
    - "-O"
    - "cloud_sql_proxy"
    - "https://storage.googleapis.com/cloudsql-proxy/${_CLOUD_SQL_PROXY_VERSION}/cloud_sql_proxy.linux.amd64"
  waitFor:
    - '-'
- id: 'db-migration'
  name: 'gcr.io/$PROJECT_ID/image:$COMMIT_SHA'
  entrypoint: sh
  args:
    - '-c'
    - |-
      chmod +x /workspace/cloud_sql_proxy
      mkdir -p /cloudsql
      /workspace/cloud_sql_proxy -dir=/cloudsql -instances=${_CLOUD_SQL_INSTANCE} &
      rake db:migrate      
  env:
    - 'RAILS_ENV=production'
    - 'DB_USER=app'
    - 'INSTANCE_CONNECTION_NAME=${_CLOUD_SQL_INSTANCE}'
  secretEnv: ['DB_PASS', 'RAILS_MASTER_KEY']
  waitFor:
    - 'image-build'
    - 'proxy-install'
- id: 'cloud-run-deploy'
  name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  entrypoint: 'gcloud'
  args: ['run', 'deploy', 'SERVICE-NAME', '--image', 'gcr.io/$PROJECT_ID/image:$COMMIT_SHA', '--platform', 'managed']
  waitFor:
    - 'db-migration'
availableSecrets:
  secretManager:
  - versionName: 'projects/【PROJECT_ID】/secrets/【DB_PASSのSecret】/versions/latest'
    env: 'DB_PASS'
  - versionName: 'projects/【PROJECT_ID】/secrets/【RAILS_MASTER_KEYのSecret】/versions/latest'
    env: 'RAILS_MASTER_KEY'

何をしているのか

image-build

- id: 'image-build'
  name: 'gcr.io/kaniko-project/executor:latest'
  args:
    - --destination=gcr.io/$PROJECT_ID/image:$COMMIT_SHA
    - --dockerfile=Dockerfile
    - --cache=true
    - --cache-ttl=24h
  waitFor:
    - '-'

ここではkanikoを使ってDocker buildを行っています。 GoogleContainerTools/kaniko

proxy-install

- id: 'proxy-install'
  name: 'gcr.io/cloud-builders/wget'
  args:
    - "-P"
    - "/workspace"
    - "-O"
    - "cloud_sql_proxy"
    - "https://storage.googleapis.com/cloudsql-proxy/${_CLOUD_SQL_PROXY_VERSION}/cloud_sql_proxy.linux.amd64"
  waitFor:
    - '-'

ここではCloud BuildからCloud SQLへ接続するために使うcloudsql-proxyをダウンロードしています。
ひとまず自動でマウントされる/workspace配下に置いてますが、独自にVolumeを設定するなどしてもよいと思います。
GoogleCloudPlatform/cloudsql-proxy
Cloud SQL Auth Proxy について

db-migration

- id: 'db-migration'
  name: 'gcr.io/$PROJECT_ID/image:$COMMIT_SHA'
  entrypoint: sh
  args:
    - '-c'
    - |-
      chmod +x /workspace/cloud_sql_proxy #実行したいので、実行権限付与
      mkdir -p /cloudsql #Unix Socketが格納されるディレクトリ作成
      /workspace/cloud_sql_proxy -dir=/cloudsql -instances=${_CLOUD_SQL_INSTANCE} & # Cloud SQL Proxyをバックグラウンドジョブとして実行
      rake db:migrate # DBマイグレーションを実行      
  env:
    - 'RAILS_ENV=production'
    - 'DB_USER=app'
    - 'INSTANCE_CONNECTION_NAME=${_CLOUD_SQL_INSTANCE}'
  secretEnv: ['DB_PASS', 'RAILS_MASTER_KEY']
  waitFor:
    - 'image-build'
    - 'proxy-install'

大本命。
Cloud SQLへDBのマイグレーションを実行します。
DBのパスワードやRAILS_MASTER_KEYはSecret Managerに格納しており、それらを利用します。

availableSecrets:
  secretManager:
  - versionName: 'projects/【PROJECT_ID】/secrets/【DB_PASSのSecret】/versions/latest'
    env: 'DB_PASS'
  - versionName: 'projects/【PROJECT_ID】/secrets/【RAILS_MASTER_KEYのSecret】/versions/latest'
    env: 'RAILS_MASTER_KEY'

cloud-run-deploy

- id: 'cloud-run-deploy'
  name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  entrypoint: 'gcloud'
  args: ['run', 'deploy', 'SERVICE-NAME', '--image', 'gcr.io/$PROJECT_ID/image:$COMMIT_SHA', '--platform', 'managed']
  waitFor:
    - 'db-migration'

DBも用意ができたので、Cloud Runにデプロイ実行。

以上。

ハマリポイント

適切な権限設定

ロールの割当を忘れていて動かないこともあった。
(今回は実験なのでかなり派手に権限設定している。本来なら条件も追加して行うべき)

Docker版 Cloud SQL ProxyでUnix Socketが使えなかった

最初はDockerでCloud SQL Proxyを実行して接続しようとしたが、なぜか権限エラーで動かず。
バイナリ版を使って愚直に進めると動くという不思議な状態

gcr.io/cloudsql-docker/gce-proxy:1.24.0-buster
2021/08/09 14:09:11 current FDs rlimit set to 1048576, wanted limit is 8500. Nothing to do here.
2021/08/09 14:09:12 listen unix /cloudsql/【Cloud SQLの接続名】: bind: permission denied
  - id: 'cloudsql-proxy'
    name: 'gcr.io/cloudsql-docker/gce-proxy:1.24.0-buster'
    args: [ '/cloud_sql_proxy', '-dir=/cloudsql', '-instances=s${_CLOUD_SQL_INSTANCE}']
    volumes:
      - name: cloudsql
        path: /cloudsql
    waitFor:
      - '-'