Cloud Functions + Cloud Scheduler で Cloud SQL のデータを定期削除する
はじめに
GCP の Cloud SQL にはストレージの自動増量機能があるため、ストレージ不足を気にしなくて良いのがとても便利です。一方で、一旦ストレージが増量されると減らすことができないので、コストが気になります。そこで、古いデータを自動で定期削除する方法を試してみたので、共有したいと思います。
構成
今回は以下のような仕組みで、データの定期削除を行います。
- Cloud SQL のインスタンスに対してクエリを実行する関数を Cloud Functions に作成する
- ソースコードを GitHub のリポジトリから取得して Cloud Functions にデプロイするように Cloud Build を構成し、関数を作成・更新する
- 定期的に関数を実行するように Cloud Scheduler を設定する
設定ファイル等のディレクトリ構成は以下のようにしました。Cloud Build のトリガー作成と Cloud Scheduler のジョブ作成については、Terraform でコード化する方法も併せて紹介します。
.
├── cloudBuild
│ └── cloudbuild.yaml
├── cloudFunctions
│ └── mysql
│ ├── mysql_execute_query.js
│ ├── mysql_tbl_delete.sql
│ └── package.json
└── infra
├── cloud_build.tf
├── cloud_scheduler.tf
├── cloud_sql.tf
├── main.tf
└── variables.tf
Cloud Functions で実行する処理を作成する
まずは、Cloud Functions で実行する処理を作成します。利用できる言語は、Node.js、Python、Go、Java、.NET、Ruby、PHPから選べますが、今回は Node.js 16 で書きました。クエリが書かれているSQLファイルを読み込んだ後、Cloud SQLインスタンスに接続し、クエリを実行するという流れになっています。
const mysql = require('mysql');
exports.executeQuery = (req, res) => {
const connection = mysql.createConnection({
socketPath: "/cloudsql/" + process.env.connectionName, // SQLインスタンス接続名
user: process.env.dbUser, // ログインユーザ名
password: process.env.dbPassword, // ログインパスワード
database: process.env.dbName, // データベース名
multipleStatements: true // 複数クエリの実行を許可する
});
// SQL文の読み込み
const fs = require('fs');
let sql = fs.readFileSync(process.env.sqlFileName, 'utf-8');
// Cloud SQLに接続
connection.connect();
// クエリ実行
connection.query(sql, (err, results) => {
if (err) {
console.error(err);
res.status(500).send(err);
} else {
console.log(JSON.stringify(results))
res.send(JSON.stringify(results));
}
});
// 接続終了
connection.end();
};
{
"version": "0.0.3",
"dependencies": {
"mysql": "latest"
}
}
# テーブルから3ヶ月より前のデータを削除する
DELETE FROM TEST_TABLE WHERE DELI_DTE < (CURRENT_DATE() - INTERVAL 3 MONTH);
Cloud Build 構成ファイルを作成する
ソースコードを作成したら、次にビルド構成ファイルを作成します。公式ドキュメントに分かりやすい解説がのっていますので、参考にして書いていきます。
構成ファイルには、関数名やソースコードに関する情報、環境変数、何をトリガーにするかなどを設定します。今回は Cloud Scheduler で関数を実行させるため、HTTPトリガーを選択しています。
steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
args:
- gcloud
- functions
- deploy
- mysql-tbl-delete
- --region=<region>
# HTTPトリガー
- --trigger-http
# 関数のソースコードのパス
- --source=./cloudFunctions/mysql
# 関数で使用される言語ランタイム
- --runtime=nodejs16
# エントリーポイント
- --entry-point=executeQuery
# 環境変数
- '--set-env-vars'
- connectionName=<projectID>:<region>:<sqlInstanceName>
- '--set-env-vars'
- dbUser=testuser
- '--set-env-vars'
- dbName=testdb
- '--set-env-vars'
- sqlFileName=mysql_tbl_delete.sql
# Secret Manager から取得する環境変数
- 'set-secrets'
- 'dbPassword=testdb_password:1'
Terraform 設定ファイルを作成する
関数のソースコードとビルド構成ファイルが準備できたので、次に、Cloud Build と Cloud Scheduler の設定ファイルを作成します。
Cloud Build のトリガー作成
Terraform の公式ドキュメントを参考に作成します。
権限の設定では、Cloud Build から Cloud Functions にデプロイすることを許可するため、Cloud Functions 開発者のロールを有効化します。トリガー設定では、ビルド構成ファイルを指定したり、トリガーイベントを設定します。以下では、「GitHubの指定リポジトリの指定ブランチにpushする」をトリガーイベントにしていますが、プルリクエストをトリガーにしたり、自動でデプロイされると困る場合は、手動呼び出しに設定することも可能です。
// 権限の設定
resource "google_project_iam_member" "cloudbuild_iam" {
project = var.project
for_each = toset([
"roles/cloudfunctions.developer",
"roles/iam.serviceAccountUser"
])
role = each.key
member = "serviceAccount:${var.project}@cloudbuild.gserviceaccount.com"
}
// トリガー(MySQLの定期削除)
resource "google_cloudbuild_trigger" "mysql-tbl-delete" {
location = var.region
name = "mysql-tbl-delete"
// Cloud Build構成ファイル
filename = "cloudBuild/cloudbuild.yaml"
// トリガーイベント
github {
owner = "ownerName"
name = "repositoryName"
// 指定ブランチにpushをトリガーにする
push {
branch = "^${var.github_branch}$"
}
}
// フィルタ
included_files = ["cloudFunctions/mysql/**"]
}
Cloud Scheduler のジョブ作成
こちらも Terraform の公式ドキュメントを参考に作成します。Cloud Scheduler のジョブの作成はとてもシンプルで、実行スケジュールとターゲットとなる Cloud Functions のトリガーURL を設定するだけです。
resource "google_cloud_scheduler_job" "mysql-tbl-delete" {
name = "mysql-tbl-delete"
schedule = "15 1 1 * *"
time_zone = "Asia/Tokyo"
# HTTPターゲット
http_target {
http_method = "GET"
uri = "https://${var.region}-${var.project}.cloudfunctions.net/mysql-tbl-delete"
}
}
まとめ
今回は、Cloud SQL のデータを定期削除する方法を試してみましたが、意外と簡単に設定できることが分かりました!
Discussion