Deploying a Python serverless function in minutes with GCP
f( ☁️, 🐍 ) = 🌈🌤️🐍🐍🐍…
Hey Pythonistas, we can deploy Python functions!
Wikipedia is my source of truth to list an author’s bibliography, filmography… Unfortunately, not everything is available in digital form. For example, I’d like to list books actually available as ebooks. This is a very simple task, a microservice in fact. GCP’s Cloud Functions looks perfect for this. Let’s check it out, starting from scratch.
A few questions
What is Cloud Functions?
Cloud Functions is a managed service for serverless functions. The acronym describing such a service is FaaS (Function as a Service).
What’s a managed service?
It’s a service I don’t have to manage. I just use it.
What’s a serverless function?
Like a function in a program, a serverless (or cloud) function is an independent unit that can be naturally isolated in an app architecture.
How does it work?
The function is event driven: code will be called upon triggers such as an HTTP request, a file uploaded to a cloud storage, a message published…
What are the benefits of a serverless function?
- The code itself can be directly deployed in an isolated cloud function.
- The function resources scale automatically, up but also down, as the traffic evolves (the infra handles this for me).
- The function is highly available (a benefit of using a managed service).
- I only pay for what I use: if the function is not used, the cost is zero.
Why do I love serverless as a developer?
- I can focus on code.
- I have a clearer, yet robust and performant, architecture.
- I can sleep on both ears. If there’s a service outage at 2am, reliability engineers will handle the issue.
How do I get started?
I just need a GCP account. GCP offers a generous free tier to get started and try different products:
- First-time users get a $300 free credit to use over 12 months.
- Additionally, every month, Cloud Functions is free for the first 2 million invocations (and below other high volume thresholds).
So, unless I deploy a very successful function invoked millions of times every month, this won’t cost anything. Did I say I love serverless?
What’s in my toolbox?
There are 3 ways I can work with Cloud Functions (or GCP in general):
- From the graphical user interface (GUI): I can deploy cloud functions straight from the browser, without installing any tool. This is perfect to monitor or just discover the service, but also for quick prototyping or debugging.
- From the command-line interface (CLI): in a proper development workflow, I will typically script everything and use
gcloud
(GCP’s main command-line utility). - From the application programming interface (API): all GCP services are API based, allowing for fully industrialized developments. When I use the GUI or the CLI, I actually initiate web requests to the REST API undercover.
What’s my battle plan?
Let’s deploy a simple HTTP-triggered cloud function, first from the GUI, then from the CLI. But let’s prepare the code first.
Show me the code!
”Hello World!”
Flask is the underlying framework used to handle incoming HTTP requests. A typical “hello world” cloud function is as simple as this:
This gives, without parameters:
or, with a parameter:
Defining the HTTP entry point
I want to print out the list of available ebooks by a given author, in a given language. Based on the hello world sample above, I can define this simple Python function:
Getting the data from Google Books API
Requests
is “an elegant and simple HTTP library for Python, built for human beings” (docs.python-requests.org) and indeed it is. This allows querying an API (such as the Google Books API) very easily:
Printing the result in plain text
Flask naturally handles HTML templates but, for the sake of simplicity, let’s define this basic function to return a plain text result:
Deploying a cloud function from the GUI
GCP offers a web console, a GUI that works from any browser, without any prior installation. The web console is perfect for testing a feature (which is my case right now), quickly building a proof of concept, and of course monitoring my services.
Opening the web console
Creating a new project
Monitoring my project from the dashboard
Using the “getting started” shortcut
Creating my cloud function
Checking the function
Deleting the project
Deploying a cloud function from the CLI
A command-line interface (CLI) is the preferred tool if you regularly manage cloud projects. GCP offers Cloud Shell, a standard bash environment working directly from your browser, without any prior installation. The magic behind it is that it automatically handles an SSH connection to a small VM where everything I need is pre-installed. The main command-line utility is gcloud
. Let’s get a chrono (⌛) of each step…
Opening Cloud Shell (⌛15”)
Creating a new project (+11” → ⌛26”)
PROJECT_ID="MY-PROJECT-ID"
PROJECT_NAME="Get Ebooks PyGCF"gcloud projects create $PROJECT_ID \
--name="$PROJECT_NAME" \
--set-as-defaultCreate in progress for [https://cloudresourcemanager.googleapis.com/v1/projects/...].
Waiting for [operations/cp...] to finish...done.
Updated property [core/project] to [PROJECT_ID].
Note: Project IDs are unique across GCP. I used get-ebooks-pygcf
but you should define your own.
Linking the project to my billing account (+5” → ⌛31”)
BILLING_ACCOUNT=$(gcloud beta billing accounts list \
--format 'value(name)')gcloud beta billing projects link $PROJECT_ID \
--billing-account $BILLING_ACCOUNTbillingAccountName: billingAccounts/XXXXXX-YYYYYY-ZZZZZZ
billingEnabled: true
name: projects/PROJECT_ID/billingInfo
projectId: PROJECT_ID
Enabling the Cloud Functions API (+11” → ⌛42”)
gcloud services enable cloudfunctions.googleapis.comOperation "operations/acf..." finished successfully.
Getting the code (+3” → ⌛45”)
git clone https://github.com/PicardParis/cloud-snippets.gitCloning into 'cloud-snippets'...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 0), reused 8 (delta 0), pack-reused 0
Unpacking objects: 100% (8/8), done.cd cloud-snippets/python/gcf-get-ebooks/
lsmain.py
Deploying the function (+1’42” → ⌛2’27”)
GCF_NAME="get-ebooks"
GCF_ENTRY="get_ebooks_by_author"
GCF_REGION="europe-west1"gcloud functions deploy $GCF_NAME \
--entry-point $GCF_ENTRY \
--region $GCF_REGION \
--runtime python37 \
--trigger-http \
--allow-unauthenticatedDeploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
entryPoint: get_ebooks_by_author
httpsTrigger:
url: https://GCF_REGION-PROJECT_ID.cloudfunctions.net/GCF_NAME
labels:
deployment-tool: cli-gcloud
name: projects/PROJECT_ID/locations/GCF_REGION/functions/GCF_NAME
runtime: python37
serviceAccountEmail: ...
sourceUploadUrl: ...
status: ACTIVE
timeout: 60s
updateTime: 'YYYY-MM-DDThh:mm:ssZ'
versionId: '1'
Notes:
- The first deployment is longer (sets up the environment). If I update
main.py
and redeploy, the update takes ~25 seconds. - By default, deployed functions are private (they can only be called by authorized services within Google Cloud Platform). Flag
--allow-unauthenticated
makes the function public (so that we can call it with acurl
command below). Unless you are creating public APIs or websites, you will generally not use this flag in production.
Calling the function (+1” → ⌛2’28”)
GCF_URL=https://$GCF_REGION-$PROJECT_ID.cloudfunctions.net/$GCF_NAMEcurl "$GCF_URL?author=saint+exupery&lang=fr"====================================================================
"saint exupery" ebooks (lang=fr)
====================================================================
# | Pages | Title
1 | 528 | Écrits de guerre (1939-1944)
2 | 384 | Carnets
3 | 240 | Lettres à sa mère
5 | 180 | Les plus belles pensées d'Antoine de Saint-Exupéry
6 | 48 | Saint-Exupéry - / Le Royaume des étoiles
7 | 36 | Le Petit Prince raconté aux enfants
...curl "$GCF_URL?author=jrr+tolkien&lang=de"====================================================================
"jrr tolkien" ebooks (lang=de)
====================================================================
# | Pages | Title
1 | 1,568 | Der Herr der Ringe / Sonderausgabe
2 | 964 | Das Buch der verschollenen Geschichten / 1. & 2. Teil
3 | 720 | Nachrichten aus Mittelerde
4 | 608 | Der Herr der Ringe - Die Gefährten / Neuüberarbeitung
5 | 560 | Die Legende von Sigurd und Gudrún
6 | 558 | Das Silmarillion
7 | 444 | Der Herr der Ringe - Die Rückkehr des Königs /
...
Deleting the project (+5” → ⌛2’33”)
gcloud projects delete $PROJECT_ID
Level up!
- f( ☁️, 🐍 ) = 🌈🌤️🐍🐍🐍…
- The function can be called from:
https://europe-west1-get-ebooks-pygcf.cloudfunctions.net/get-ebooks?lang=en&author=jrr%20tolkien - The code snippet is available from this repo:
https://github.com/PicardParis/cloud-snippets/tree/master/python/gcf-get-ebooks - Deploying a simple cloud function from scratch takes less than 3 minutes.
- Updating an existing cloud function takes less than 30 seconds.
Updates
- 2019.12.16: Added flag
--allow-unauthenticated
to explicitly deploy a public function.
What’s next?
Deploying a Python serverless app in minutes (stay tuned)…