[GCP]Cloud Buildの中からCloud SQLにrake db:migrateしてみた話
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:
- '-'