Launch Cloud Datastore Emulator with docker-compose and work with python app

background

I decided to use datastore as a service, and tried and errored how to test in the development environment and CI. : thinking: Since there is less information compared to AWS, I summarized it. : grinning:

The point

--Create a container for the flask app and Cloud Datastore Emulator --Linker-compose services --Initial data input of data store --Operate Cloud Datastore Emulator from the flask app

Directory structure

.
├── 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 container construction

datastore/Dockerfile

Minimal creation from the official SDK image. Grant execution authority to the emulator startup shell and data input 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

Even when docker-compose down is done, the data is stored in the/ datastore / .data /directory to maintain the data.

If you start it without options, you can only access it inside the container, so start it as --host-port = 0.0.0.0:8081. From the environment variable, pour it along with the project name.

#!/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}

Data store / import / run.sh for data input

After starting the server, you can import the data by throwing the save path of the dumped data to the following endpoint.

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 metadata

This time, from the gcp console, I brought the entire directory dumped to gcs under datastore / import. You may generate sdk data directly.

python app container construction

Build a fluent app for the sample.

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

I think it's a little too suitable, but I created an endpoint that just saves and retrieves data. : thinking:

The authentication information bites the dummy authentication.

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

If you open the datastore port on the host side as well, you can see it with a GUI tool, which is convenient.

https://github.com/GabiAxel/google-cloud-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"

Start-up

docker-compose up 

If you access http: // localhost: 5000 with your browser or curl, you should see {"foo": "bar"}.

log

You can see that the data is stored below

/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.

Data input

You can also import dump data by sending a request to the emulator endpoint.

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

reference

I referred to the following site.

--Connect and use Cloud Datastore Emulator from Node.js between Docker containers --Qiita

Recommended Posts

Launch Cloud Datastore Emulator with docker-compose and work with python app
Launch a web server with Python and Flask
Python environment with docker-compose
Easy to use Nifty Cloud API with botocore and python
Programming with Python and Tkinter
Encryption and decryption with Python
Python and hardware-Using RS232C with Python-
python with pyenv and venv
Search engine work with python
Collecting information from Twitter with Python (MySQL and Python work together)
Easy deep learning web app with NNC and Python + Flask
Works with Python and R
Build a detonation velocity website with Cloud Run and Python (Flask)
Make a scraping app with Python + Django + AWS and change jobs
Communicate with FX-5204PS with Python and PyUSB
Shining life with Python and OpenCV
[Package cloud] Manage python packages with package cloud
Robot running with Arduino and python
Install Python 2.7.9 and Python 3.4.x with pip.
Neural network with OpenCV 3 and Python 3
AM modulation and demodulation with python
[Python] font family and font with matplotlib
Scraping with Node, Ruby and Python
Scraping with Python, Selenium and Chromedriver
Scraping with Python and Beautiful Soup
JSON encoding and decoding with python
Run XGBoost with Cloud Dataflow (Python)
Hadoop introduction and MapReduce with Python
[GUI with Python] PyQt5-Drag and drop-
Reading and writing NetCDF with Python
Introduce errBot and work with Slack
I played with PyQt5 and Python3
Reading and writing CSV with Python
Multiple integrals with Python and Sympy
When matplotlib doesn't work with python2.7
Coexistence of Python2 and 3 with CircleCI (1.0)
Easy modeling with Blender and Python
Sugoroku game and addition game with python
FM modulation and demodulation with Python
Don't work Python with OpenCV on AMD Ryzen CPU on WSL2 Ubuntu 18.04 And 20.04
Deploy a Python app on Google App Engine and integrate it with GitHub
Work memo to migrate and update Python 2 series scripts on the cloud to 3 series
RabbitMQ message notification app in Python with Growl ~ with Raspberry Pi and Julius ~
Automate keyboard and mouse operations with python to streamline daily work [RPA]
Communicate between Elixir and Python with gRPC
Data pipeline construction with Python and Luigi
Calculate and display standard weight with python
Run Cloud Dataflow (Python) from App Engine
Monitor Mojo outages with Python and Skype
[Automation] Manipulate mouse and keyboard with Python
Passwordless authentication with RDS and IAM (Python)
Python installation and package management with pip
Uncle SES modernizes VBA app with Python
POST variously with Python and receive with Flask
Capturing images with Pupil, python and OpenCV
Fractal to make and play with Python
A memo with Python2.7 and Python3 on CentOS
Shared screen screenshot exe app with python
How to work with BigQuery in Python
Try using Python with Google Cloud Functions
Use PIL and Pillow with Cygwin Python