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
で起動・停止のアクションを選択できるようにしている。
また、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を使いたかったので今回の対応をとった。
どなたかのコストカットの参考になれば嬉しい。