CA Beat エンジニアリーダーのヤマサキです。
今回はDatastoreのバックアップとリストアについての解説です。
以前はDatastoreのバックアップと言えば、
ローカルPCからコマンドを叩いてダウンロードして、
リストアする時はコマンドを叩いてローカルからアップロードする、
という方法でした。
現在はApp Engineの管理画面の「Datastore Admin」から
「Google Cloud Storage」に簡単にバックアップを行うことができます。
バックアップデータの情報は「Datastore Admin」の画面に一覧で表示され、
リストアする時もそこから戻したいバックアップデータを選択してリストアする、
という事ができるようになっています。
○Google Coud Storageって?
「Google Cloud Storage」というのはAmazon S3のようなファイル置き場のサービスです。
App Engineに配置したプログラムからS3にアクセスする場合、
httpで通信してAPIを実行することでファイルの配置や取得を行いますが、
「Cloud Storage」の場合、JavaのAPI(「java.nio」、「java.io」)で読み書きできるのはうれしいところです。
以前に紹介したmacheもそうですが、Cloud StorageはApp Engineからファイルを読み書きしやすいため、
今後もApp Engineからのファイル出力はGoogle Cloud Storageに対して出力するものが増えていく事でしょう。
今回説明する「バックアップ」以外にも、
公式の機能でDatastoreのデータをBigQueryにコピーする機能も
Google Cloud Storageにダンプされたバックアップデータを読み込みます。
参考:BigQuery Data Ingestion Best Practices and Cookbook - Google BigQuery — Google Developers
今後は公式の機能でもCloud Storageを経由した連携は多くなりそうなので、
App Engineを学ぶ際に、一緒に覚えておくと良いでしょう。
「バックアップ」機能はCloud StorageだけではなくApp EngineのBlob Storeに保存することもできます。
しかし、最近の流れを鑑みると、Google Cloud Storageへのバックアップをオススメしたいと思います。
○Google Coud Storageの準備
ここでは「bucket」(データを保存するディレクトリのようなもの)の作成と、
指定したApp Engineアプリから書き込めるようにするための権限の設定を行います。
1.gsutilの導入
・ダウンロードする
https://developers.google.com/storage/docs/gsutil_install#install
・ホームディレクトリに展開する
・Pathの設定をする(Mac)
vi ~/.bash_profile
「export PATH=${PATH}:$HOME/gsutil」を追記する。
source ~/.bash_profile
・gsutilとタイプして確認
使い方の説明が表示されればOK
2.bucketの作成と権限設定
・bucket作成
gsutil mb gs://backup-hoge
・bucketの権限設定ファイルを取得する
gsutil getacl gs://backup-hoge > backup-hoge.acl
・bucketの権限設定ファイルを編集して、アプリから操作できるようにする。
以下を追加
<Entry>
<Scope type="UserByEmail">
<EmailAddress>
{AppEngineのアプリケーションID}@appspot.gserviceaccount.com
</EmailAddress>
</Scope>
<Permission>
WRITE
</Permission>
</Entry>
・bucketの権限設定ファイルを更新
gsutil setacl backup-hoge.acl gs://backup-hoge
※Google Cloud Storageは
Webのインターフェースから
・bucketの作成
・ファイルの閲覧
・ファイルのアップロード
・ファイルのダウンロード
・ファイルに対して誰でも見れるように権限を設定する
ができますが、
bucketの権限設定は現状gsutilからしかできないようです。
○Datastore Adminでバックアップ
初めて「Datastore Admin」にアクセスした際はこの画面になっているので、
「Enable Datastore Admin」を押して有効にします。
少しするとこの状態になっています。
この時点ではKindの一覧しか表示されていません。
少し脇にそれますが、
ここでKindを選択して「Delete Entity」を押せば指定したKindのEntityを全て削除することもできます。
以前はこの一括削除も管理画面からできなくて、そのためのプログラムを書いたりしてました。
これも便利になった点ですね。
「Copy to Another App」のボタンで、
指定したKindのEntityをこのアプリから別のアプリにコピーすることができます。
コピーする相手側のアプリに「remote_apiを有効にしたアプリ」をデプロイして、
そのアプリのremote_apiのURLを指定することでアプリ間のデータのコピーができます。
(ただしデータ量が多いと結構お金かかります)
remote_apiはPython版とJava版がありますが、
Java版では使えない機能があるのでPython版を使いましょう。
remote_apiをonにする設定も簡単です。
(このアプリ間でのデータコピーもJava版ではできなかったと思います)
一応後述の「おまけ:Python版の「remote api」の使い方」に
remote_apiをONにしたPython版のプロジェクトのファイルと、使い方を書いておきます。
さて、本題に戻って次が本題のバックアップです。
Kindを選択して「Backup Entities」を押すことで、
選択したKindをCloud Storageにコピーする流れが始まります。
「Google Cloud Storage」を選択し、bucket名を指定して「Backup Entities」を押します。
bucketの権限設定等に問題なく正常に開始するとこの画面になります。
バックアップが完了すると、「Backups」に先ほど行ったバックアップデータが表示されます。
下の「Completed Operations」は「先ほど行ったバックアップが完了した」というログです。
○Datastore Adminでリストア
先ほどの画面で「リストアしたいバックアップデータ」をチェックして「Restore」を押します。
復元したいKindを指定して「Restore」を押します。
正常にリストアが開始しました。
「Completed Operations」にリストアが正常に完了したログが表示されています。
○バックアップデータを別のアプリにリストア
・コピー元アプリで前述の手順でバックアップを行います。
・権限設定をする(コピーしたい時だけ設定すればいいので、こちらの権限設定は簡単に済ませます)
・Google APIs ConsoleのTeamタブで「[コピー先アプリID]@appspot.gserviceaccount.com」を
「Can view」で追加する。(*当初「Can edit」と書きましたが「Can view」で足ります)
・コピー先アプリの管理画面で「Datastore Admin」を開く
・コピー先アプリの管理画面で「Import Backup Information」に
「コピー元アプリのバックアップデータを保存したbucket」のPathを指定してボタンを押す。
・一覧からバックアップしたいデータの「backup_info」を選択して
「Add to Backup List」を押してBackupのリストに追加する。
後は通常のリストアと同じです。
「Datastore Admin」の「Backups」から目的のバックアップを選択してRestoreボタンを押すとリストアが始まります。
・終わったら再びGoogle APIs ConsoleのTeamタブで権限を外しておきましょう。
○データの一貫性に関する注意
運用中のアプリに対してそのままバックアップを行った場合、
一貫性を持ってバックアップ・リストアできるわけではありません。
バックアップ・リストア中にDatastoreへの書き込みがあると、データの整合性が崩れる可能性があります。
この問題を完全に排除したい場合、前述の手順に加え、
管理画面の「Application Settings」で「Disable Datastore Writes」のボタンを押すことで
バックアップ前に書き込みできないようにした上で、
バックアップ完了後にまた書き込み可能に戻す、
という手順が必要になります。
当然「書き込み禁止中」にアプリからDatastoreへの書き込み処理を行うと
ユーザーによる更新は全て失敗します。
(「ApiProxy$CapabilityDisabledException」が発生)
よって、書き込み禁止にしてバックアップ・リストアを行う場合は、
A.プログラム中で「ApiProxy$CapabilityDisabledException」をcatchして
「503 Service Unavailableを返す」、
または「ユーザーが書き込み操作を行った場合に『現在書き込みができません』等のエラーメッセージを表示する
B.システム環境変数等に「メンテナンス中フラグ」を用意して、
それが立っている場合にアプリにAと同様の挙動をさせる等の「メンテナンス状態にする仕組み」が必要です。
Aは障害時等、自分の操作に寄らない理由で発生した場合に対応できるのが利点、
Bは後述しますがリストア時に必要。
→両方あると良いが、最低限ならBのみ対応が必要
リストアする場合も注意点があります。
1.リストア中に書き込み禁止にしないとリストア中に新しいデータが追加されたりデータが更新される可能性がある
2.バックアップ後に更新されたEntityはバックアップデータで上書きされる
3.バックアップ以降に追加されたEntityは削除されずに残る
こちらも一貫性を保証したい場合にはリストア中はDatastoreを書き込み禁止にする必要があります。
運用環境でフルリストアするような状況なら
アプリの不具合で大きなトラブルが起きてデータが大量に壊れたような致命的な事態でしょうから
どのみちユーザーに対して「メンテナンス中状態」にしたいような状況になっているでしょう。
その意味ではリストア時に書き込み禁止にすることは実質(今さら)問題は無いかもしれません。
3については、バックアップ時刻以降に作成されたEntityを全て削除すれば良いはずです。
ただし、Datastoreを「書き込み禁止」にした場合には当然プログラムから削除を行うことはできませんので、
前述の「メンテナンス状態にする仕組み-B」が必要です。
また、「バックアップ開始時刻以降に作成されたEntity」を特定するためには、
全てのEntityについて「作成時刻」のプロパティを用意して作成時刻をセットしておく(かつSingle Property Indexを有効にしておく)必要があるでしょう。
もっとシンプルに「一度全部削除してからリストア」もできるでしょうが、
・インデックスも含めた全データ削除のコスト
・新規Entity登録コスト&インデックスを全て作成し直すコスト
となれば、より多くのお金がかかりそうです。
参考:Backup/Restore, Copy, and Delete Data - Google App Engine — Google Developers
バックアップ・リストアをシステムを停止して行うのであれば、
RDBで言うところの「オフラインバックアップ」に近いイメージですが、
「データをまるごと入れ替える」のではなく
「バックアップした個別のレコード(Entity)を全て上書きする」ような挙動なので、
「バックアップ以降に追加されたEntityは削除されずに残る」→「削除が必要」
の部分が異なります。
データの一貫性を保証した上でRDBの「オンラインバックアップ」に相当する事は現状できないと思われます。
これができたらもう言うこと無いんですが。ω・)
※実はわたくし、幸いというべきか、現状運用環境でフルリストアが必要なほどトラブルを起こした経験はなく、
フルリストアは未経験でございます。
間違い等ありましたらご指摘いただけたら幸いです。m(__)m
また、「こうすればいいよ」というベストプラクティス的なノウハウがあれば是非ご教示いただきたく。(´▽`)
○おまけ:Python版の「remote api」の使い方
Python版の「remote api」を有効にしているアプリケーションです。
(サンプルアプリのremote apiを有効にしただけで、サイトトップはHelloWorldです^-^;)
Python版の[remote-api」をダウンロード
使い方は簡単です。
・GoogleAppEngineLauncherをダウンロードしてインストール
・SDKのダウンロードページを開く
・Mac OS X用 「GoogleAppEngineLauncher-*.*.*.dmg」をダウンロードする。
・dmgの中身をApplicationにコピーする。
(Windowsはインストーラーを実行するだけです)
・デプロイ先のアプリIDを設定する
・「app.yaml」の1行目にアプリIDの定義があるので書き換える。
・Launcherのアプリ一覧でも「Name」が変わっていることを確認する事。
・アプリ間でのデータコピーのための権限設定
「remote-api」ディレクトリ内、「appengine_config.py」の一番下に、「コピー元として許可するアプリ」を定義する項目があります。
↓以下の項目に定義してください。
remoteapi_CUSTOM_ENVIRONMENT_AUTHENTICATION = ('HTTP_X_APPENGINE_INBOUND_APPID', ['アプリID1','アプリID2'])
・GoogleAppEngineLauncherからデプロイする
・GoogleAppEngineLauncherを起動する。
・アプリを登録する。
・「Add Existing Application」→「remote-api」フォルダを選択してアプリを追加する。
・アプリを選択して「Deploy」する
Python版はほとんど触ったことがありませんが、環境構築はJavaより簡単ですね。
※remote apiの実行に権限チェックはあるものの、バージョン名は一応変えておいた方がいいかもしれません。
○まとめ
これでバックアップ・リストアの説明は終了です。
Cloud Storageの権限設定が若干面倒なものの、それでも以前よりは随分簡単になったと思います。
実はCloudStorageではなくBlobstoreに保存することもできます。
BigQueryにデータを持っていく予定の無い人は手っ取り早くBlobStoreも良いかもしれませんが、
今後を考えるとCloud Storageの使い方を覚えておくのは良い事だと思います。
さて、なぜこのタイミングでデータストアのバックアップについて説明したのか、というと、
第一回で触れたような、「料金改定後の便利になった機能」にはできるだけ紹介しておこうというのが一つ。
そしてもう一つは、次回以降に予定している
「自動バックアップ」
「DatastoreのバックアップデータをBigQueryに取り込む機能」(こちらは正式リリース後)
について記事を書くための伏線だったりします。
○次回予告
というわけで、次回は「自動バックアップ」についの記事を書こうと思います。
もちろん記事一回分を使って公式ドキュメント通りにcron設定だけして終わるつもりはなく、
一歩踏み込んでもう少しだけ運用を楽にする方法をご紹介しようと思います。
それでは次回も Let's enjoy App Engine!
★宣伝★
CA BeatではTwitter、Facebookページの運営も行っております!
ブログの更新情報だけでなく、役立つスマホトピックニュースを
選りすぐって配信中♪
ブログ右サイドバーからぜひフォロー、いいね!してくださいねヽ(´▽`)ノ