Cloud Deploy + Cloud Run + RDB(Spanner) における DB スキーママイグレーションの設計
コンテキスト
いままでは GKE 前提で CI/CD を組んできたが、今回 Cloud Deploy + Cloud Run に挑戦中。
おそらくいままでだと GitHub Actions や initContainer で吸収してきた部分を別途の責務として切り出してデザインする必要あり。さてどうしよう
ref. 『色々できるぞ! Cloud Deploy!!』
デプロイ時、pre-deploy / post-deploy hooks が利用できるようになりました。
従来、デプロイ時にスキーマを変更する必要がある場合などは Cloud Deploy とは別にマイグレーションを設定しておく必要がありましたが、今後はこの機能を使って Cloud Deploy に集約することができます。
これが公式としても推奨されてそうな設計。
-
skaffold.yaml
でcustomAction
を定義 -
clouddeploy.yaml
でcustomActions
を呼び出す設定を追加 [1]
skaffold.yaml
の提供してくださっているサンプルを見ると:
customActions[].containers[].image
でイメージを指定して CLI を動かす。ここに golang-migrate 実行用イメージを作って指定すればよい。
ただ、この際に実行したいマイグレーションの状態は PR マージ時のそれでなくてはいけない。
なので release-please に打ってもらった semver に併せてイメージも作る建て付けの方がよさそう。
現状だと release PR のマージ時に Cloud Build 実行(skaffold build)してアーティファクト生成 → gcloud deploy releases create しているので同タイミングでビルドすればよいはず。
-
私は Terraform の google_clouddeploy_delivery_pipeline で
clouddeploy.yaml
相当の設定を書いてるのでこちらで対応。 ↩︎
わかっていない部分
そうなると以下が疑問点になる:
- Cloud Deploy Hooks で実行されるコンテナの実行主体の GSA は?誰に Spanner の権限を渡せばよい?
デプロイ前のアクションまたはデプロイ後のアクション、あるいはその両方を対象としたアクションを実行するように、Cloud Deploy と Skaffold を構成できます。これらのプログラムは、「フック」と呼ばれます。 デプロイ前フックとデプロイ後フックは、ロールアウトでデプロイ前とデプロイ後のジョブとして実行されます。
- ロールアウトとは?
- ジョブとは?
ロールアウトとは?
ロールアウトは、リリースをターゲットに関連付ける Cloud Deploy リソースです。
さらに細かくすると
「デプロイされる変更(コード、構成、またはその両方)を表す Cloud Deploy リソース」を「アプリケーションをデプロイする特定のランタイム環境(Kubernetes クラスタ、Cloud Run サービス、またはその他のサポートされているランタイム)。また、その環境の構成。」に関連付ける Cloud Deploy リソースです。
ジョブとは?
文言を抜粋してまとめ直すと
ロールアウトは、1 つ以上のフェーズで構成されます。標準のデプロイ戦略の場合は、stable という 1 つのフェーズのみです。各ロールアウト フェーズには、1 つ以上のジョブが含まれます。
つまり標準デプロイなら、ロールアウトにつき1つ以上ジョブが生える。少なくとも1つのジョブがリリースをターゲットにデプロイすることでロールアウトの実装を担うはず。prehook, posthook な customActions はここに対して別ジョブとして複数紐づいてくる、と認識した。
Cloud Deploy 実行環境
ここに「Cloud Build ワーカープール」って書いてあった...
Cloud Deploy の実行環境は、Cloud Deploy がレンダリング、プリデプロイ、デプロイ、検証、ポストデプロイのオペレーションを実行する環境です。実行環境は、次のコンポーネントで構成されます。
- Cloud Deploy がレンダリング、プリデプロイ、デプロイ、検証、ポストデプロイのオペレーションを実行する Cloud Build ワーカープール(デフォルトまたはプライベート)
- これらのアクションを実行するために Cloud Deploy を呼び出すサービス アカウント(デフォルトまたは代替)
- レンダリングされたマニフェストの Cloud Storage 内の保存場所(デフォルトまたは代替)
- オペレーションの Cloud Build タイムアウト(デフォルトまたはカスタム)
デフォルト
必要そうな部分を抜粋
デフォルトでは、Cloud Deploy はデフォルトの Cloud Build ワーカープールで実行されます。
デフォルトでは、Cloud Deploy は、デフォルトの Compute Engine サービス アカウントを使用します。
この値は、Cloud Deploy がレンダリングされたマニフェストを保存する Cloud Storage バケットです。デフォルトでは、Cloud Deploy は、Cloud Deploy リソースと同じリージョンに Cloud Storage バケットを作成します。形式は次のとおりです。
<location>.deploy-artifacts.<project ID>.appspot.com
デフォルトでは、Cloud Build が実行するオペレーションのタイムアウトは 1 時間です。
これを踏まえてやりたいことを定義し直すと、CloudBuild のデフォルトワーカープール上で Spanner にアクセスできる GSA(DAC) で golang-migrate を実行するイメージを CloudDeploy の prehook から kick したい、となる。
Spanner の接続情報
spanner://projects/{projectId}/instances/{instanceId}/databases/{databaseName}?param=true
ref. https://pkg.go.dev/github.com/golang-migrate/migrate/v4/database/spanner
各環境で Spanner の接続情報を拾うためには、Target, Stage などの情報から Environment を拾う必要がある。
↓ これは PREDEPLOY
とは異なる VERIFY
実行環境で使える環境変数の一覧。
ここに
CLOUD_RUN_PROJECT
タイプがRUN
のターゲットの場合、Cloud Run サービスが作成されたプロジェクト。
がある。Cloud Run を動かしたいそれぞれのプロジェクトに Spanner は横付けしてあるので、これが使える。 POSTDEPLOY
でも CLOUD_RUN_PROJECT
が環境変数に入っているのを確認したので PREDEPLOY
にも入りそう!
instanceId
, databaseName
を共通化すればこの情報だけでマイグレーション対象を Target で切り替えられる。
↑ 結局別タスクが入り migration 自動化を進めるまでにいかなかったので一旦確認できずでした...
もしどなたか知見あったらコメントください!