10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

docker-compose で Cloud Datastore Emulatorを立ち上げpythonアプリと連携する

Posted at

背景

サービスでdatastoreを使う事になり、開発環境やCIでのテストをどうするか試行錯誤した。 :thinking:
AWSと比較して情報が少ないので、まとめた。 :grinning:

要点

  • flaskアプリとCloud Datastore Emulatorをコンテナを作る
  • docker-composeでサービス連携を行う
  • datastoreの初期データ投入
  • flaskアプリから、Cloud Datastore Emulatorを操作する

ディレクトリ構成

.
├── app
│   ├── Dockerfile
│   └── src
│       ├── main.py
│       └── requirements.txt
├── datastore
│   ├── Dockerfile
│   ├── entrypoint
│   └── import
│       ├── 2020-01-21.overall_export_metadata
│       ├── default_namespace
│       │   └── kind_test_data
│       │       ├── default_namespace_kind_test_data.export_metadata
│       │       └── output-0
│       └── run.sh
└──  docker-compose.yaml

Cloud Datastore Emulator コンテナ構築

datastore/Dockerfile

公式のSDKイメージから最小限で作成。
エミュレータ起動用shell、データ投入用shellに実行権限付与。

FROM google/cloud-sdk:alpine

RUN apk add --update --no-cache openjdk8-jre \
  && gcloud components install cloud-datastore-emulator beta --quiet

COPY . /datastore/

WORKDIR /datastore

RUN chmod +x ./entrypoint
RUN chmod +x ./import/run.sh

ENTRYPOINT ["./entrypoint"]

datastore/entrypoint

docker-compose downした際にも、データを維持するために、/datastore/.data/ディレクトリにデータをためる。

オプションなしで起動すると、コンテナ内でしかアクセスできないので、--host-port=0.0.0.0:8081 として起動する。
環境変数から、プロジェクト名と一緒に流し込む。

#!/usr/bin/env bash

gcloud config set project ${DATASTORE_PROJECT_ID}

gcloud beta emulators datastore start \
  --data-dir=/datastore/.data \
  --host-port=${DATASTORE_LISTEN_ADDRESS}

データ投入のdatastore/import/run.sh

サーバー起動後、下記のエンドポイントにdump済みのデータの保存パスを投げるとデータがインポートできる。

export DATASTORE_PROJECT_ID

curl -X POST localhost:8081/v1/projects/${DATASTORE_PROJECT_ID}:import \
    -H 'Content-Type: application/json' \
    -d '{"input_url":"/datastore/import/2020-01-21.overall_export_metadata"}'

datastore/importのメタデータ

今回は、gcp のコンソールから、gcsにダンプしたデータをディレクトリ丸ごとdatastore/import配下に持ってきた。
sdkデータを直接生成してもよい。

python アプリ コンテナ構築

サンプル用にペラペラのアプリ構築する。

app/Dockerfile

FROM python:3.7-slim-buster

ENV HOME /api/

ADD ./ ${HOME}

WORKDIR ${HOME}

RUN pip install --upgrade pip \
    && pip install --no-cache-dir -r ${HOME}src/requirements.txt

ENTRYPOINT ["python", "src/main.py"]

app/main.py

ちょっと適当すぎる気がするが、データを保存して取り出すだけのエンドポイントを作成。 :thinking:

認証情報は、ダミーの認証をかませる。

from flask import Flask, jsonify
from google.auth.credentials import AnonymousCredentials
from google.cloud import datastore
from os import getenv

client = datastore.Client(
    credentials=AnonymousCredentials(),
    project=getenv('PROJECT_ID')
)

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False


@app.route('/')
def index():
    key = client.key('EntityKind', 1234)
    entity = datastore.Entity(key=key)
    entity.update({
        'foo': u'bar'
    })
    client.put(entity)
    result = client.get(key)
    return jsonify(result)


if __name__ == '__main__':
    app.run(host='0.0.0.0')

app/requirements.txt

Flask==1.1.1
google-auth==1.6.2
google-cloud-datastore==1.8.0

docker-compose.yaml

