Hosting apps in the cloud with Google App Engine in 2024
Wesley Chun (@wescpy)
Posted on October 22, 2024
TL;DR:
If you're looking to put code online today, you have many options, with a VM or Kubernetes as the best choice for some. Others with less code may consider (AWS) Lambda. There are also options where developers don't have to consider traditional servers... these are known as serverless (compute) platforms. Google Cloud (GCP) offers several serverless options, and while Google (and I) will encourage developers to go straight to Cloud Run, it would be remiss not to start the conversation with their "OG" serverless platform, App Engine, as it still does a few things that Cloud Run can't or won't do. Cloud Run will be covered in future posts.
Introduction
Welcome to the blog focused on using Google APIs, developer tools, and platforms, primarily from Python, but also sometimes Node.js. Here, you'll see me cover everything from to AI/ML on Google Cloud/GCP to Google Maps to YouTube to Gemini, and also nut-n-bolts like API key, and OAuth client ID credentials, especially for Workspace/GWS APIs like Google Docs and Google Sheets. The aim is to provide something for everyone.
The purpose of this post is to directly address the question, "Where can I host my app in the cloud?" More specifically, "How can I do so with the least amount of setup and friction?" The answers to both will almost always point to serverless, a subset of cloud computing focused on logic-hosting.
Serverless computing and logic-hosting
"Logic" means any code, whether a short code snippet to a web app to a container running a service. So "logic-hosting" is an all-encompassing term that includes app-hosting (hosting your apps in the cloud), function-hosting (hosting your functions in the cloud; FaaS), and container-hosting (hosting your containers in the cloud; CaaS).
All of that hosting can be accomplished with serverless computing, the ability to host that logic without thinking about or being concerned with servers, virtual machines or bare-metal. It's a bit of a misnomer because of course there are servers, but they've been abstracted away from users. Developers only need to focus on building their solutions, not what they run on. Learn more in the serverless post I'm about to introduce below.
Introduction
Google offers various serverless platforms for developers, and I introduce four (4) of them in the "Broader Perspective of Serverless" post. The majority of the platforms covered are offered by GCP, and I want to dive into each. Setting aside the use cases of functions and microservices for Cloud Functions (GCF) and Cloud Run Functions (GCRF), the platforms most suitable for applications (web apps primarily) are Google App Engine (GAE) and Cloud Run (GCR).
While Google and I encourage developers to start with GCR, GAE may still be the right tool for a certain class of users who prefer a bit less sophistication and core "building blocks" for building apps. GAE still has some features not available in GCR and is a pure PaaS solution while GCR falls between the PaaS and IaaS categories of cloud computing, meaning you have to do a few more things (like bundle a web server and tell GCR how to start your app) whereas it's taken care of by GAE.
With this in mind, I feel it's a better approach to start with GAE before diving into GCR, so here we are. The intention of the rest of the post is to show Python (3) and Node.js developers how to get a "Hello World!" app up-and-running on GAE with the least amount of friction. I'll then do the same with GCR and the functions/FaaS platforms in upcoming posts.
ā ļø ALERT: Cost: billing required (but free?!?)
While many Google products are free to use, GCP products are not. In order to run the sample apps, you must enable billing and have active billing supported by a financial instrument like a credit card (payment method depends on region/currency). If you're new to GCP, review the billing and onboarding guide. Billing wasn't mandatory to use GAE in the past but became a requirement in 2019.
The good news is that GAE (and other GCP products) have an "Always Free" tier, a free daily or monthly usage before incurring charges. The GAE pricing and quotas pages have more information. Furthermore, deploying to GCP serverless platforms incur minor build and storage costs.
Cloud Build has its own free quota as does Cloud Storage (GCS). For greater transparency, Cloud Build builds your application image which is than sent to the Cloud Container Registry (CR) or Artifact Registry (AR), its successor. Storage of that image uses up some of that GCS quota as does network egress when transferring that image to the service you're deploying to. However you may live in a region that does not have a free tier, so monitor storage usage to minimize potential costs. (Check out storage use and delete old/unwanted build artifacts via the GCS browser.)
With the above said, you may qualify for credits to offset any costs of using GCP. If you are a startup, consider applying for the GCP for Startups program grants. If you are a higher education student, faculty member, researcher, or are an education technology ("edtech") company, explore all the GCP for education programs.
Getting started
Let's see how easy it is to get started with GAE. The shortest/smallest app or "MVP" (minimally-viable product) requires just three (3) files:
- GAE configuration
- 3rd-party requirements
- Main application
Both samples are available in the repo for this post. Let's walk through each code sample, run locally, then do some GCP setup and deploy to GAE; Python first.
Python
The GAE configuration file is (always) named app.yaml
(repo link), and while many options are available, an MVP only requires specifying the language runtime ID. In this sample app, I set it to Python 3.12:
runtime: python312
Python's requirements.txt
file (repo link) is used for specifying all 3rd-party libraries, and in this case, it's just the Flask micro web framework:
flask
Name the main application file anything you want; I settled on main.py
(repo link), shown here:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def root():
return 'Hello World!'
# local-only
if __name__ == '__main__':
import os
app.run(debug=True, threaded=True, host='0.0.0.0',
port=int(os.environ.get('PORT', 8080)))
The Flask library is imported, then the Flask app instantiated. The only route in this app is to '/' which returns 'Hello World!'
from the (default) GET
request (browser, curl
, etc.). You can name route functions anything you like, such as main()
or index()
-- I often use root()
. The last few lines are only for running this app locally... drop this section entirely if only deploying to the cloud. It uses the os
module to read the PORT
environment variable and fires up the Flask "devserver" (development server) on port 8080 if PORT
isn't set.
Now run the script to test the app locally; your (terminal) output should look something like this:
$ python3 main.py
* Serving Flask app 'main'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8080
* Running on http://172.20.20.20:8080
Press CTRL+C to quit
* Restarting with watchdog (fsevents)
* Debugger is active!
* Debugger PIN: 135-387-333
With a running server, point a web browser to http://localhost:8080:
Ā
In your terminal, you'll see log entries for each HTTP request:
127.0.0.1 - - [05/Dec/2023 15:42:55] "GET / HTTP/1.1" 200 -
To exit the devserver, issue a ^C
(Control-C) on the command-line.
Node.js
Now, let's turn to the Node.js version, which looks quite similar, starting with the config file, same name: app.yaml
(repo link) but specifying Node 20:
runtime: nodejs20
The Node.js 3rd-party requirements are found in the package.json
file (repo link). The Python sample relies on the Flask web framework, and similarly, this Node.js app uses the Express.js framework. The package.json
file looks like this:
{
"name": "helloworld-nodejs",
"version": "0.0.1",
"description": "Node.js App Engine sample app",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "@wescpy",
"license": "Apache-2.0",
"dependencies": {
"express": "^4.17.1"
}
}
Like with Python, call your main application anything you like. Typically with Node.js, they're named app.js
, main.js
, or what I picked: index.js
(repo link). Here are its contents:
const express = require('express');
const app = express();
app.get('/', (req, rsp) => {
rsp.send('Hello World!');
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () =>
console.log(`Listening on port ${PORT}`)
);
module.exports = app;
App instantiation is followed by the main (GET
) handler returning "Hello World!" regardless of request content. The rest sets up the server on the designated PORT
and exports the app
. Unlike Python which automatically spawns a web server on GAE, Node.js requires you to run your own, hence why this code isn't enclosed in an if
block like Python.
Run npm install
to install all the required packages, resulting in the expected node_modules
folder and package-lock.json
file. Express doesn't log individually-received requests -- developers have to add middleware to see HTTP requests, so after starting the app with npm start
to test the app locally, the only output you'll see until you quit the server looks like this (unlike Flask which dumps user requests to the console):
$ npm start
> helloworld-nodejs@0.0.1 start
> node index.js
Listening on port 8080
Hit this app with your web browser at http://localhost:8080 and observe similar "Hello World!" output like with the Python app. Press ^C
(Control-C) to quit the Express server.
If you prefer to work with modern ECMAScript modules, quick require
-to-import
and module export tweaks result in the equivalent index.mjs
(repo link) ESM file:
import express from 'express';
const app = express();
app.get('/', (req, rsp) => {
rsp.send('Hello World!');
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () =>
console.log(`Listening on port ${PORT}`)
);
export default app;
Deploying apps to App Engine
Now that you have something working locally, a more exciting next step is to run the app in the cloud and observe how it's accessible globally. Let's start with some GCP setup.
Google Cloud SDK
The gcloud
command-line tool (CLI) is required to deploy apps to GAE. It comes with the Google Cloud SDK (software development kit). If you already have it installed, skip to the next section. If you're new to GCP, follow the instructions to install the SDK and learn a few gcloud
commands to get comfortable using it. If you simply want to install the SDK, see these instructions instead. After installation, run gcloud init
to initialize it -- you may be prompted to login.
Google Cloud project
A cloud project is required to manage all the resources, e.g., APIs, compute, and storage, used for an app. Create a new one or reuse an existing project. Then assign a billing account to that project if you haven't already.
Each project can create at most one GAE app, and it gets a free domain name: PROJECT_ID.REG.appspot.com
where PROJECT_ID
is the project ID (duh) while REG
is an abbreviation for the assigned region.
For your convenience, set a default project with this command: gcloud config set project PROJECT_ID
. If you don't set a default, you'd have to pass it in with --project-id PROJECT_ID
each time you run a gcloud
command. Otherwise you'll be prompted for the project ID.
š Optional Python-related installs
Python developers have a few additional, optional installations if they're of interest to you.
- App Engine SDK (Python 3): If you have an existing Python 2 app using the GAE bundled services and considering a migration to Python 3, this is for you. The App Engine (services) SDK (not to be confused with the GCP SDK described above) gives Python 3 apps access to those bundled services bridging Gen1 apps to Gen2. Install it with:
pip install appengine-python-standard
(orpip3
).- GCP App Engine "extras": There are a pair of additional, (very) optional GCP components. The
app-engine-python
component contains the local GAE devserver (not to be confused with the Flask or Express devservers) if you want to simulate bundled services locally. Another one isapp-engine-python-extras
, which has code for Python 2 apps using built-in libraries. Since 2.x apps can no longer be deployed, you wouldn't use this. Regardless, install one or both components with a command like this:gcloud components install app-engine-python-extras app-engine-python
Final setup
Once you've confirmed you've completed these basic tasks, you should be ready to deploy your app:
- Install GCP SDK (includes
gcloud
CLI) - Create new or reuse existing GCP project
- Enable billing & assign billing account
- Run
gcloud init
- (optional) Run
gcloud config set project PROJECT_ID
- (optional) Any additional installs (see sidebar above)
Deploy app to App Engine
Before deploying your sample Python or Node.js sample app, a few words on regions. Here are the key points all developers should know:
- Every GAE app is hosted in a GCP region.
- Regardless of which region it is hosted in, your app is accessible globally.
- When deploying an app the first time, you'll be prompted to set your GAE app's region.
- You'll only set the region once because it's a permanent choice (see sidebar below).
ā App Engine region cannot be modified! |
---|
Every Cloud project can only have one App Engine app, and that app can only ever belong to one region. When assigned, that decision is permanent and cannot be changed, so choose wisely. Rest assured however that regardless of where your app is hosted, it's still reachable globally. Choose a region where you expect most of your users to be. If you insist on another region, you'll have to create another GAE app with another project. One advantage of newer GCP serverless platforms like GCR is that you can have more than one and they can serve different regions. |
Let's take a look at the deployment process... run: gcloud app deploy
. If you didn't set your project ID with gcloud
earlier, pass it in with --project-id PROJECT_ID
here or enter when prompted. You'll then be prompted to select a region. Your output will resemble this:
$ gcloud app deploy
Reauthentication required.
Please enter your password:
You are creating an app for project [PROJECT_ID].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.
Please choose the region where you want your App Engine application located:
[1] asia-east1 (supports standard and flexible)
[2] asia-east2 (supports standard and flexible and search_api)
[3] asia-northeast1 (supports standard and flexible and search_api)
. . .
[21] us-west2 (supports standard and flexible and search_api)
[22] us-west3 (supports standard and flexible and search_api)
[23] us-west4 (supports standard and flexible and search_api)
[24] cancel
Please enter your numeric choice: 19
Creating App Engine application in project [PROJECT_ID] and region [us-east4]....done.
Because your region choice is permanent, you'll never be prompted for it again. The rest of the output shown below is what you'll see next for your first and all subsequent deployments:
Services to deploy:
descriptor: [/private/tmp/app.yaml]
source: [/private/tmp]
target project: [PROJECT_ID]
target service: [default]
target version: [20231205t154433]
target url: [https://PROJECT_ID.REG.appspot.com]
target service account: [PROJECT_ID@appspot.gserviceaccount.com]
Do you want to continue (Y/n)?
Beginning deployment of service [default]...
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā Uploading 3 files to Google Cloud Storage āā£
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://PROJECT_ID.REG.appspot.com]
You can stream logs from the command line by running:
$ gcloud app logs tail -s default
To view your application in the web browser run:
$ gcloud app browse
On the first deployment, all three files are new, so that is what the build system uploads. On subsequent deployments, only new and modified files are uploaded. Once deployed, visit the app at: https://PROJECT_ID.REG.appspot.com
, a real online address, and you'll see the same "Hello World!" page as before:
Ā
Now that you've successfully deployed a sample Python or Node.js app and have seen it running on Google Cloud, continue to experiment by changing the sample app and redeploying, or uploading one of your "real" apps. Regardless of next steps, you've now learned one place you can run your apps in the cloud, and nowhere in the discussion above did you consider allocating, configuring, or paying for a server.
Summary and next steps
Congratulations on taking your first step in learning about Google's original serverless platform, App Engine. In addition to web apps, GAE can also host mobile backends. A web UI is optional, and GAE only needs to receive HTTP GET
and POST
requests, as long as your app has the appropriate handlers for such requests. Overall, is serverless right for you? And what other platforms does GCP offer?
Where should you run your app?
Serverless is the right solution for developers who want their CSP (cloud services provider) to do most of the heavy-lifting in terms of app-hosting and deployment. It is suitable for apps where the traffic is unpredictable or infrequent (say for student projects, small business websites, mobile backends, etc.), including short-lived apps with extreme traffic, say for a royal wedding!
Apps that get steady, continuous traffic, requiring it be available 24x7, are likely best served with VM-based solutions like Google Compute Engine (GCE) or Google Kubernetes Engine (GKE) because the cost of serverless will outgrow its benefits. However, if you're just getting off the ground, serverless helps you get there first, or at least, faster. More on serverless non-use cases can be found in the other serverless post.
Newer serverless platforms > GAE
If serverless sounds right for you, GAE is one option, but GCP has two others: GCF and GCR, both newer than GAE and part of a "Serverless 2.0" generation of products. In late summer 2024, the 2nd-generation of Cloud Functions was rebranded as Cloud Run Functions (GCRF). The newer platforms extend from GAE's original app-hosting mission to include functions/microservices and containers.
When you don't have an entire app or don't want/need to worry about full-stack or (LAMP, MEAN, etc.) stacks at all, function-hosting is likely the tool for you. These scenarios cover shorter snippets of code to perform specific tasks or microservices in a multi-tiered application, or basic cloud-based mobile backend functionality.
Modern software applications are now shipped as containers. Organizing app code and resources helps isolate dependencies, coordinate versions, and serves the purpose of providing a "shopping bag" for what apps depends on. In the past, developers had to decide between the flexibility of containers versus the convenience of serverless. This "problem" is directly addressed by GCR which runs containers in a serverless way. Furthermore, those with modern AI/ML workloads will be happy to know that GCR supports GPUs serverlessly.
Overall, the newer platforms are generally more flexible and have newer features than GAE, giving you the ability to have multiple (GCF) functions or (GCR) services vs. one GAE app per project. Both GCF and GCR can be deployed to different regions too, which can be critical for some applications.
GAE > Newer serverless platforms
With all of the advantages of GCF, GCR, and GCRF, what can GAE still do better? The bundled services are a big part of the picture. Don't most apps need a database, caching server, external task execution, large blob/file storage, etc? And what if they're (mostly) free and highly-scalable? Yep, that's the promise that the original App Engine team envisioned. The newer serverless platforms require use of standalone GCP services, each of which has their own pricing, or your own best-of-breed options if you prefer or if GCP doesn't have an exact replacement.
Additionally, GAE infrastructure features an automatic static content service. This free pseudo-CDN (content delivery network) gives you edge-caching without having to set up your own CDN, critical to rendering assets to your users much faster than if served by your application. These are just a few things GAE still does better, so if they're less important for your app(s), you'll likely be better served by the newer serverless platforms.
Billing epilogue
If you want to keep the sample GAE app up and working-as-intended to continue learning and experimenting, great. However, recall it's not a free service, and while you're not likely to incur any charges, the longer you run it, and the more you deploy new versions, this will eventually lead to billable usage. There are several options to minimize getting charged.
One option is to "pause" your app so as to not get billed for inadvertent usage, especially if robots, hackers, or other online scanners discover your app. To do this, disable your app. When you're ready to continue playing with GAE, re-enable it. While your app is disabled and won't incur charges, be aware other resources used in your Cloud project may accrue charges.
Let's say you deployed your app numerous times, with accrued images and other build files exceeding the free tier on GCS or the registries (CR/AR). (In my experience, the registries accrue costs faster than GCS.) These will continue to bill you even with a disabled app, so be sure to tidy both up in addition to disabling your app to avoid (bad) surprises. (Also review the earlier sidebar on costs if you haven't already!)
On the other hand, if you're not going to continue with GAE or this GCP project, delete everything forever to avoid possible billing. To do that, shutdown your project.
Conclusion
If you're a Python, Java, Go, PHP, Ruby, or Node.js developer curious about GAE and made it this far, you now know what the platform is all about and how to deploy a sample app to the cloud. While new features are primarily going into the newer serverless platforms, GAE is still fulfilling its purpose of providing a serverless app-hosting service with autoscaling properties. GAE apps using bundled services can now migrate from Gen1 to Gen2 with much less friction and upgrade to more recent language versions.
When ready to migrate off the bundled services, developers have additional options of staying on GAE (Gen2), breaking up large apps into multiple microservices for GCF or GCRF, or containerizing their apps for GCR. Furthermore, apps that don't use GAE bundled services can also be migrated to "serverful"/VM-based platforms like GCE or GKE.
If you found an error in this post, bug in the code, or have a topic you want me to cover in the future, drop a note in the comments below or file an issue at the repo. If you have an older GAE app and need advice on possible next steps, whether upgrading from Python 2 to 3, migrating off the bundled services, or shifting to other serverless platforms (GCF/GCRF or GCR), I may be able to help... check out https://appenginemigrations.com and submit a request there.
Thanks for checking out my blog... I hope you find this content useful. I enjoy meeting users on the road, so check if I am visiting your community soon... see the travel calendar on my consulting page.
References
Below are various resources related to this post which you may find useful.
Blog post code samples
Google App Engine (GAE) resources
GCP compute platforms (serverless and VM-based)
- Google App Engine (GAE)
- Google Cloud Functions (Gen1) and Cloud Run Functions (Gen2) (GCF/GCRF)
- Google Cloud Run (GCR)
- Google Compute Engine (GCE)
- Google Kubernetes Engine (GKE)
GCP serverless content by the author
- Pick a GCP serverless platform video
- How to design serverless apps video
- Exploring serverless with a nebulous app: Deploy the same app to App Engine, Cloud Functions, or Cloud Run blog post, video, code repo, socials
Google App Engine (GAE) historical references
- GAE 2008 original (Gen1) launch post and video
- GAE 2009 Java launch post
- GAE 2009 GAE general availability (GA) announcement
- GAE 2012 Go(lang) launch post
- GAE 2013 PHP launch post
- GAE 2014 Flexible launch post
- GAE 2018 Gen2 launch post
- GAE 2021 bundled services on Gen2 launch post
Other GAE content by the author
- GAE (Gen1 & early Gen2) 2024 deprecation "what you need to know" blog post
- "Rapid cloud development using App Engine for the Cycle Hire Widget app" blog post -- Building London's bike-share app with GAE case study
- "Change the world in 10 lines of code!" video -- Python GAE (inbound) Mail intro
^ -- Optional; only provides platform background information
Ā
DISCLAIMER: I was a member of the GAE product team 2009-2013 and 2020-2023. While product information is as accurate as I can find or recall, the opinions are my own.
WESLEY CHUN, MSCS, is a Google Developer Expert (GDE) in Google Cloud (GCP) & Google Workspace (GWS), author of Prentice Hall's bestselling "Core Python" series, co-author of "Python Web Development with Django", and has written for Linux Journal & CNET. He runs CyberWeb specializing in GCP & GWS APIs and serverless platforms, Python & App Engine migrations, and Python training & engineering. Wesley was one of the original Yahoo!Mail engineers and spent 13+ years on various Google product teams, speaking on behalf of their APIs, producing sample apps, codelabs, and videos for serverless migration and GWS developers. He holds degrees in Computer Science, Mathematics, and Music from the University of California, is a Fellow of the Python Software Foundation, and loves to travel to meet developers worldwide at conferences, user group events, and universities. Follow he/him @wescpy & his technical blog. Find this content useful? Contact CyberWeb if you may need help or buy him a coffee (or tea)!
Posted on October 22, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.