今回の記事では GCPの主要プロダクトであるFirestore(Datastore モード)の日次Backup方法とBigQueryへのインポート方法について、GCE、GAE cronを用いて行う方法を紹介いたします。
目次
Firestoreとは
GCPが提供するフルマネジドなNoSQLサービスです。
FirestoreにはFirebase Realtime Databaseの後継であるFirestore(ネイティブ モード)とCloud Datastoreの後継であるFirestore(Datastore モード)の二種類のモードが存在しますが、今回はFirestore(Datastore モード)について扱っていきます。
以下の記事内では混乱を避けるため、便宜上Firestore(Datastore モード)のことをDatastoreと呼ぶことにします。
Datastoreの作成方法
Firestoreの利用開始時に下記のようにどちらを作成するか、選択できるようになっています。
作成時にはDatastoreのリージョンを選択します。
2019/5/8時点では、App Engineを作成すると同じリージョンで旧Datastoreが裏側で作成されますが、その場合でもDatastoreのコンソールより、FirestoreにUpgradeすることが可能です。ただし、Datastoreにまだ一度もデータを挿入していないことが条件となります。
DatastoreのExport機能について
DatastoreにはEntityのExport/Import機能が用意されています。
今回は既に用意されているこの機能を使用し、Datastoreの日次Backupを取得していきます。
GCS Bucket作成
DatastoreのEntityをExportする為のGCSのBucketを作成します。
BucketのリージョンはDatastore作成時に決定したリージョンと合わせる必要があります。
ナビゲーションメニューより、Storageを開き、「バケットを作成」をクリックします。
任意のBucket名を付け、場所はDatastoreのリージョンと合わせるようにし、作成をクリックします。
Backupジョブ作成
Backupを行うジョブを作成していきます。
今回紹介するのは下記の二種類です。
- GCE + cron(GCEインスタンスのcronでgcloudコマンドを実行)
- GAE cron(GAE cronで用意したエンドポイントを実行)
GCE + cron
サービスアカウントの準備
DatastoreのEntityをGCSへExportする必要がある為、下記の2つの権限を付けたサービスアカウントを用意します。
- Datastoreインポート/エクスポート管理者
- GCSオブジェクト作成者
インスタンス作成
GCEではデフォルトのサービスアカウントを指定できるので、前の手順で作成したサービスアカウントを指定します。
GCPにはAlways Free プロダクトというものがあり、制限内であればトライアル期間等関係なく、無料で利用できるサービスというものが存在します。
GCEでは特定リージョンのf1-micro
というマシンタイプが無料で使用できます。
GCS Bucketについては、Datastoreと同リージョンにしなければならない制限がありましたが、GCEで行うのはgcloudコマンドの定期実行だけなので、GCEインスタンス自身のリージョンについてはDatastoreと合わせる必要はありません。
今回は下記のように作成します。
cronの設定
cronの設定を行う前に、デフォルトのGCEインスタンスのTimeZoneを変更しておきます。
デフォルトはUTCになっており、そのままでも問題なくcronは動きますが、設定ミスを防ぐ為にJSTに変更しておくのがよいかと思います。
設定方法については下記が参考になります。
GCEちょい技 ~システム言語、タイムゾーンの変更方法~ メインOS全まとめ
下記cronの設定です。
0 0 * * * gcloud datastore export --kinds="KIND1,KIND2" --namespaces="(default)" gs://${Bucket}
下記のようにKindを指定しないExportもできますが、後述するBigQueryへのインポートにはKind毎にExportしておく必要があります。
0 0 * * * gcloud datastore export --namespaces="(default)" gs://${Bucket}
スケジュール後に指定したGCSのBucketにBackupがExportされていればOKです。
GAE cron
GAE cronを利用する場合は、gcloudコマンドを直接実行する術が無い為、Export用のエンドポイントを実装してやる必要があります。
GAEは複数の言語に対応していますが、最近個人的にGo言語をよく触っているので、こちらを使用してやってみたいと思います。
エンドポイント内でHTTP Clientを使用し、DatastoreのExport用のAPIにRequestを投げる形で実装をします。
GAE デフォルトサービスアカウントの役割追加
GAE cronの実行はGAEのデフォルトのサービスアカウントで行われます。
その為、GAE cronでExportを実行する際には、GCEで設定したサービスアカウントと同様権限を追加してやる必要があります。
GAEのデフォルトサービスアカウントは${PROJECT_ID}@appspot.gserviceaccount.com
なので、このアカウントに下記の権限を追加します。
- Datastoreインポート/エクスポート管理者
- GCSオブジェクト作成者
プロジェクト作成
まずは個人的な備忘録も兼ねて、GO1.11からのModulesに慣れていないのでプロジェクト作成からやります。
# プロジェクト作成
mkdir gae-go-datastore-backup-example
cd gae-go-datastore-backup-example
# GO Modulesを有効にする
export GO111MODULE=on
# GO Modules初期化
go mod init github.com/cloud-ace/gae-go-datastore-backup-example
プロジェクトを作成したら、GAEに必要なファイルを配置していきます。
雛形としてGAE GoのQuickStartで使用されているサンプルコードを利用します。
エンドポイント実装
今回はmain.goに下記のように実装をしました。
GCPの認証についてはこちらのパッケージを利用しています。
Backup先のGCS Bucketの指定、Kindの指定はQueryパラメータから受け取るようにしています。
main.go
~~下記一部抜粋~~
func exportHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ts, err := google.DefaultTokenSource(ctx,
"https://www.googleapis.com/auth/datastore")
if err != nil {
log.Printf("Get token failed. err: %v", err)
}
client := oauth2.NewClient(ctx, ts)
q := r.URL.Query()
type M map[string]interface{}
type I []interface{}
kindI := I{}
for _, k := range q["kind"] {
kindI = append(kindI, k)
}
log.Println(q["outputUrlPrefix"])
reqBody := M{
"outputUrlPrefix": q["outputUrlPrefix"][0],
"entityFilter": M{
"kinds": kindI,
}}
b, err := json.Marshal(reqBody)
if err != nil {
log.Printf("Marshal request body failed. err: %v", err)
return
}
req, err := http.NewRequest(
"POST",
"https://datastore.googleapis.com/v1/projects/{YOUR_PROJECT_ID}:export",
bytes.NewBuffer(b))
req.Header.Set("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
log.Printf("Request failed. err: %#v", err)
return
}
if res.StatusCode != http.StatusOK {
respb, _ := ioutil.ReadAll(res.Body)
log.Printf("Request is not OK. ReponseBody: %s", string(respb))
return
}
log.Printf("Request success. Response: %v", res)
return
}
余談ですが、GAE Go1.11からコードのGAE依存が少なくなりいい感じです!
cronの設定
GAE cronの設定はcron.yamlという設定ファイルを使用して行います。
ジョブの実行日時とTimeZoneを指定しています。
cron.yaml
cron:
- description: "Daily Cloud Datastore Export"
url: /cloudDatastoreExport?outputUrlPrefix=gs://${Bucket}&kind=KIND1&kind=KIND2
schedule: every day 00:00
timezone: Asia/Tokyo
スケジュール後に指定したGCSのBucketにBackupがExportされていればOKです。
cronジョブのデプロイ
cronジョブのデプロイはcron.yaml
を指定してデプロイします。
gcloud app deploy cron.yaml
cronジョブの確認
またGAEにデプロイされているcronジョブは、GCPコンソール上から確認ができます。
ナビゲーションメニューの「App Engine」 > 「cronジョブ」 に現状のcronジョブ一覧が表示されます。
ジョブの一覧が確認できるだけではなく、スケジューリングされた日時以外にもcronジョブを手動実行することも可能です。
Backupのローテーション
上記までで、DatastoreのEntityのBackupはされるようになりましたが、このままではGCS内に延々とBackupファイルが増え続けていってしまいます。
不要なBackupファイルでGCS料金が嵩んでしまってはもったいないので、必要な分だけBackupファイルを取っておくように設定してみましょう。
ここではGCS Bucketのライフサイクル機能を使用します。
ナビゲーションメニューから「Storage」をクリックし、設定を行いたいBucket配下まで移動し、「バケットロック」タブから、「ライフサイクル ルールを追加」をクリックします。
オブジェクト条件の選択で「年齢」をチェックし、任意の日数を設定、アクションを選択するで「削除」をチェックし、ルールを保存します。
Bucketを一覧から確認し、「ライフサイクル列」が有効になっていることを確認します。
ライフサイクル設定後は、設定した日数以前のBackupファイルが消えていることを確認してみてください。
BigQueryへのインポート
DatastoreのデータをSQLを使って柔軟に解析をしたい等の用途でBigQueryにDatastoreBackupのインポートができます。
BigQueryはデフォルトでDatastoreのExportデータに対応しており、そのままインポートすることが可能です。
ただし、DatastoreのBackupはKind毎に別れている必要があります。
BigQueryへのインポートは下記の手順で行えます。
ナビゲーションメニューより、「BigQuery」をクリックして、BigQueryUIまで移動します。
左メニューからプロジェクト名を選択し、「データセットを作成」をクリックします。
任意のID、ロケーションを選択し、データセットを作成します。
作成したデータセットを選択し、「テーブルを作成」をクリックします。
テーブルの作成元に「Google Cloud Storage」を、GCSバケットからのファイルには「~kind{KIND}.export_metadata」というファイルを、ファイル形式には「Cloud Datastore バックアップ」を指定し、テーブルを作成します。
指定したデータセット配下にテーブルが作成され、データがインポートされていればOKです。
まとめ
いかがでしたでしょうか。
ソースコードは 弊社のGitHubに置いてありますので、ご参考にして頂ければ幸いです。
ご意見・ご感想お待ちしております。