datastoreのポートは、ホスト側にも開けておくと、GUIツールで見ることができて便利

version: '3.7'

x-custom:
  gcp:
    - &gcp_project_id "dummy"

services:
  app:
    build: "./app/"
    volumes:
      - "./app/:/app/"
    environment:
      FLASK_APP: dev
      DATASTORE_HOST: "http://datastore:8081"
      DATASTORE_EMULATOR_HOST: "datastore:8081"
      PROJECT_ID: *gcp_project_id
      TZ: Asia/Tokyo
    ports:
      - "5000:5000"
    depends_on:
      - datastore
  datastore:
    build: "./datastore"
    volumes:
      - "./datastore/.data:/datastore/.data"
    environment:
      DATASTORE_PROJECT_ID: *gcp_project_id
      DATASTORE_LISTEN_ADDRESS: 0.0.0.0:8081
    ports:
      - "18081:8081"

起動

docker-compose up 

ブラウザやcurlで、http://localhost:5000にアクセスすると{"foo": "bar"}と表示されるはず。

ログ

下記にデータが貯められてるのがわかる

/datastore/.data/WEB-INF/appengine-generated/local_db.bin
datastore_1  | Updated property [core/project].
datastore_1  | WARNING: Reusing existing data in [/datastore/.data].
datastore_1  | Executing: /google-cloud-sdk/platform/cloud-datastore-emulator/cloud_datastore_emulator start --host=0.0.0.0 --port=8081 --store_on_disk=True --consistency=0.9 --allow_remote_shutdown /datastore/.data
app_1        |  * Serving Flask app "main" (lazy loading)
app_1        |  * Environment: production
app_1        |    WARNING: This is a development server. Do not use it in a production deployment.
app_1        |    Use a production WSGI server instead.
app_1        |  * Debug mode: off
app_1        |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
datastore_1  | [datastore] Jan 22, 2020 6:22:00 AM com.google.cloud.datastore.emulator.CloudDatastore$FakeDatastoreAction$9 apply
datastore_1  | [datastore] INFO: Provided --allow_remote_shutdown to start command which is no longer necessary.
datastore_1  | [datastore] Jan 22, 2020 6:22:01 AM com.google.cloud.datastore.emulator.impl.LocalDatastoreFileStub <init>
datastore_1  | [datastore] INFO: Local Datastore initialized:
datastore_1  | [datastore]      Type: High Replication
datastore_1  | [datastore]      Storage: /datastore/.data/WEB-INF/appengine-generated/local_db.bin
datastore_1  | [datastore] Jan 22, 2020 6:22:02 AM com.google.cloud.datastore.emulator.impl.LocalDatastoreFileStub load
datastore_1  | [datastore] INFO: Time to load datastore: 218 ms
datastore_1  | [datastore] API endpoint: http://0.0.0.0:8081
datastore_1  | [datastore] If you are using a library that supports the DATASTORE_EMULATOR_HOST environment variable, run:
datastore_1  | [datastore] 
datastore_1  | [datastore]   export DATASTORE_EMULATOR_HOST=0.0.0.0:8081
datastore_1  | [datastore] 
datastore_1  | [datastore] Dev App Server is now running.
datastore_1  | [datastore] 
datastore_1  | [datastore] The previous line was printed for backwards compatibility only.
datastore_1  | [datastore] If your tests rely on it to confirm emulator startup,
datastore_1  | [datastore] please migrate to the emulator health check endpoint (/). Thank you!
datastore_1  | [datastore] The health check endpoint for this emulator instance is http://0.0.0.0:8081/Jan 22, 2020 6:22:11 AM io.gapi.emulators.grpc.GrpcServer$3 operationComplete
datastore_1  | [datastore] INFO: Adding handler(s) to newly registered Channel.
datastore_1  | [datastore] Jan 22, 2020 6:22:11 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
datastore_1  | [datastore] INFO: Detected HTTP/2 connection.

データ投入

エミュレータのエンドポイントに、リクエストを投げてあげれば、ダンプデータも取り込める

docker-compose exec datastore bash ./import/run.sh

参考

下記のサイトを参考にさせていただきました。

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?