データのエクスポートをスケジュールする

このページでは、Cloud Firestore データのエクスポートをスケジュールする方法について説明します。スケジュールに基づいてエクスポートを実行するには、Cloud Firestore のマネージド エクスポート機能を呼び出す App Engine サービスをデプロイすることをおすすめします。デプロイ後は、App Engine Cron サービスを使用してこのサービスへの呼び出しをスケジュールできます。

始める前に

マネージド エクスポート機能を使用してデータのエクスポートをスケジュールする前に、次のタスクを完了する必要があります。

  1. Google Cloud Platform プロジェクトに対する課金を有効にします。 エクスポートとインポートの機能を使用できるのは、課金が有効になっている GCP プロジェクトのみです。
  2. Cloud Firestore データベースのロケーションの近くにあるロケーションに、プロジェクトの Cloud Storage バケットを作成します。エクスポート / インポート オペレーションには、リクエスト元による支払いバケットは使用できません。
  3. Google Cloud SDK をインストールしてアクセス権限を付与し、アプリケーションをデプロイします。

アクセス権限を構成する

アプリは、App Engine のデフォルトのサービス アカウントを使用して、エクスポートの認証と承認を行います。プロジェクトを作成すると、次の形式でデフォルトのサービス アカウントが作成されます。

YOUR_PROJECT_ID@appspot.gserviceaccount.com

サービス アカウントには、エクスポートを開始する権限と、Cloud Storage バケットに書き込む権限が必要です。これらの権限を付与するには、次の IAM 役割をデフォルトのサービス アカウントに割り当てます。

  • Cloud Datastore Import Export Admin
  • バケットに対する Owner 役割または Storage Admin 役割

Google Cloud SDK の gcloudgsutil コマンドライン ツールを使用してこれらの役割を割り当てることができます。

  1. Cloud Datastore Import Export Admin 役割の割り当て:

    gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
        --member serviceAccount:YOUR_PROJECT_ID@appspot.gserviceaccount.com \
        --role roles/datastore.importExportAdmin
    
  2. バケットに対するストレージ管理者役割の割り当て:

    gsutil iam ch serviceAccount:YOUR_PROJECT_ID@appspot.gserviceaccount.com:storage.admin \
        gs://BUCKET_NAME
    

アプリケーション ファイル

新しいフォルダで、以下のコードを使用して次のアプリケーション ファイルを作成します。

app.yaml
App Engine ランタイムを構成します。このアプリはスタンダード環境の Node.js ランタイムを使用します。
app.js
メインのアプリコードです。このアプリは、エクスポートを開始するウェブサービスを https://YOUR_PROJECT_ID.appspot.com に設定します。
package.json
アプリとその依存関係に関する情報が含まれています。
cron.yaml
ウェブサービスを呼び出す cron ジョブを構成します。

app.yaml

runtime: nodejs8

上記のコードは、このアプリがデフォルト アプリケーションであることを前提としています。デフォルトでない場合は、次の行を追加します。

target: cloud-firestore-admin

app.js

const axios = require('axios');
const dateformat = require('dateformat');
const express = require('express');
const { google } = require('googleapis');

const app = express();

