GAEでプロジェクト間 or ローカルにDatastoreのデータをコピーする

timakin
7 min readOct 4, 2017

--

概要

開発してると本番からstaging, dev, localに対してデータをコピーしてきたいことがあると思います。

今回はGoogle App Engine上でDatastoreを使ってる時に、そんな場面に出くわしたらどうすべきか記載します。

なぜデータをコピーする必要があるか

モチベーションは一つで、「ユーザーが実際に見る画面とほぼ同一の画面を再現するために、材料となるデータが必要だから」です。

以下の記事に自分の思っていることが全て書いてあります。

ほんの些細な見た目の違いで、「UI良さげだな〜」という思い込みが崩れることがあります。

エンジニアは、最適なユーザー体験を実現するべく、その錯覚をローカル開発の時点から目指すべきです。

以前、自分でGo製のDBレプリケーションツールを作成して、この問題を解決しました。

ですが、最近AppEngineを触ってる自分からすると、果たしてどうやればデータをコピーできるのか、ましてやDatastoreともなるとMySQLとかと違ってインターフェースが違うのでは、困る!と思っていました。

しかし、AppEngineでDatastoreを使っている場合、これは非常に簡単に解消可能な問題でした。

事前準備

Remote APIの有効化

事前準備として、AppEngineのRemote APIというものに対して、アクセスする窓口を作る必要があります。API経由でDatastoreを叩くためです。

今回はGoのAPIを事例に設定して行きます。

まず、それぞれのprojectのyamlファイルに以下のように追記します。

handlers:
- url: /_ah/remote_api
script: _go_app

次に、サービスの起動スクリプト(今回ならmain.go)に、次のパッケージimportを追記します。

_ "google.golang.org/appengine/remote_api"

こうすることで、GAEがよしなにRemote APIへのアクセスを有効化してくれます。

Service Account

APIにアクセスする際のCredentialとして、サービスアカウントの発行を行います。

Google Cloud Platformのコンソール上で、IAMと管理者 から、 サービスアカウント を選びます。

サービスアカウントを作成 ボタンを押して、Datastoreに関するroleを選択(僕は面倒なのでAppEngineのオーナーキーをそのまま使っちゃいましたが)し、keyとなるjsonを発行します。

それを任意のPATHに保存し PROD_KEY_PATH とか DEV_KEY_PATH とか、よしなな環境変数を設定しておくと、あとで便利です。

データコピー

では、窓口ができたところで実際のデータコピーです。さくっとスクリプトを書いておきます。

Production -> Staging/Dev

次のスクリプトが、本番から別の、stagingやdevに対してデータコピーするものです。今回はdevに対して行います。

rm -f bulkloader-*	
rm -f /tmp/dump.datastore
GOOGLE_APPLICATION_CREDENTIALS=${PROD_KEY_PATH} appcfg.py download_data --application=${PROD_PROJECT_ID} --url="https://${PROD_PROJECT_ID}.appspot.com/_ah/remote_api" --filename=/tmp/dump.datastore
GOOGLE_APPLICATION_CREDENTIALS=${DEV_KEY_PATH} appcfg.py upload_data --application=${DEV_PROJECT_ID} --url="https://${DEV_PROJECT_ID}.appspot.com/_ah/remote_api" --filename=/tmp/dump.datastore

ざっくり解説すると、まず毎回Datastoreからbulk loadしてきたログと、dumpしたデータが吐き出されますので、実行前にもし残ってれば削除します。

次に、 GOOGLE_APPLICATION_CREDENTIALS という定数にそれぞれのAPIのkeyへのPATHを設定し、コマンドを叩きます。

一目瞭然、remote_apiにアクセスしていますが、重要なのは download_dataupload_data です。

雑に/tmp/dump.datastore (こちらも任意に変更してください) にdumpしたデータを、別環境に書き出す、という処理をしています。

Production -> Local

次に、ローカルへのコピーです。

まず、コピーの前にAppEngineのdev_serverを起動しておきます。その後次のようなコマンドを打ちます。

rm -f bulkloader-*	
rm -f ~/.appcfg_oauth2_tokens
rm -f /tmp/dump.datastore
GOOGLE_APPLICATION_CREDENTIALS=${PROD_KEY_PATH} appcfg.py download_data --application=${PROD_PROJECT_ID} --url="https://${PROD_PROJECT_ID}.appspot.com/_ah/remote_api" --filename=/tmp/dump.datastore
appcfg.py upload_data --application=dev~${LOCAL_PROJECT_ID} --url="http://localhost:${PORT}" --filename=/tmp/dump.datastore

Localにコピーしてくるときだけ、認証エラーが出るので、rm -f ~/.appcfg_oauth2_tokens というのを新たに追記しています。

また、upload_dataするときにクレデンシャルが不要で、GOOGLE_APPLICATION_CREDENTIALS を特に指定していません。

以上で、リモートのプロジェクト間ないしローカルへのDatastoreのデータコピーが行えました。

まとめ

通常のRDBMSのように特に困ることなく、数行のスクリプトでデータをコピーすることができます。

注意としては、コピーする前にコピー先の環境のデータを消しておかないと、データが2重になるので、雑にコピーしてくるときだけ上記のような手段をとってください。

ユーザーに適切な価値を提供できるか、本番にならないとわからない!データ起因でdevだけうまく挙動してしまう!というような事態を未然に防ぎ、効率的な開発をやっていきましょう!

もし何か不明点があれば、timaki.st@gmail.comか、ツイッターアカウントにお問い合わせください。

--

--