怠慢プログラマーの備忘録

怠慢でナマケモノなプログラマーの備忘録です。

GAE Cron+Pub/Sub+Cloud FunctionsによるFirestoreのバックアップ定期実行

yutaabe200.hatenablog.com

こちらの記事でバックアップ自体はgcloudコマンドで実行できることがわかりました。

本記事はこれを定期実行する為の手順を説明します。

Google Cloud SDKをインストール・Google Cloud StorageのBucket設定

こちらの記事を参照ください。

yutaabe200.hatenablog.com

Danger!!

$ gcloud config set project [PROJECT_ID]でプロジェクトを設定しておいて下さい。他のプロジェクトに間違ってデプロイしないように。

functions-cronをクローンしてくる

$ git clone https://github.com/firebase/functions-cron

App Engine の設定・Deploy

$ cd appengine/ でappengineディレクトリに移動しておいてください。

cron.yaml

cron:
- description: make a backup for firestore everyday at 00:00
  url: /publish/firebase-backup
  schedule: every 24 hours

に変更する

$ gcloud app deploy app.yaml$ gcloud app deploy cron.yamlを実行。

Updating config [cron]...done.                                                 

Cron jobs have been updated

が出力されたら成功。

f:id:ka0in:20190208105831p:plain

上記画像のようにcronジョブが登録されています。

functionsの設定・Deploy

GoogleAPIsをインストール $ npm install googleapis

request-promiseをインストール $ npm install --save request-promise

index.js

const {google} = require('googleapis')
const rp = require('request-promise')
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()

exports.backupFirestore = functions.pubsub.topic('firebase-backup').onPublish((message, context) => {
  const projectId = '[PROJECT_ID]'
  const getAccessToken = new Promise(function (resolve, reject) {
    const scopes = ['https://www.googleapis.com/auth/datastore', 'https://www.googleapis.com/auth/cloud-platform']
    const key = require(`./${projectId}.json`)
    const jwtClient = new google.auth.JWT(
      key.client_email,
      undefined,
      key.private_key,
      scopes,
      undefined
    )
    const authorization = new Promise(function (resolve, reject) {
      return jwtClient.authorize().then((value) => {
        return resolve(value)
      })
    })
    return authorization.then(function (value) {
      return resolve(value.access_token)
    })
  })
  return getAccessToken.then(function (accessToken) {
    const url = `https://firestore.googleapis.com/v1beta1/projects/${projectId}/databases/(default):exportDocuments`
    return rp.post(url, {
      headers: {
        Authorization: `Bearer ${accessToken}`
      },
      json: true,
      body: {
        outputUriPrefix: `gs://${projectId}-backups-firestore`
      }
    })
  })
})

に変更する。

秘密鍵の発行

f:id:ka0in:20190208110023p:plain

上記画像の通り、IAMの管理 -> サービスアカウントを開きます。

[PROJECT_ID]@appspot.gserviceaccount.comのサービスアカウントに関して以下の2つのアカウントを紐付けます。 - Cloud Datastore Import Export Admin - Storage Object Admin

gcloudコマンドで以下の2つを実行します。

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:storage.admin \
    gs://[BUCKET_NAME]

f:id:ka0in:20190208104820p:plain

上記の写真のように秘密鍵jsonで取得します。

その秘密鍵をPROJECTIDにrenameしてfunctionsディレクトリ内に配置します。

Deploy

$ firebase deploy --only functions --project [PROJECTID] を実行します。

✔ Deploy complete! と表示されれば成功です。

f:id:ka0in:20190208110126p:plain

上記画像の通りfunctionが登録されている状態になります。

テスト実行

f:id:ka0in:20190208111006p:plain

cronジョブの「今すぐ実行」をクリックしてステータスに「成功しました」と表示されれば成功です。

f:id:ka0in:20190208111256p:plain

バケットにデータが追加されている状態が確認できます。

古いバックアップファイルを削除

f:id:ka0in:20190208111835p:plain

バケットロックを選択し、ライフサイクルルールを設定します。

f:id:ka0in:20190208112021p:plain

上記のように条件を30日として、次ページでアクションを「削除」として保存します。

f:id:ka0in:20190208112317p:plain

画像のようにライフサイクルルールが追加されていれば成功です。