// Trigger a backup
app.get('/cloud-firestore-export', async (req, res) => {
  const auth = await google.auth.getClient({
    scopes: ['https://www.googleapis.com/auth/datastore']
  });

  const accessTokenResponse = await auth.getAccessToken();
  const accessToken = accessTokenResponse.token;

  const headers = {
    'Content-Type': 'application/json',
    Authorization: 'Bearer ' + accessToken
  };

  const outputUriPrefix = req.param('outputUriPrefix');
  if (!(outputUriPrefix && outputUriPrefix.indexOf('gs://') == 0)) {
    res.status(500).send(`Malformed outputUriPrefix: ${outputUriPrefix}`);
  }

  // Construct a backup path folder based on the timestamp
  const timestamp = dateformat(Date.now(), 'yyyy-mm-dd-HH-MM-ss');
  let path = outputUriPrefix;
  if (path.endsWith('/')) {
    path += timestamp;
  } else {
    path += '/' + timestamp;
  }

  const body = {
    outputUriPrefix: path
  };

  // If specified, mark specific collections for backup
  const collectionParam = req.param('collections');
  if (collectionParam) {
    body.collectionIds = collectionParam.split(',');
  }

  const projectId = process.env.GOOGLE_CLOUD_PROJECT;
  const url = `https://firestore.googleapis.com/v1beta1/projects/${projectId}/databases/(default):exportDocuments`;

  try {
    const response = await axios.post(url, body, { headers: headers });
    res
      .status(200)
      .send(response.data)
      .end();
  } catch (e) {
    if (e.response) {
      console.warn(e.response.data);
    }

    res
      .status(500)
      .send('Could not start backup: ' + e)
      .end();
  }
});

// Index page, just to make it easy to see if the app is working.
app.get('/', (req, res) => {
  res
    .status(200)
    .send('[scheduled-backups]: Hello, world!')
    .end();
});

// Start the server
const PORT = process.env.PORT || 6060;
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
  console.log('Press Ctrl+C to quit.');
});

package.json

{
  "name": "solution-scheduled-backups",
  "version": "1.0.0",
  "description": "Scheduled Cloud Firestore backups via AppEngine cron",
  "main": "app.js",
  "engines": {
    "node": "8.x.x"
  },
  "scripts": {
    "deploy": "gcloud app deploy --quiet app.yaml cron.yaml",
    "start": "node app.js"
  },
  "author": "Google, Inc.",
  "license": "Apache-2.0",
  "dependencies": {
    "axios": "^0.18.0",
    "dateformat": "^3.0.3",
    "express": "^4.16.4",
    "googleapis": "^34.0.0"
  },
  "devDependencies": {
    "prettier": "^1.14.3"
  }
}

cron.yaml

cron:
- description: "Daily Cloud Firestore Export"
  url: /cloud-firestore-export?outputUriPrefix=gs://BUCKET_NAME[/PATH]&collections=test1,test2
  target: cloud-firestore-admin
  schedule: every 24 hours

url 行を変更してエクスポート オペレーションを構成します。このアプリは、次の URL パラメータを受け取るサービスを https://YOUR_PROJECT_ID.appspot.com/cloud-firestore-export に設定します。

outputUriPrefix
Cloud Storage バケットのロケーションで、形式は gs://BUCKET_NAME です。
collections
エクスポートするコレクション ID のカンマ区切りのリストです。これを指定しない場合、すべてのコレクションがエクスポートされます。

たとえば、コレクション ID が Songs または Albums のすべてのコレクションをエクスポートするには、次の形式を使用します。

url: /cloud-firestore-export?outputUriPrefix=gs://BUCKET_NAME&collections=Songs,Albums

この cron.yaml の例では 24 時間ごとにエクスポートが実行されます。さまざまなスケジュール オプションについては、スケジュール形式をご覧ください。

アプリと cron ジョブをデプロイする

gcloud を使用してアプリと cron ジョブをデプロイします。

gcloud app deploy app.yaml cron.yaml

cron ジョブをテストする

デプロイした cron ジョブをテストするには、Google API Console の [cron ジョブ] ページでジョブを開始します。

  1. GCP Console で [cron ジョブ] ページを開きます。
    [cron ジョブ] ページを開く

  2. Daily Cloud Firestore Export の記述がある cron ジョブの場合は、[今すぐ実行] をクリックします。

  3. ジョブが完了したら、[ステータス] の下でステータス メッセージを確認します。ジョブログを表示するには、[表示] をクリックします。ステータス メッセージとジョブログには、ジョブの成功または失敗に関する情報が示されます。

エクスポート内容を表示する

エクスポートが完了したら、Cloud Storage バケットでエクスポート内容を表示できます。

API Console で Cloud Storage ブラウザを開きます。
Cloud Storage ブラウザを開く

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。