GitHubActionsからCloudSQLインスタンスを手動で起動・停止し、夜間に自動停止するワークフローを構築する

はじめに

必要なときだけサービスが動いていれば良いステージング環境等の検証環境において、CloudSQLを常時稼働させていては余計なコストが嵩んでしまう。お金が無限にあるわけではないので、できればコストを削減したい。とはいえ、必要なタイミングで毎回コンソールに入ってインスタンスを起動し、使い終わったら停止する、という運用は面倒だ。

そこで、GitHubから手動でインスタンスを起動・停止でき、停止忘れ防止のために夜間に自動停止するワークフローを構築した。

ワークフローの内容

作成したワークフローが以下。

name: Manage Staging Cloud SQL

on:
  workflow_dispatch:
    inputs:
      action:
        description: "Action"
        required: true
        default: "start"
        type: choice
        options:
          - start
          - stop
  schedule:
    - cron: "0 12 * * *" # JST 21:00 (UTC+9)

jobs:
  manage-cloudsql:
    permissions:
      contents: "read"
      id-token: "write"
    runs-on: ubuntu-latest
    steps:
      - name: Google Auth
        uses: google-github-actions/auth@v2
        with:
          token_format: access_token
          workload_identity_provider: "${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}"
          service_account: "${{ secrets.SERVICE_ACCOUNT }}"
      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2
      - name: Manage CloudSQL Instance
        run: |
          INSTANCE_NAME=${{ secrets.CLOUDSQL_INSTANCE_NAME_STAGING }}
          STATUS=$(gcloud sql instances describe "$INSTANCE_NAME" --format="value(state)")

          echo "Requested action: $ACTION"
          echo "Current status of the instance: $STATUS"

          if [ "$ACTION" = "start" ] && [ "$STATUS" = "RUNNABLE" ]; then
            echo "Instance is already running."
            exit 0
          elif [ "$ACTION" = "stop" ] && [ "$STATUS" != "RUNNABLE" ]; then
            echo "Instance is already stopped."
            exit 0
          fi

          if [ "$ACTION" = "start" ]; then
            gcloud sql instances patch "$INSTANCE_NAME" \
              --activation-policy=ALWAYS \
              --async
          elif [ "$ACTION" = "stop" ]; then
            gcloud sql instances patch "$INSTANCE_NAME" \
              --activation-policy=NEVER \
              --async
          else
            echo "Invalid action: $ACTION"
            exit 1
          fi
        env:
          ACTION: ${{ github.event.inputs.action || 'stop' }}

ワークフローの詳細

トリガー

まずはトリガーにworkflow_dispatchを指定して、手動でワークフローを実行できるようにしている。type: choiceで起動・停止のアクションを選択できるようにしている。

workflow_dispatch

また、cronで夜の21時にワークフローが実行されるようにしている。この場合はworkflow_dispatchで指定できていたinputs.actionが設定できないため、その場合は後のジョブで停止アクションがデフォルトで実行されるようにしている。

認証とgcloudコマンドのセットアップ

CloudSQLインスタンスの起動と停止の実行にはgcloudコマンドを利用する。そのための認証にはgoogle-github-actions/auth、コマンドのセットアップにgoogle-github-actions/setup-gcloudのアクションを利用している。実行するサービスアカウントには管理者ロール(roles/cloudsql.admin)を付与しておく。

認証・認可に利用しているWorkloadIdentityについてはここでは詳細に触れない。公式ドキュメントが充実しているのでそちらを参照してほしい。

CloudSQLインスタンスの起動と停止

最初にインスタンスのステータスを確認し、インスタンスがすでに目的の状態であれば処理をスキップするようにした。というのも、CloudSQLは停止中に停止コマンドを送るとHTTP400エラーになり、ジョブが失敗扱いになってしまう。インスタンスそのものには問題はないが、ジョブが失敗と記録されるのは精神衛生上よくないため、丁寧に処理を制御している。

いよいよインスタンスの起動と停止処理だが、gcloud sql instances patchコマンドを利用する。起動する際は--activation-policy=ALWAYS、停止は--activation-policy=NEVERのオプションを指定することでそれぞれ実行できる。

また、インスタンスの起動や停止が完了するまでには数分〜十数分かかる場合があり、それを待っているとERROR: (gcloud.sql.instances.patch) Operation is taking longer than expected.というエラーが出てしまうことがある。エラーが出ても実際には起動・停止はされるのだが、GitHubActionsの実行時間を無駄に消費してしまう。それを避けるために--asyncオプションを指定して、完了を待たずにジョブを終了するようにしている。

patchコマンドのオプションについて詳細を知りたい場合は以下のドキュメントを参照してほしい。

最後に、ACTION変数に${{ github.event.inputs.action || 'stop' }}を設定することで、手動実行時はinputs.actionを反映し、自動実行(cron)時はstopをデフォルトとして処理を分岐している。

おわり

以上、GitHubActionsを使ったCloudSQLの起動・停止ワークフローの紹介だった。ネットで調べた限りでは、Cloud FunctionsとSchedulerでの実現が多く、自分はGitHubActionsを使いたかったので今回の対応をとった。

どなたかのコストカットの参考になれば嬉しい。

Next

GitHubActionsのmacOSイメージでXcodeのバージョンを指定してビルドする

PR

関連記事