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]/
これで、また一つ秘密鍵の管理が減った。嬉しい。
追記
Cloud Functions にスケジュール実行機能が追加されてるようだ。 ↑の PubSub / Cloud Scheduler を使う必要がなくなって、より簡単に実装できるようになっている。
https://firebase.google.com/docs/functions/schedule-functions?hl=ja