Cloud SQLの自動起動停止

# 概要
Cloud SQLは、インスタンスを継続的に実行するとコストが非常に高い。
そのため、開発環境など24時間稼働する必要のないインスタンスは、必要な時以外インスタンスを停止していくことがベストである。
しかし、「起動停止を毎回しないといけない」+「起動に時間がかかる」で効率が悪い。
したがって、以下のGoogleの公式ブログを参考にCloud SQLの起動停止を自動化してみた。

https://cloud.google.com/blog/ja/topics/developers-practitioners/lower-development-costs-schedule-cloud-sql-instances-start-and-stop


# 全体像
スケジューリングの役割をなすのが、**Cloud Scheduler**
Cloud SQLインスタンスを起動停止させるのが、**Cloud Functions**
Cloud SchedulerからCloud Functionsへの通知の役割をするのが、**Cloud Pub/Sub**

![Screen Shot 2021-09-05 at 13.27.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553280/a1da9184-2c0b-9105-a357-64809bcdbfb5.png)

ステップは3つある。

1. Cloud SQLインスタンスの作成（本記事では省略）
1. Cloud SQL インスタンスを起動、停止させる Cloud Functions の関数を作成する
1. Cloud Functions の関数をトリガーする Cloud Scheduler ジョブを作成する


# Cloud Functionsの作成

**まず、**以下のようにCloud Functionsを設定して「次へ」をクリック
![Screen Shot 2021-09-05 at 13.59.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553280/efc9aa5d-857c-8e34-9165-9596a7035ba1.png)


**次に、**以下のようにランタイムとエントリポイントを設定し、function.goにてコードを作成する。
![Screen Shot 2021-09-05 at 13.56.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553280/ebb00e3d-97d3-a48c-71cb-1e1b6b0a047f.png)

```go:function.go
// Package p には、Pub/Sub Cloud Function を含む
package p

import (
	"context"
	"encoding/json"
	"log"

	"golang.org/x/oauth2/google"
	sqladmin "google.golang.org/api/sqladmin/v1beta4"
)

// PubSubMessage は、Pub/Sub eventのペイロード
type PubSubMessage struct {
	Data []byte `json:"data"`
}

type MessagePayload struct {
	Instance string // Cloud SQL のインスタンス名
	Project  string // プロジェクトID
	Action   string // 起動か停止か
}

// Pub/Sub message を受け取って処理
func ProcessPubSub(ctx context.Context, m PubSubMessage) error {
	var psData MessagePayload
    // PubSubMessageのjsonデータをMessagePayloadオブジェクトに変換する
	err := json.Unmarshal(m.Data, &psData)
	if err != nil {
		log.Println(err)
	}
	log.Printf("Request received for Cloud SQL instance %s action: %s, %s", psData.Action, psData.Instance, psData.Project)

    // デフォルトの認証情報を使って、http.Cllientを作成
	hc, err := google.DefaultClient(ctx, sqladmin.CloudPlatformScope)
	if err != nil {
		return err
	}

	// Cloud SQL service を作成
	service, err := sqladmin.New(hc)
	if err != nil {
		return err
	}

	// 起動と停止のアクションのリクエストを読み取る
	action := "UNDEFINED"
	switch psData.Action {
	case "start":
		action = "ALWAYS"
	case "stop":
		action = "NEVER"
	default:
		log.Fatal("No valid action provided.")
	}

	// 読み取ったアクションをCloud SQLに設定する準備をする
	rb := &sqladmin.DatabaseInstance{
		Settings: &sqladmin.Settings{
			ActivationPolicy: action,
		},
	}
    // CloudSQLインスタンス内のデータベースに関する情報を含むリソースを部分的に更新
    resp, err := service.Instances.Patch(psData.Project, psData.Instance, rb).Context(ctx).Do()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("%#v\n", resp)
	return nil
} 
```

**最後に、**Cloud Functions の関数に Cloud SQL インスタンスを起動または停止する権限を付与します。
Cloud Console の IAM セクションに移動し、Cloud Functions で使用される「App Engine デフォルト サービス アカウント」を見つけます。このサービス アカウントのサフィックスは「@appspot.gserviceaccount.com」です。
 そして、[Cloud SQL 管理者] ロールを追加します。



# Cloud Scheduler ジョブの作成
以下のようにCloud Scheduler を設定し、「作成」をクリック。
![Screen Shot 2021-10-26 at 8.45.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553280/3b173da0-44d5-5687-c768-214a5096e6a3.png)


ジョブの「頻度」は、「平日の朝9時」に実行する記述である。
Pub/Subの「メッセージ本文」は、Cloud Functionsの関数に必要な情報を記述する。

同様に、停止のジョブも作成する。
※停止の場合は、Pub/Subのメッセージ本文の"Action"をstopに変更すること。

# 参照
https://cloud.google.com/blog/ja/topics/developers-practitioners/lower-development-costs-schedule-cloud-sql-instances-start-and-stop