Community Post

Deploying A Flask App On Google App Engine (Standard Environment)

John Mecham

Background

Recently I had to change Google App Engine environments due to my project requiring a python extension, which aren’t typically supported in the “standard” environment (the exception is numpy). There are pretty vast difference between the environments, but the thing that made the flex enviroment most attractive to me is that the charge back model is based on exact cpu, memory, and disk usage instead of instance hours. This becomes relevant when you have really small applications that have little to no usage which is likely how many of my project will be positioned. A full comparison of the two environments can be seen here: https://cloud.google.com/appengine/docs/the-appengine-environments

App.yaml

The first thing that has to be changed is the configuration file that determines how the app is deployed and in what enviroment. The allowed parameters are completely different based on the enviroment; in fact they don’t necessarily allow/require the same parameters.

Compare the difference in the app.yaml files in both enviorments:

# app.yaml (standard)
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /static/.*
  static_dir: static

- url: /.*
  script: main.app

#[START env]
env_variables:
    CLOUD_STORAGE_BUCKET: khet-online-storage
#[END env]
# app.yaml (flex)
runtime: python
env: flex
entrypoint: gunicorn -b :$PORT main:app

runtime_config:
  python_version: 2

handlers:
- url: /static/.*
  static_dir: static

- url: /.*
  script: main.app

#[START env]
env_variables:
    CLOUD_STORAGE_BUCKET: khet-online-storage
#[END env]

Probably the most notable is the "env" property is required in flex app.yaml but not valid in the standard environments configation, and yields vague 500 errors when trying to deploy your app.

Storage

This is where the majority of the pain came from for me. I had originally used wonderful libraries like ndb which functions much like ORMs like SQLAlchemy but for Google Cloud Storage; these however are not functional in the flex environment, which means I had to actually change significant amount of code…

Something like this (standard env):

from google.appengine.ext import ndb

# Define Model
class Task(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    description = ndb.StringProperty()

# Create new entity
task = Task(id='simpletask1', description= 'Buy Milk')

# Insert new entity
task.put()

Becomes like this (flex env):

# Imports the Google Cloud client library
from google.cloud import datastore

# Instantiates a client
datastore_client = datastore.Client()

# The kind for the new entity
kind = 'Task'
# The name/ID for the new entity
name = 'sampletask1'
# The Cloud Datastore key for the new entity
task_key = datastore_client.key(kind, name)

# Prepares the new entity
task = datastore.Entity(key=task_key)
task['description'] = 'Buy milk'

# Saves the entity
datastore_client.put(task)

As you can see the ndb library helps save quite a lot of boiler plate....

Testing

One of the things I actually loved about the standard environment is that it was deployable on my local desktop (dev_appserver.py tool), this allowed me to work out all the bugs before I ever needed to even try to deploy my app. Unfortunately the flex environment offers no equivent functionality, although we’re not totally left to the wolves either. The google cloud SDK offers a datastore emulator which was predominate feature I was utilizing in the local deployable server for the standard enviroment. It requires a bit more effort to setup as you'll like need to set specific environment variables that get are used by datastore library.

    os.environ['DATASTORE_DATASET'] ='YOUR DATASTORE'
    os.environ['DATASTORE_EMULATOR_HOST'] = 'localhost:PORT'
    os.environ['DATASTORE_EMULATOR_HOST_PATH'] = 'localhost:PORT/datastore'
    os.environ['DATASTORE_HOST'] = 'http://localhost:PORT'
    os.environ['DATASTORE_PROJECT_ID'] = 'YOUR PROJECT ID'

In conjunction with the emulator command

gcloud beta emulators datastore start --host-port=127.0.0.1:PORT --no-store-on-disk  --project=YOUR_PROJECT_ID

Opinions

The google app engine space in my opinion is still in an early phase so you should expect to get burned when being an early adopter. While I still think the deployment process is better then what’s present at my corporate job, it certainly not especially easy as there are tons of potential pitfalls and non-obvious errors. My prediction is that the flex environment will see at least one backwards breaking change this year or perhaps be just be demised all together (see flex support policy), so stay tuned for the next post on how I had to fix my app in a few months from now…

flex support/sla