Cloud BuildでCD時にCloud SQL Proxyを使ってマイグレーションしてみた話

この記事は [ミクシィグループ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/mixi) の8日目の記事です。

初めましての方は初めまして、19新卒エンジニアの @Taillook です。
Qiitaで書くのは久しぶりです。

最近、仕事上でGoogle CloudのCloud Buildを初めて使う機会があったので書いてみます。

## Cloud Buildとは
> Cloud Build は、Google Cloud Platform のインフラストラクチャでビルドを行うサービスです。

> Cloud Build は、さまざまなリポジトリやクラウド ストレージ スペースからソースコードをインポートし、仕様に合わせてビルドを実行し、Docker コンテナや Java アーカイブなどのアーティファクトを生成できます。

引用: [Cloud Build の概要](https://cloud.google.com/cloud-build/docs/overview?hl=ja)

## やること
ここではCloud BuildでCloud SQLにCloud SQL Proxyで接続しマイグレーションしデプロイするstepを示し解説します。

## step.yaml

```yaml
steps:
  # Test steps
  - id: 'Test: Build Image: API'
    name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_API}-${_TEST_ENV}:$COMMIT_SHA', '-f', '${_TEST_DOCKER_FILE_API}', '.']

  - id: 'Test: API'
    name: 'gcr.io/cloud-builders/docker'
    args: ['run', '--rm', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_API}-${_TEST_ENV}:$COMMIT_SHA', 'make', 'test']

  # Migrate step
  - id: 'Build Image: Migrator'
    name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_MIGRATE}:$COMMIT_SHA', '-f', '${_DOCKER_FILE_MIGRATE}', '.']

  - id: 'Run: cloudsql-proxy'
    name: 'gcr.io/cloudsql-docker/gce-proxy:1.15'
    args: ['/cloud_sql_proxy', '-dir=/cloudsql', '-instances=$_CLOUD_SQL_CONNECTION_NAME']
    volumes:
      - name: cloudsql
        path: /cloudsql
    waitFor:
      - 'Test: API'

  - id: 'Migrate: Database'
    name: 'gcr.io/cloud-builders/docker'
    args: ['run','--rm', '-v', 'cloudsql:/cloudsql', '-e', 'ENV=${_DEPLOY_ENV}', '-e', 'DB_SOCKET=${_DB_SOCKET}', '-e', 'DB_USER=${_DB_USER}', '-e', 'DB_PASSWORD=${_DB_PASSWORD}', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_MIGRATE}:$COMMIT_SHA', 'make', 'db_migrate_execute']
    volumes:
      - name: cloudsql
        path: /cloudsql
    waitFor:
      - 'Build Image: Migrator'

  - id: 'Kill: cloudsql-proxy'
    name: 'gcr.io/cloud-builders/docker'
    waitFor:
      - 'Migrate: Database'
    entrypoint: 'sh'
    args: ['-c', 'docker ps -q --filter ancestor="gcr.io/cloudsql-docker/gce-proxy:1.15"|xargs docker stop']

  # Deploy steps
  - id: 'Build Image: API'
    name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_API}-${_DEPLOY_ENV}:$COMMIT_SHA', '-f', '${_DOCKER_FILE_API}', '.']

  - id: 'Push Image: API'
    name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_API}-${_DEPLOY_ENV}:$COMMIT_SHA']

  - id: 'Deploy: API'
    name: 'gcr.io/cloud-builders/gcloud'
    args: [
      'beta', 'run', 'deploy', '${_PRODUCT_NAME_API}-${_DEPLOY_ENV}',
      '--platform', 'managed',
      '--allow-unauthenticated',
      '--project', '$PROJECT_ID',
      '--region', '${_REGION}',
      '--image', 'asia.gcr.io/$PROJECT_ID/${_PRODUCT_NAME_API}-${_DEPLOY_ENV}:$COMMIT_SHA',
      '--set-env-vars', 'ENV=${_DEPLOY_ENV},DB_USER=${_DB_USER},DB_SOCKET=${_DB_SOCKET},DB_NAME=${_DB_NAME},DB_PASSWORD=${_DB_PASSWORD}'
    ]

timeout: 1000s
```

## 解説
stepは大きく3つに分かれ、test、migrate、deployとなります。
ここではmigrateについて解説します。

### migrateの流れ
1. Migratorのビルド
2. cloudsql-proxyを`Test: API`の終了をトリガーに実行
3. `Build Image: Migrator`の終了をトリガーにマイグレーションを実行
4. `Migrate: Database`の終了をトリガーにcloudsql-proxyを終了

### waitForについて
[ビルドステップの順序の構成](https://cloud.google.com/cloud-build/docs/configuring-builds/configure-build-step-order?hl=ja)にある様にビルドステップのwaitForフィールドを使用してステップの順序を指定することができます。また、waitForを指定しない場合は先行するステップが終了するまで待機し実行されます。
ちなみに3で`Build Image: Migrator`の終了をトリガーにする理由はcloudsql-proxyと並列に実行されないとCloud SQLとの接続が出来ないためです。

### volumesについて
volumesではCloud SQL ProxyのソケットをMigratorに対して共有するための記述です。
これによりMigratorはCloud SQLへの接続を可能にしています。

### 所感
おそらくdocker composeでやればよくないか？との指摘があるかと思います。今回はCloud Buildの機能を活用したい＋docker-composeのyamlを書かなくてよいのでdocker composeを用いずに行いました。
また、cloudsql-proxyを終了をする処理はfilterを用いずにできた気がするのでやり直したいといった思いです。