App Engine (GAE) - Introduction
An overview to App Engine on GCP and how to deploy a sample Flask Python application on the Standard Environment
Introduction to App Engine + Python Environment π
Google App Engine (GAE) is a fully managed and serverless platform for deploying applications. Focus on writing your code and easily deploy it on the cloud without worrying about operational overhead. It supports various languages including Python, Java, Ruby, and Go and the service can scale to varying demand to ensure reliable performance.
Features βοΈ
What makes App Engine a great service for developers? These are the features it provides:
- Supports many popular languages like Node.js, Java, Ruby, C#, Go, Python, or PHP in the Standard environment. Otherwise, use Flexible environment for other runtimes
- If you prefer to use Docker containers then that's possible with Flexible environment
- Fully Managed! A cloud favourite buzzword, but it's worth hyping because it means as a developer you focus more on your code and less on operations
- Host different versions of your app for easy testing, staging and managing environments
- A/B testing of versions by using traffic splitting
- Built in application security with SSL/TLS certificates and App Engine firewalls
- Easily integrate with other Google Cloud products!
Concepts π©βπ«
Before diving into an example of deploying a Flask application lets look at a couple important concepts.
Environment Types πΊοΈ
App Engine offers two environments - Standard and Flexible. There are a couple differences that may affect your environment decision. I've summarised the main differences in the following table:
Category | Standard | Flexible |
---|---|---|
Deployment Method | App is deployed in a sandbox, which restricts what your app can do | App is run by Docker containers on GAE virtual machines |
Programming Languages | Requires specific versions. For example Python version must be either 3.7, 3.8 or 3.9 | Can support any version and includes languages like .NET, which isn't available in Standard |
Free Tier | Yes | No |
Traffic | Good for rapid scaling for huge traffic spikes | Better for consistent traffic or regular traffic fluctuations |
Scale to Zero | Yes | No, minimum 1 instance |
Multiprocessing | No | Yes |
If you're looking for specific differences, such as which versions of Python can write to local disk or SSH debugging availability, then refer to Google's documentation here.
Overall, the cheaper and easier environment to use is Standard, but if you have specific requirements or functionality you need then you'll likely need Flexible.
Additionally, the documentation points out it's possible to combine both environments in your applications. How is that possible? Before I explain let's go through a few more concepts.
Architecture π
Applications in App Engine consists of a layered architecture with each layer containing one or more lower level items.
- Application: This top-level container includes everything that make up your application. All resources created and app metadata will be stored in your chosen region.
- Service: Logical components of your application. Services behave like microservices so each one should have a specific task it completes, such as handling API requests or processing backend data. It's not mandatory to break down your app into multiple services - everything can be bundled under the
default
service. - Version: Every service can have multiple versions so you can split your traffic to a new version for testing or rollback to an old version if something goes wrong.
- Instances: Compute for running your application. App Engine can provision additional instances to scale performance for higher traffic to ensure consistent performance. There are different scaling methods and configuration settings to when instances should be added.
If you split your application into multiple services then each service can be run in either the standard environment or flex environment. To do this in App engine we store configuration settings in a app.yaml
file, which I will cover shortly.
Creating an App on App Engine π©βπ»
I'll go through an example of deploying a Flask application to App Engine on the Standard Environment using the default
service only. This application will call an API ('official-joke-api.appspot.com/random_joke') and it will obtain a joke each time a button is pressed.
Code & Setup π₯οΈ
In your desired GCP project run a gcloud command to create an App Engine application in the current project. Pick a region that is close to you and make sure it's the one you want as you can't change it.
gcloud app create --region=europe-west2
Code β¨οΈ
This is the folder structure for the basic Flask application I'll be deploying.
your-app/
app.yaml
main.py
requirements.txt
static/
style.css
templates/
index.html
You can add more directories or adjust the structure, but ensure you have app.yaml
file in the main directory and your python file is called main.py
.
Python Flask Code π
To get started open your favourite code editor or Cloud Shell. If you're unfamiliar with Cloud Shell it's a handy online development environment embedded in Google Cloud Platform. The editor is similar to Visual Studio Code and supports various languages and development tools including gcloud
, Kubernetes, Docker and more. Every user gets 5GB of persistent disk storage so anything you store in your home directory persists.
Read more on Google Cloud Shell Here
There are two ways of accessing Google Cloud Shell
- Visit this link here
- From the browser
Click on the >_ button on the top right corner. This will pop open a screen at the bottom.
Then click on the box with the arrow pointing upwards. This will open the window in another tab so it's easier to see.
Finally click on the Pencil icon and you will have the editor open.
The code creates a Flask application that pulls a Joke from an API. It's very simple as I want to focus on App Engine in this tutorial.
The main.py
has the following code:
import os
import ast
import secrets
import datetime
import requests
from flask import Flask, render_template, redirect, request, url_for, session
app = Flask(__name__)
app.secret_key = os.getenv('APP_SECRET_KEY', secrets.token_urlsafe(16))
joke_url = 'https://official-joke-api.appspot.com/random_joke'
@app.route("/")
def index():
joke = session.get('joke', None)
return render_template("index.html", current_date=datetime.datetime.now(), joke=joke)
@app.route("/joke")
def joke():
joke = requests.get(joke_url)
joke_dict = ast.literal_eval(joke.content.decode('utf-8'))
session['joke'] = f"{joke_dict['setup']}? {joke_dict['punchline']}"
return redirect(url_for('index'))
if __name__ == "__main__":
app.run()
Our app can be run with Gunicorn when deployed to App Engine. We can test this locally by running gunicorn --bind localhost:5000 main:app
, which will run a Gunicorn production server for our Flask application. This command binds the host and port to Gunicorn and provides the entry point as main:app
- this is the name of the python file and name of the callable in the application.
Just in case it's unclear when you run a Flask application on the development server by default it'll run on localhost:5000. In our configuration file however we change the port to $PORT
, which is an environment variable in App Engine to automatically link to the NGINX layer.
app.yaml
We need to configure our application or default
service for App Engine. The only mandatory field is runtime
, but we'll add a couple other things for our application.
runtime: python37
instance_class: F1
entrypoint: gunicorn --bind localhost:$PORT main:app
handlers:
- url: /static
static_dir: static
# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
script: auto
secure: always
Let's go over these parameters and what they mean:
runtime
- Python version to useinstance_class
- The instance class to use which defines a memory limit and CPU limitentrypoint
- The command that should be run for executing the entrypoint commandhandlers
- A list of URL patterns and how they should be handledstatic_dir
- The path to directory containing static filesscript
- Specifies that requests to a specific handler should target your appsecure
- A configuration for either always, never or optionally using https
Other Files ποΈ
Here are the other files I have used.
Requirements.txt
Flask==2.0.1
requests==2.25.1
gunicorn==20.1.0
static/style.css
@import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');
body {
font-family: 'Montserrat', sans-serif;
text-align: center;
background-color: lavender;
}
hr {
width: 80%;
border-top: 1px solid #f0f0f5;
}
templates/index.html
<html>
<head>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>Hello!</h1>
<h3>Welcome to my website!</h3>
<h4>The current time is {{ current_date }} </h4>
<hr>
{% if joke %}
<p>{{ joke }}</p>
{% endif %}
<form action="/joke">
<input type="submit" value="I want a joke!"/>
</form>
</body>
</html>
Deploying π
In the root of your directory run the following command. This deploys your service(s) as a new version and behind the scenes Google will create a container using Cloud Build and keeps a copy in Google Cloud Storage before deploying on App Engine.
# Deploy a new version to GAE
gcloud app deploy
# Get the URL of your application
gcloud app browser
To view your application in the browser either run gcloud app browser
or visit your app engine URL at https://[PROJECT_ID].[REGION_ID].r.appspot.com
if you're not usng a custom domain.
NOTE If you get an error (gcloud.app.deploy) NOT_FOUND: Unable to retrieve P4SA:
wait a few seconds and try running the command again.
Deployed App π±
This is what my simple Flask application looks like. Every time you click the button it retrieves another joke from the API.
App Engine Interface π
On Google Cloud you can see information on your application including number of requests, 4xx or 5xx responses, visited URIs and their response times and traces, and much more. The great thing is this is all built into the service so it's ready to use.
I can see all of my versions, split traffic to run A/B testing and view information such as logs, source code or view the app in GCP's Debugging tool.
Conclusion & Thank You π
This is only scratching the surface in terms of App Engine's features and capabilities. I hope you found this introduction to App Engine and Python 3 Standard Environment useful. In a future post I'll be covering another Flask example except it will use OAuth2 to pull data from Google and store user data in a database.
Thanks for reading and I would love to hear your comments.