This series of codelabs (self-paced, hands-on tutorials) aims to help Google App Engine (Standard) developers modernize their apps by guiding them through a series of migrations. The most significant step is to move away from original runtime bundled services because the next generation runtimes are more flexible, giving users a greater variety of service options. Moving to the newer generation runtime enables you to integrate with Google Cloud products more easily, use a wider range of supported services, and support current language releases.
There are situations when you don't have an "entire app" to require the resources of App Engine or Cloud Run. If your code consists only of a microservice or simple function, Cloud Functions is likely a better fit. This tutorial teaches you how to migrate simple App Engine apps (or break apart larger apps into multiple microservices) and deploy them to Cloud Functions, a fully-managed service which was created specifically for use cases like this. This codelab begins with the Python 3 version of the Module 2 Cloud NDB App Engine sample app. Note that Cloud Functions does not support Python 2.
You'll learn how to
- Use Cloud Shell
- Enable the Google Cloud Translation API
- Authenticate API requests
- Convert a simple App Engine app to run on Cloud Functions
- Deploy functions/microservices to Cloud Functions
What you'll need
- A Google Cloud Platform project with an active GCP billing account
- Basic Python skills
- Working knowledge of common Linux commands
- Basic knowledge of developing and deploying App Engine apps
- A working Module 2 Cloud NDB Python 3 App Engine app
- Recommended: Complete the Module 2 codelab and the bonus step of porting the app from Python 2 to 3
How will you use this tutorial?
How would you rate your experience with Python?
How would you rate your experience with using Google Cloud services?
PaaS systems like Google App Engine and Cloud Functions provide many conveniences for users. These serverless platforms enable your technical team to focus on creating business solutions rather than spend time on investigating platforms to use and determining the amount of hardware needed. Applications can autoscale up as needed, scale down to zero with pay-per-use billing to control costs, and they allow for a variety of today's common development languages.
However, while full-stack web application development or complex back-ends for mobile apps is a great fit for App Engine, it's often the case that developers are mainly trying to put some functionality online, such as updating a news feed or pulling in the latest score of the home team's playoff game. While there is coding logic behind both scenarios, neither appear to be full-blown "applications" requiring the power of App Engine. This is where Cloud Functions come in.
Cloud Functions is for deploying the small bit of code that:
- Isn't a part of an entire application
- Isn't needed in an entire development stack
- Is in an application or single mobile app backend that focuses on one thing
You can also use Cloud Functions to break up a large, monolithic application into multiple microservices, each using a shared common database such as Cloud Firestore or Cloud SQL. And if you're in a situation where you would like your function or microservice containerized and executed serverlessly on Cloud Run, you can do that too.
Our sample App Engine app that's been featured in almost all of the migration tutorials is a simple app with basic functionality that would work well for Cloud Functions rather than App Engine. In this tutorial, you'll learn how to modify that app so that it runs on Cloud Functions. From the App Engine perspective, because functions are simpler than entire apps, your getting started experience should be easier (and faster), and there should be less "overhead" in general. You will experience this as well
This migration features these steps:
- Remove configuration files
- Modify application files
Before we get going with the main part of the tutorial, let's set up our project, get the code, then deploy the baseline app so we know we started with working code.
1. Setup project
If you completed the Module 2 codelab (and ported it to Python 3), we recommend reusing that same project (and code). Alternatively, you can create a brand new project or reuse another existing project. Ensure the project has an active billing account and App Engine (app) is enabled.
2. Get baseline sample app
One of the prerequisites to this codelab is to have a working Module 2 sample app. If you don't have one, go complete either tutorial linked above before moving ahead here. Otherwise if you're already familiar with its contents, you can start by grabbing the Module 2 code below.
Whether you use yours or ours, the Module 2 Python 3 code is where we'll START. This Module 11 codelab walks you through each step, concluding with code that resembles what's in the Module 11 repo folder (FINISH).
- START: Module 2 code (3.x [Module 2b repo folder])
- FINISH: Module 11 code (3.x)
- Entire repo (to clone or download ZIP)
The directory of Python 3 Module 2 STARTing files (yours or ours) should look like this:
$ ls README.md main.py templates app.yaml requirements.txt
3. (Re)Deploy baseline app
Your remaining prework steps to execute now:
- Re-familiarize yourself with the
- Re-deploy the sample app with
gcloud app deploy
- Confirm the app runs on App Engine without issue
Once you've successfully executed those steps, you're ready to convert it to a Cloud Function.
4. Remove configuration files
app.yaml file is an App Engine artifact not used with Cloud Functions so delete it now. If you don't or forget to do this, there is no harm since Cloud Functions won't use it. That's the only configuration change as
requirements.txt should stay identical to that from Module 2.
If you happen to be porting a Python 2 App Engine app to Python 3 at the same time, also delete
appengine_config.py as well as any lib folder you might still have. They are older App Engine artifacts and unused in Python 3 App Engine.
5. Modify application files
There is only one application file,
main.py, so all necessary changes to move to Cloud Functions occur in this file.
Because we're only working with functions, there's no need for a web application framework. However, for convenience, when Python-based Cloud Functions are called, they are automatically passed a request object for your code to use as needed. (The Cloud Functions team selected it to be a Flask Request object that is passed to your function.)
Since a framework isn't used, you won't import anything from Flask unless your app uses other features available from Flask. This is indeed our case. While we're not using the framework, we do need to render the same HTML template as our App Engine app. That means we still need to call
flask.render_template(), so that's the only thing we're keeping around to import from Flask. No web framework means we also need to delete its instantiation. Here are the changes from the top part of the source code resulting from these updates:
from flask import Flask, render_template, request from google.cloud import ndb app = Flask(__name__) ds_client = ndb.Client()
from flask import render_template from google.cloud import ndb ds_client = ndb.Client()
If you are dependent on the app object (
app) or any other web framework infrastructure, you need to resolve all those dependencies, finding appropriate workarounds, or remove their usage entirely or find proxies. Only then can you convert your code to a Cloud Function. Otherwise, you'll be better of staying on App Engine or containerizing your app for Cloud Run
Update main handler function signature
The changes required in the function signature are as follows:
- Since we're no longer using Flask when moving to Cloud Functions, remove the route decorator.
- Because Cloud Functions automatically passes in the Flask
Requestobject as a parameter, create a variable for it. In our sample app, we'll call it
- Deployed Cloud Functions must be named. Our main handler was named appropriately
root()in App Engine to describe what it was (the root application handler). As a Cloud Function, it makes less sense to use that name. Instead, we'll deploy the Cloud Function with the name
visitme, so use that as the Python function's name as well. Similarly, in Modules 4 and 5, we also named the Cloud Run service
Here are the before and after with these updates:
@app.route('/') def root(): 'main application (GET) handler' store_visit(request.remote_addr, request.user_agent) visits = fetch_visits(10) return render_template('index.html', visits=visits)
def visitme(request): 'main application (GET) handler' store_visit(request.remote_addr, request.user_agent) visits = fetch_visits(10) return render_template('index.html', visits=visits)
There are no changes at all to the main application code, so the code from Module 2 will stay as is. Here is a pictorial representation of the changes that were made:
Deploying to Cloud Functions is slightly different from App Engine. Because there are no configuration files outside of
requirements.txt, a few more things must be specified in the deployment command.
6. Build and deploy
Deploy your transformed
visitme function to Cloud Functions running under Python 3.9 triggered by HTTP with this command:
$ gcloud functions deploy visitme --runtime python39 --trigger-http --allow-unauthenticated
The output should look as follows, and provide some prompts for next steps:
Deploying function (may take a while - up to 2 minutes)...⠹ For Cloud Build Stackdriver Logs, visit: https://console.cloud.google.com/logs/viewer?project=PROJECT_ID&advancedFilter=resource.type%3Dbuild%0Aresource.labels.build_id%3D7e32429d-ec36-422c-8a8b-43c4d661a15c%0AlogName%3Dprojects%2FPROJECT_ID%2Flogs%2Fcloudbuild Deploying function (may take a while - up to 2 minutes)...done. availableMemoryMb: 256 buildId: 7e32429d-ec36-422c-8a8b-43c4d661a15 entryPoint: visitme httpsTrigger: securityLevel: SECURE_OPTIONAL url: https://REGION-PROJECT_ID.cloudfunctions.net/visitme ingressSettings: ALLOW_ALL labels: deployment-tool: cli-gcloud name: projects/PROJECT_ID/locations/REGION/functions/visitme runtime: python39 serviceAccountEmail: PROJECT_ID@appspot.gserviceaccount.com sourceUploadUrl: https://storage.googleapis.com/gcf-upload-REGION-873f8448-838f-4eb2-beda-3e200a1420d/cb1cbdca-34eb-41d0-88d6-c27d25fb.zip?GoogleAccessIdemail@example.com&Expires=1619139674 status: ACTIVE timeout: 60s updateTime: '2021-04-23T00:32:58.065Z' versionId: '3'
After your function has deployed, use the URL from the deployment output and visit your app. The URL is of the form:
REGION-PROJECT_ID.cloudfunctions.net/visitme. The output should be identical to when you deployed it earlier to App Engine:
As with most other codelabs and videos in the series, the baseline app functionality doesn't change. The purpose is to make a single modernization and have the app work exactly as before but with something newer, for example migrating from an older App Engine legacy service to its replacement Cloud standalone product, or you moved the app to another Google Cloud serverless platform. Today, it was the latter with moving your code from App Engine to Cloud Functions.
Congratulations for converting this small App Engine app to a Cloud Function! Another use case which could be appropriate would be breaking-up a large monolithic App Engine app into a series of microservices, each as a Cloud Function. This is a more modern development technique resulting in a more "plug-and-play" component (a la " JAM stack") style. It allows for mixing and matching, and code reuse, which are two goals, but another benefit is that these microservices will continue to get debugged over time, meaning stable code and lower maintenance costs overall.
Optional: Clean up and/or disable service
If you're not ready to go to the next tutorial yet, disable the Module 2 App Engine app to avoid incurring charges. When you're ready to move to the next codelab, you can re-enable it. While App Engine apps are disabled, they won't get any traffic to incur charges, however Datastore usage may be billable if it exceeds its free quota, so delete enough to fall under that limit.
Unfortunately Cloud Functions doesn't have a "disable" feature. Backup your code and just delete the function. You can always redeploy it with the same name later. On the other hand, if you're not going to continue with any other migrations and want to delete everything completely, shutdown your Cloud projects.
Beyond this tutorial, other migration modules to look at include containerizing your App Engine app for Cloud Run. See the links to the Module 4 and Module 5 codelabs:
- Module 4: Migrate to Cloud Run with Docker
- Containerize your app to run on Cloud Run with Docker
- This migration allows you to stay on Python 2.
- Module 5: Migrate to Cloud Run with Cloud Buildpacks
- Containerize your app to run on Cloud Run with Cloud Buildpacks
- You do not need to know anything about Docker, containers, or
- Requires your app to have already migrated to Python 3 (Buildpacks doesn't support Python 2)
8. Additional resources
App Engine migration module codelabs issues/feedback
If you find any issues with this codelab, please search for your issue first before filing. Links to search and create new issues:
Links to the repo folders for Module 8 (START) and Module 9 (FINISH) can be found in the table below. They can also be accessed from the repo for all App Engine codelab migrations which you can clone or download a ZIP file.
Below are online resources which may be relevant for this tutorial:
- App Engine documentation
- Python 2 App Engine (standard environment) runtime
- Python 3 App Engine (standard environment) runtime
- Differences between Python 2 & 3 App Engine (standard environment) runtimes
- Python 2 to 3 App Engine (standard environment) migration guide
- App Engine pricing and quotas information
- Second generation App Engine platform launch (2018)
- Comparing first & second generation platforms
- Long-term support for legacy runtimes
- Documentation migration samples repo
- Community-contributed migration samples repo
Other Cloud information
- Python on Google Cloud Platform
- Google Cloud Python client libraries
- Google Cloud "Always Free" tier
- Google Cloud SDK (
- All Google Cloud documentation
- Serverless Migration Station
- Serverless Expeditions
- Subscribe to Google Cloud Tech
- Subscribe to Google Developers
This work is licensed under a Creative Commons Attribution 2.0 Generic License.