なるべく firebase プロダクト内で完結する Firestore の自動バックアップを目指してみました
役者が揃ってきたこともあり、今ならだいぶ簡潔に実装できます
結論
ADC と functions の pubsub.schedule 機能を使うのがポイントです
import { region } from 'firebase-functions'
import { credential } from 'firebase-admin'
import axios from 'axios' // optional
export const backupFirestoreToStorage = region('asia-northeast1')
.pubsub.schedule('0 3 * * *')
.timeZone('Asia/Tokyo')
.onRun(async () => {
try {
const accessToken = await credential
.applicationDefault()
.getAccessToken()
.then(result => result.access_token)
const projectID = process.env.GCLOUD_PROJECT
const response = await axios.post(
`https://firestore.googleapis.com/v1/projects/${projectID}/databases/(default):exportDocuments`,
{ outputUriPrefix: `gs://${projectID}-firestore-backups` },
{ headers: { Authorization: `Bearer ${accessToken}` } }
)
console.log(response)
} catch (err) {
console.error(err)
}
})
手順
2019年6月時点での方法です
(どうしても GCP の操作が必要になる部分はありますが、その際は gcloud
コマンドは使わずにコンソールで行うようにしています)
課金を有効にする
まずは Blaze プランにして課金を有効にします
バックアップ用の Storage バケットを作成
GCP のコンソールから「Storage」を開き、一意のバケット名を指定して作成します
今回の例では ${projectId}-firestore-backup
としています
Firestore のリージョンと同じリージョンを指定しましょう
参考:https://speakerdeck.com/vexus2/alu-firestore-number-aru?slide=14
サービスアカウントに役割を付与
GCP のコンソールから「IAM と管理」を開き、
[project-id]@appspot.gserviceaccount.com
に以下の2つの役割を付与します
- ストレージのオブジェクト管理者
- Cloud Datastore インポート / エクスポート管理者
functions をデプロイ
$ firebase deploy --only functions:backupFirestoreToStorage
これで終わりです
デプロイの際に Cloud Pub/Sub と Could Scheduler を自動で設定してくれます(便利)
今回は毎日午前3時にバックアップを実行する cron (Cloud Scheduler) を設定しました
動作確認をする場合は Cloud Scheduler を実行すれば OK です
解説
ADC について
Cloud Firestore のバックアップは REST API 経由で実行しているため OAuth2 のアクセストークンが必要です
そのためにサービスアカウントキーの json ファイルを渡したり googleapis ライブラリを使ったりしてトークンを発行するサンプルコードがありましたが、実はそれらを使わなくても実現することができます
ADC (= Application Default Credentials) の詳しい説明は上記の公式ドキュメントに任せますが、
今回の流れで説明すると
- Cloud Functions に設定されているデフォルトのサービスアカウント(
[project-id]@appspot.gserviceaccount.com
)がある - firebase-admin が ADC を使ってそのサービスアカウントを検出している
(今回のサンプルのcredential.applicationDefault()
に対応) - ついでに OAuth2 のアクセストークンを取得 (
getAccessToken()
に対応) - 検出したサービスアカウントに firestore バックアップ用の権限を持たせておいたので API が実行できる
となります
サービスアカウントキーを管理しなくて済みますし、追加のライブラリが必要ないのでよりセキュアな実装といえるのではないでしょうか
他にいい方法があればぜひ教えてくださいm(_ _)m
余談ですが、
[project-id]@appspot.gserviceaccount.com
は IAM 上では App Engine default service account と説明されているのに Cloud Functions のデフォルトのサービスアカウントでもあるのややこしいですよね。。。