GAEでプロジェクト間 or ローカルにDatastoreのデータをコピーする
概要
開発してると本番からstaging, dev, localに対してデータをコピーしてきたいことがあると思います。
今回はGoogle App Engine上でDatastoreを使ってる時に、そんな場面に出くわしたらどうすべきか記載します。
なぜデータをコピーする必要があるか
モチベーションは一つで、「ユーザーが実際に見る画面とほぼ同一の画面を再現するために、材料となるデータが必要だから」です。
以下の記事に自分の思っていることが全て書いてあります。
こんにちは。技術部の吉川です。 今回はクックパッドの開発環境構成、特に開発用データベースの構成についてご紹介します。 クックパッドのシステム環境は以下のようなフェイズに分かれています。 ※ これはcookpad.comの構成で、サブシス…techlife.cookpad.com
ほんの些細な見た目の違いで、「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_data
と upload_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か、ツイッターアカウントにお問い合わせください。