🔥
Firestore のデータをデイリーでバックアップする
経緯
いつのまにか 公式ドキュメント に手順が明記されていたので、こちらを試したときのメモです。基本的に公式の通りにやれば何の問題もありませんが、 Functions のコードだけは ESM 形式にしたり、 async/await に書き換えたり一部モダンにしています。
手順
1. バックアップ保存先となるバケットの作成および権限の変更
バケット作成時にリージョンと保護期間を指定しています。こちらは ドキュメント を参考にお好みで変更してください。 また、 PROJECT_ID
や BUCKET_NAME
は環境に合わせて変更してください。
# プロジェクト ID を確認
$ gcloud projects list
# ターゲットのプロジェクトをデフォルトプロジェクトに指定する
$ gcloud config set project PROJECT_ID
# バケットを作成 (リージョンと保持期間を指定してます)
$ gsutil mb -l asia-northeast1 --retention 30d gs://BUCKET_NAME
# バケット一覧を表示し、作成されていることを確認する
$ gsutil ls
gs://BUCKET_NAME/
# Cloud Datastore インポート / エクスポート管理者ロールを割り当てる
$ gcloud projects add-iam-policy-binding PROJECT_ID \
--member serviceAccount:PROJECT_ID@appspot.gserviceaccount.com \
--role roles/datastore.importExportAdmin
# バケットに対するストレージ管理者ロールを割り当てる
$ gsutil iam ch serviceAccount:PROJECT_ID@appspot.gserviceaccount.com:admin gs://BUCKET_NAME
2. Functions にコードを追加する
公式のコードから以下の変更を加えています。
- ESM 形式に変更
- async/await を使用
- バックアップスケジュールを日本時間の深夜 0 時に
import { v1 } from '@google-cloud/firestore';
export const backupFirestoreDaily = functions
.region('asia-northeast1')
.pubsub.schedule('0 0 * * *')
.timeZone('Asia/Tokyo')
.onRun(async () => {
const projectId = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT;
if (projectId === undefined) {
throw new Error('Project ID is not specified.');
}
const firestoreAdminClient = new v1.FirestoreAdminClient();
const databaseName = firestoreAdminClient.databasePath(projectId, '(default)');
try {
const responses = await firestoreAdminClient.exportDocuments({
name: databaseName,
outputUriPrefix: 'gs://BUCKET_NAME',
collectionIds: [], // all collections
});
const response = responses[0];
if (response.name !== undefined) {
logBackupSucceeded(response.name);
}
} catch (error) {
if (error instanceof Error) {
logBackupError(error.message);
}
throw new Error('Export operation failed');
}
});
おわりに
これで夜中の 12 時頃に全コレクションのバックアップが走るので、 念の為 Functions のログやバケットの中身を確認してみるのが良いと思います。
Discussion