FirestoreのバックアップをCloudStorageに定期的に行うCloudFunctionsの設定方法

これは[Firebase Advent Calendar 2022](https://qiita.com/advent-calendar/2022/firebase)の1日目の記事です。

こんにちは。[virapture株式会社](https://virapture.com)でCEOしながら[ラグナロク株式会社](https://ragna-rock.com)でもCKOとして働いている[@mogmet](https://twitter.com/mogmet)です。
![CleanShot 2022-12-02 at 22.43.14@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/214a5962-b6ce-c10e-b153-f77b658cb1c5.png)
最近シーズンインしました。これからが楽しみです。


本日は最近バックアップ設定をしてみたら随分と設定が必要になっていたので、皆様が簡単に設定できるように紹介いたします。

以前紹介した勘所についても抑えながら紹介いたします。

https://qiita.com/mogmet/items/6623ae37c3ae2269ec02

公式手順に関しては下記をご参照ください。

https://firebase.google.com/docs/firestore/solutions/schedule-export

## 想定読者 

- とりあえずfirestoreでバックアップをサッととってデータ損失に備えたい

といった方にさっと取得できるように紹介します。

## 手順

バックアップを保存する場所の確保→IAMの設定→CloudFunctionsの作成→動作確認 の順で紹介します。

また、事前に課金プランはBlazeにしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/8fc345d2-e244-0037-4fd9-dbff3f5d7490.png)


### バックアップを保存する場所の確保

バックアップ先としてCloud Storageに保管をします。
それにあたり、新しくバケットを作成します。

Cloud Storageにアクセスして、右の...から「バケットを追加」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/f3177d47-b3bd-3a8f-fc52-a6085347bc37.png)

バケットリファレンスに「PROJECT_ID-firestore-backup」と命名します。
ロケーションはCloudFunctionsが動くリージョンに合わせましょう。ASIA1の方が凄そうですが、いざバックアップを動かすとリージョンが違うと怒られて動かないです。
アクセス頻度はそんなにないので「まれ」か「アーカイブ」を選択しましょう。コストを抑えられます。
設定したら「続行」を押下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/1caa6e23-fce2-fe84-dfba-180fe388d27c.png)


「本番環境モードで開始する」を選択して「作成」を押下。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/a3175f7c-3508-0d3d-a818-71dbaacac14f.png)

このままでは無限にバックアップを取り続けてしまうので、次に保管期限を設定します。
詳細は下記に記載しているので簡易的に紹介します。

https://qiita.com/mogmet/items/6623ae37c3ae2269ec02#expire%E3%81%AE%E8%A8%AD%E5%AE%9A%E6%96%B9%E6%B3%95

`gs-lifecycle.json`として下記を作成します。

```gs-lifecycle.json
{
  "lifecycle": {
    "rule":
    [
      {
        "action": {"type": "Delete"},
        "condition": {"age": 7}
      }
    ]
  }
}
```

あとはターミナルで下記コマンドを実行するだけで7日間の保管期限が設定されます。

```shell
gsutil lifecycle set ./gs-lifecycle.json gs://バックアップ先のバケット名
```

以上でバックアップの保存先は確保完了です。

### IAMの設定

次にバックアップするのに必要な権限をCloudFunctionsを動かすユーザに付与します。

下記にアクセスしてIAMの管理画面を開きます。

https://console.cloud.google.com/iam-admin/iam

`PROJECT_ID@appspot.gserviceaccount.com`のプリンシパルを編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/3995e35c-88ee-2ca3-c306-94fcb5365a4d.png)

「Cloud Storage for Firebase 管理者」と「Cloud Datastore インポート/エクスポート」を追加して、「保存」を押下。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/2e65a061-aae1-c9dd-c676-3d5444138a81.png)

これでIAMの設定は完了です。

### CloudFunctionsの作成

Cloud Functionsは公式が提供してるので丸コピします。

https://firebase.google.com/docs/firestore/solutions/schedule-export#create_a_cloud_function_and_a_job

```js
const functions = require('firebase-functions');
const firestore = require('@google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient();

// Replace BUCKET_NAME
const bucket = 'gs://bkcup-dev-firestore-backup'; // 今回の記事で作ったバケット名を指定します

exports.scheduledFirestoreExport = functions.pubsub
                                            .schedule('every 24 hours')
                                            .onRun((context) => {

  const projectId = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT;
  const databaseName = 
    client.databasePath(projectId, '(default)');

  return client.exportDocuments({
    name: databaseName,
    outputUriPrefix: bucket,
    // Leave collectionIds empty to export all collections
    // or set to a list of collection IDs to export,
    // collectionIds: ['users', 'posts']
    collectionIds: []
    })
  .then(responses => {
    const response = responses[0];
    console.log(`Operation Name: ${response['name']}`);
  })
  .catch(err => {
    console.error(err);
    throw new Error('Export operation failed');
  });
});
```

bucketのところを先ほど作成したバケット名に変えておきましょう。
```js
const bucket = 'gs://bkcup-dev-firestore-backup';
```

scheduleの部分をcron形式にすると実行時間を指定しやすいです。
例えばAM3:00に実行するならこんな感じ。

```js
.schedule('0 3 * * *')
```

準備ができたら上記CloudFunctionsをデプロイします。

### 動作確認

Cloud Schedulerを開きます。

https://console.cloud.google.com/cloudscheduler

対象のプロジェクトを選択すると先ほどデプロイしたCloud Functionsが表示されているので、右の...から「ジョブを強制実行する」を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/d2454906-01a1-b5e5-749b-38b90813eebb.png)

しばらくして、作成したバックアップ用のバケットにフォルダができていれば大成功です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/77823528-b5c7-9ec2-1275-cb63b581929f.png)

もしできていなかった場合はCloudFunctionsのログを開いて原因を追求しましょう。

## まとめ

バケットを作成→IAMを設定→CloudFunctionsをコピペデプロイすることで簡単にバックアップをスケジューリングすることができるようになりました。
これで皆さんも大切なデータを守りましょう！

最後に、[ワンナイト人狼オンライン](https://online.1nite-jinro.com/link/blog)というゲームを作ってます！よかったら遊んでね！

https://online.1nite-jinro.com/link/blog

他にも[Camecon](https://camecon.me/)や[Offcha](https://offcha.com/)、問い合わせ対応が簡単にできる[CSmart](https://csmart.jp/lp)、フリーランスのコミュニティの[NextFreelance](https://next-freelance.net/)といったサービスも作ってるのでよかったら使ってね！

https://camecon.me/

https://offcha.com/

https://csmart.jp/lp

https://next-freelance.net/

また、チームビルディングや技術顧問、Firebaseの設計やアドバイスといったお話も受け付けてますので御用の方は弊社までお問い合わせください。

https://virapture.com/

ラグナロクでもエンジニアやデザイナーのメンバーを募集しています！！楽しくぶち上げたい人はぜひお話ししましょう！！

https://ragna-rock.com