GCP DatastoreのExport機能を自動的に実行する方法

今回はGoogleCloudPlatformのDatastoreの自動バックアップの取り方を紹介します

#前提
- Datastoreにエンティティが存在する 

#手順

##１，Storageでバックアップするためのバケットを作ります


[GoogleCloudPlatformStorage](https://console.cloud.google.com/projectselector2/storage/browser?supportedpurview=project)へアクセスし、プロジェクトを選択します
![スクリーンショット 2019-11-26 9.20.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/567859c5-5275-ccfe-3d9a-b32878b5b45a.png)

[バケット作成]をクリックし、バケットを作成します
<img width="720" alt="スクリーンショット 2019-11-26 14.17.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/fe275b4a-f250-32f3-d723-b6a1b00cfb3c.png">

- バケットに名前を入れる
 - 英数小文字で名前を入れましょう(今回はbackup_qiitaでやります)
- データの保存場所の選択
 - Region:特定の地理的な場所
 - Multi-region:米国などの2つ以上の地理的な場所を含む広い地理的な場所
 - Dual-region:フィンランドやオランダなどペアを示すの特定の地理的な場所
<details><summary>[詳しくはこちら](https://cloud.google.com/storage/docs/locations?_ga=2.5981299.-176719181.1568084299)</summary>
- データのデフォルトのストレージ クラスを選択する
 - Standard:短期間のストレージや頻繁にアクセスされるデータに最適
 - Nearline:アクセス頻度が月1回以下のデータやバックアップに最適
 - Coldline:アクセス頻度が年1回以下のデータやバックアップに最適
- オブジェクトへのアクセスを制御する方法を選択する
 - きめ細かい管理
 - 均一
- 詳細設定（省略可）
 
設定ができたら[作成]を押します
![スクリーンショット 2019-11-26 15.13.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/13a1ce62-5d0d-978b-5583-df8315250de1.png)
こんな感じでバックアップを入れるバケットができました。
では次にバケットにバックアップを入れるための設定を行います


##２，Datastoreのエンティティを受け取り、バックアップを取るソースコードを作成する
ここでは[https://cloud.google.com/datastore/docs/schedule-export?hl=ja](https://cloud.google.com/datastore/docs/schedule-export?hl=ja)を
参考にしながらYaml、Pythonで追加していきます。
まずはYamlで**[app.yaml]**を作成します。（既にある場合はフォルダを作成しその中に作成しましょう）
　　　　　<img width="166" alt="スクリーンショット 2019-11-26 15.33.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/33d19637-5c2a-e193-021d-fc343a278dbf.png">←こんな感じに私はbackupフォルダで置いてます

ここのapp.yamlはPythonの設定をするために用意します。

```yaml:app.yaml
runtime: python27
api_version: 1
threadsafe: true
service: cloud-datastore-admin

libraries:
- name: webapp2
  version: "latest"

handlers:
- url: /cloud-datastore-export
  script: cloud_datastore_admin.app
  login: admin

```

では次にPythonで**[cloud_datastore_admin.py]**も用意しましょう

```python:cloud_datastore_admin.py
import datetime
import httplib
import json
import logging
import webapp2

from google.appengine.api import app_identity
from google.appengine.api import urlfetch

class Export(webapp2.RequestHandler):

  def get(self):
    access_token, _ = app_identity.get_access_token(
        'https://www.googleapis.com/auth/datastore')
    app_id = app_identity.get_application_id()
    timestamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')

    output_url_prefix = self.request.get('output_url_prefix')
    assert output_url_prefix and output_url_prefix.startswith('gs://')
    if '/' not in output_url_prefix[5:]:
      # Only a bucket name has been provided - no prefix or trailing slash
      output_url_prefix += '/' + timestamp
    else:
      output_url_prefix += timestamp

    entity_filter = {
        'kinds': self.request.get_all('kind'),
        'namespace_ids': self.request.get_all('namespace_id')
    }
    request = {
        'project_id': app_id,
        'output_url_prefix': output_url_prefix,
        'entity_filter': entity_filter
    }
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + access_token
    }
    url = 'https://datastore.googleapis.com/v1/projects/%s:export' % app_id
    try:
      result = urlfetch.fetch(
          url=url,
          payload=json.dumps(request),
          method=urlfetch.POST,
          deadline=60,
          headers=headers)
      if result.status_code == httplib.OK:
        logging.info(result.content)
      elif result.status_code >= 500:
        logging.error(result.content)
      else:
        logging.warning(result.content)
      self.response.status_int = result.status_code
    except urlfetch.Error:
      logging.exception('Failed to initiate export.')
      self.response.status_int = httplib.INTERNAL_SERVER_ERROR

app = webapp2.WSGIApplication(
    [
        ('/cloud-datastore-export', Export),
    ], debug=True)
```

このように入力ができたならデプロイを行いましょう

　1.gcloudが適正なプロジェクトに対して構成されるように[config set]を使います

```
gcloud config set project [プロジェクト名]
```
　2.[app.yaml]をプロジェクトにデプロイ ※[app.yaml]が置かれているディレクトリを間違えないように！！

```
gcloud app deploy
```
上手くデプロイできたら自動バックアップの下準備が完了です。
次で自動化させていきます。

##3，cronジョブで自動的に実行するように設定する
ここも[https://cloud.google.com/datastore/docs/schedule-export?hl=ja](https://cloud.google.com/datastore/docs/schedule-export?hl=ja)を参考に進めていきます

まずGCPのschedule-datastore-exportsを呼び出すcronジョブを設定するために
**[cron.yaml]**を作成します。

```yaml:cron.yaml
cron:
- description: "説明文"
  url: /cloud-datastore-export?output_url_prefix=gs://[バケット名]
  target: cloud-datastore-admin
  timezone: Asia/Tokyo
  schedule: every １２ hours


```
これで問題なければデプロイを行いましょう

```
gcloud app deploy cron.yaml
```
####詳しく設定する場合 ~選択範囲~
バックアップする際、特定の種類のエンティティのみをエクスポートする場合はkindを使いましょう

```
url: /cloud-datastore-export?output_url_prefix=gs://[バケット名]&kind=[エンティティの種類の名前]
```
特定のものを複数指定することも可能です

```
url: /cloud-datastore-export?output_url_prefix=gs://[バケット名]&kind=[エンティティの種類の名前]&kind=[エンティティの種類の名前]
```

特定のエンティティに名前空間が存在する場合にエクスポートする場合は

```
url: /cloud-datastore-export?output_url_prefix=gs://[バケット名]&kind=[エンティティの種類の名前]&kind=[エンティティの種類の名前]
```
と入力してあげると細かく設定をしてあげることもできます


####詳しく設定する場合 ~時間設定~
バックアップが実行する時間は**[schedule]**に時間を指定してあげましょう
現在は12時間毎に1回バックアップを実行する設定にしてありますが、1日に1回なら

```
schedule: every 24 hours
```
毎日00：00にバックアップを実行する場合は

```
schedule: every day 00:00
```
など幅広く設定することができます
詳しくは[https://cloud.google.com/appengine/docs/flexible/java/scheduling-jobs-with-cron-yaml?hl=ja#defining_the_cron_job_schedule](https://cloud.google.com/appengine/docs/flexible/java/scheduling-jobs-with-cron-yaml?hl=ja#defining_the_cron_job_schedule)に詳細が載っております
ちなみにバックアップを実行する基準時間を日本時間に設定する場合は

```
timezone: Asia/Tokyo
```
とすれば日本時間で行われます。



##4，cronジョブの実装
####実際に動くかテストしてみよう
AppEngineのcronジョブがこのように表示していればcronが上手くできています

<img width="1301" alt="スクリーンショット 2019-11-27 10.48.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/401523a9-2461-120f-c6c9-b4cb38658177.png">

さてさて[今すぐ実行]で動かしてみましょう
いざ！

ワーオ、エラッター(´ﾟдﾟ｀)
<img width="1062" alt="スクリーンショット 2019-11-27 10.49.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/bd0da4aa-50f5-d332-1ab6-e12d7580bfab.png">
そんなときはログの[表示]でエラーを見ましょう

####ログでエラーを見る
ログを表示してみたところ、403と出ていますね
<img width="1440" alt="スクリーンショット 2019-11-27 10.49.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/9669811d-80ac-50ac-8fb4-147a18dbbde6.png">

開くと"The caller does not have permission"と書かれてますね…
つまりは「インポート、エクスポートにおける権限持ってねーです」ってことですね
<img width="1117" alt="スクリーンショット 2019-11-27 10.50.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/69e3fc05-0190-063c-0d80-1e47f6a27b67.png">

じゃあ権限を与えにいきましょう

####権限を与える
IANと管理でIAMを開きます
<img width="1437" alt="スクリーンショット 2019-11-27 11.14.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/d088160e-0719-fb32-534a-a40eaf0e063f.png">
その中で[プロジェクトID]@appspot.gserviceaccount.comを編集しましょう
<img width="668" alt="スクリーンショット 2019-11-27 11.22.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/73a7e0d0-0420-76c3-0ce4-4b10a0c206c3.png">
[別の役割を追加]を押して
Datastoreの[CloudDatastoreインポート/エクスポート管理者]を選択して保存！
<img width="847" alt="スクリーンショット 2019-11-27 11.22.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/f9bccecc-f05f-6da8-1e68-2607552121e9.png">

ということでもう一度実行しに戻りましょう
<img width="1062" alt="スクリーンショット 2019-11-27 10.49.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/740ddfe1-ea42-9f91-b894-858ae59ed01c.png">
もう一度［今すぐ実行］を押すと…
<img width="1154" alt="スクリーンショット 2019-11-27 11.24.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522196/f7de5730-8a6d-d910-cc91-faca6f635d7f.png">
できました！！

これで12時間毎にデータを取ってくれるようになりました！

##最後に
皆様いかがでしたでしょうか、上手くいきましたか？
私はこの解説が上手くいったかがヒヤヒヤしてます(´ﾟдﾟ｀)

でもこのようにDatastoreのデータをバックアップしておくと
人為的なミスで消したり、何かしらのトラブルにも対応できますので
やってて損はないと思います

では長くなりましたがありがとうございました！！

##参考URL
https://cloud.google.com/storage/docs/locations?_ga=2.5981299.-176719181.1568084299

https://cloud.google.com/datastore/docs/schedule-export?hl=ja

https://cloud.google.com/appengine/docs/flexible/java/scheduling-jobs-with-cron-yaml?hl=ja#defining_the_cron_job_schedule