Posted at

Cloud Functions を使って Firestore の定期バックアップ


Motivation

Firestore のバックアップを Cloud Scheduler と Cloud Functions で完全サーバーレス的に実行したい。 もちろん、既に先人達が実装済み。

https://tech.ginco.io/post/cloud_scheduler/

https://qiita.com/Kasanomo/items/566191f0c106b4669f22

が、サービスアカウントの秘密鍵を使うようなサンプルになっている。

折角 Cloud Functions 自体が既に権限を持っているのだから、別口で秘密鍵をアップロードする事なくできないだろうかと思って調べてみた。


下準備

コード書く前に環境設定


GCSバケット

まずバックアップ先であるバケットを作成する。 ここでは gs://foo とする。 不特定多数からアクセスされる訳じゃないので Regional でいいだろう。 注意すべきなのは、Firebase のプロジェクト作成時に選択したリージョンと同じにしておくこと。 じゃないと、あとでこける。


IAM

Firebase で Cloud Functions 作った場合のデフォルト権限である <PROJECT_NAME>@appspot.gserviceaccount.com は、Project Editor にもかかわらず、実は十分な権限がついてない。 ということで、Cloud Datastore Import Export Admin のロールとマニュアルで付与する。


Cloud Functions で PubSub トリガーを作る

PubSubの backup-firestore トピックへのメッセージのトリガーにする。

実際のバックアップは googleapis パッケージを使って REST API 呼び出し。 ちなみに、なんだかクセの強いライブラリで、使い方、コード追わないとよくわからんかった。

exports.onFirestoreBackup = functions.pubsub

.topic('backup-firestore')
.onPublish(async message => {
const { google } = require('googleapis')
const { GCP_PROJECT } = process.env

const auth = await google.auth.getClient({
scopes: [
'https://www.googleapis.com/auth/datastore',
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/compute'
]
})

await google.firestore('v1').projects.databases.exportDocuments({
name: `projects/${GCP_PROJECT}/databases/${GCP_PROJECT}`,
requestBody: { outputUriPrefix: 'gs://foo' },
auth
})
})

これで、gs://foo 以下に、2019-02-26T10:10:35_84272/ みたいなディレクトリがバックアップの度に生成されるようになる。 ちなみに outputUriPrefix にバケットを指定する際にバケット名の先(gs://foo/bar)まで指定すると、タイムスタンプのディレクトリが作られず、そのディレクトリ直下にファイルが置かれる仕様っぽい。

謎に https://www.googleapis.com/auth/compute を追加してあげる必要があった。なぜかはよくわからない。きっと内部の話なんだろう。


Cloud Scheduler

GCPコンソールからさらっと設定。 任意のタイミングで backup-firestore トピックへメッセージを投げる。 メッセージの中身は読まないのでなんでもいい。 空メッセージは送れなかったような気がしなくもないので {} とかにした。本当に空でもいいのかもしれない。

設定できたら Run Now で手動実行。


結果

普通にできた。Cloud Functions のログを確認。

リストアするには CLI から

gcloud alpha firestore import gs://[BUCKET_NAME]/[EXPORT_PREFIX]/

これで、また一つ秘密鍵の管理が減った。嬉しい。