Funzioni Cloud Functions HTTP in Python

1. Introduzione

b158ce75c3cccd6d.png

Python è un linguaggio di programmazione open source molto diffuso utilizzato da data scientist, sviluppatori di applicazioni web, amministratori di sistemi e altri ancora.

Cloud Functions è una piattaforma di computing serverless basata su eventi. Cloud Functions ti consente di scrivere il codice senza preoccuparti del provisioning delle risorse o della scalabilità per gestire i requisiti in evoluzione.

Esistono due tipi di Cloud Functions:

  • Le funzioni HTTP rispondono alle richieste HTTP. In questo codelab ne creerai un paio.
  • Le funzioni in background vengono attivate da eventi, ad esempio la pubblicazione di un messaggio in Cloud Pub/Sub o il caricamento di un file in Cloud Storage. Non trattiamo questo argomento in questo lab, ma puoi saperne di più nella documentazione.

efb3268e3b74ed4f.png

Questo codelab ti guiderà nella creazione delle tue Funzioni Cloud in Python.

Cosa creerai

In questo codelab, pubblicherai una funzione Cloud Functions che, se richiamata tramite HTTP, visualizza il logo"Python Powered":

a7aaf656b78050fd.png

Obiettivi didattici

  • Come scrivere una funzione Cloud Functions HTTP.
  • Come scrivere una funzione Cloud Functions HTTP che accetta argomenti.
  • Come testare una funzione Cloud Functions HTTP.
  • Come eseguire un server HTTP Python locale per provare la funzione.
  • Come scrivere una funzione Cloud HTTP che restituisce un'immagine.

2. Configurazione e requisiti

Configurazione dell'ambiente autonomo

  1. Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Il nome del progetto è il nome visualizzato per i partecipanti a questo progetto. È una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarlo.
  • L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo l'impostazione). La console Cloud genera automaticamente una stringa univoca, di solito non ti interessa di cosa si tratta. Nella maggior parte dei codelab, dovrai fare riferimento all'ID progetto (in genere identificato come PROJECT_ID). Se non ti piace l'ID generato, puoi generarne un altro casuale. In alternativa, puoi provare a crearne uno e vedere se è disponibile. Non può essere modificato dopo questo passaggio e rimane per tutta la durata del progetto.
  • Per tua informazione, esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, devi abilitare la fatturazione in Cloud Console per utilizzare le risorse/API Cloud. Completare questo codelab non costa molto, se non nulla. Per arrestare le risorse ed evitare addebiti oltre a quelli previsti in questo tutorial, puoi eliminare le risorse che hai creato o il progetto. I nuovi utenti di Google Cloud possono usufruire del programma prova senza costi di 300$.

Avvia Cloud Shell

Sebbene Google Cloud possa essere gestito da remoto dal tuo laptop, in questo codelab utilizzerai Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Attiva Cloud Shell

  1. Nella console Cloud, fai clic su Attiva Cloud Shell 853e55310c205094.png.

3c1dabeca90e44e5.png

Se è la prima volta che avvii Cloud Shell, viene visualizzata una schermata intermedia che ne descrive le funzionalità. Se è stata visualizzata una schermata intermedia, fai clic su Continua.

9c92662c6a846a5c.png

Bastano pochi istanti per eseguire il provisioning e connettersi a Cloud Shell.

9f0e51b578fecce5.png

Questa macchina virtuale è caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Gran parte del lavoro per questo codelab, se non tutto, può essere svolto con un browser.

Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è autenticato e il progetto è impostato sul tuo ID progetto.

  1. Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list

Output comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Esegui questo comando in Cloud Shell per verificare che il comando gcloud conosca il tuo progetto:
gcloud config list project

Output comando

[core]
project = <PROJECT_ID>

In caso contrario, puoi impostarlo con questo comando:

gcloud config set project <PROJECT_ID>

Output comando

Updated property [core/project].

Assicurati che le API Cloud Functions e Cloud Build siano abilitate

Esegui questo comando da Cloud Shell per assicurarti che le API Cloud Functions e Cloud Build siano abilitate:

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

Nota: Cloud Build verrà chiamato dal comando gcloud functions deploy e compilerà automaticamente il codice in un'immagine container.

scarica il codice sorgente

Dal terminale Cloud Shell, esegui questi comandi:

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

Controlla i contenuti della directory di origine:

ls

Dovresti avere i seguenti file:

main.py  python-powered.png  test_main.py  web_app.py

3. Introduzione delle funzioni HTTP Cloud Functions

Le funzioni Cloud Functions HTTP in Python vengono scritte come normali funzioni Python. La funzione deve accettare un singolo argomento flask.Request, che di solito viene chiamato request.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

Puoi aprire il file con il tuo editor della riga di comando preferito (nano, vim o emacs). Puoi anche aprirlo nell'editor di Cloud Shell dopo aver impostato la directory di origine come area di lavoro:

cloudshell open-workspace .

Eseguiamo il deployment di questa funzione come funzione Cloud Functions HTTP utilizzando il comando gcloud functions deploy:

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Output comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Note sulle opzioni gcloud functions deploy:

  • --runtime: specifica il runtime della lingua. Per Python, al momento può essere python37, python38, python39, python310 o python312. Consulta la sezione Runtime.
  • --trigger-http: alla funzione verrà assegnato un endpoint. Le richieste HTTP (POST, PUT, GET, DELETE e OPTIONS) all'endpoint attiveranno l'esecuzione della funzione.
  • --allow-unauthenticated: la funzione sarà pubblica, consentendo a tutti i chiamanti di chiamarla senza verificare l'autenticazione.
  • Per saperne di più, consulta gcloud functions deploy.

Per testare la funzione, puoi fare clic sull'URL httpsTrigger.url visualizzato nell'output del comando riportato sopra. Puoi anche recuperare l'URL e chiamare la funzione in modo programmatico con i seguenti comandi:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Dovresti ottenere il seguente risultato:

Hello World! 👋

4. Scrivere una funzione Cloud Functions HTTP che accetta argomenti

Le funzioni sono più versatili quando accettano argomenti. Definiamo una nuova funzione hello_name che supporta un parametro name:

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

Eseguiamo il deployment di questa nuova funzione:

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Output comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Per testare la funzione, puoi fare clic sull'URL httpsTrigger.url visualizzato nell'output del comando riportato sopra. Puoi anche recuperare l'URL e chiamare la funzione in modo programmatico con i seguenti comandi:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Dovresti ottenere il risultato predefinito:

Hello World! 🚀

Stai ricevendo il risultato predefinito perché l'argomento name non è impostato. Aggiungi un parametro all'URL:

curl -w "\n" $URL?name=YOUR%20NAME

Questa volta riceverai la tua risposta personalizzata:

Hello YOUR NAME! 🚀

Il passaggio successivo consiste nell'aggiungere test delle unità per assicurarti che le funzioni continuino a funzionare come previsto quando il codice sorgente viene aggiornato.

5. Redazione dei test

Le funzioni Cloud Functions HTTP in Python vengono testate utilizzando il modulo unittest della libreria standard. Non è necessario eseguire un emulatore o un'altra simulazione per testare la funzione, ma solo il normale codice Python.

Ecco come appare un test per le funzioni hello_world e hello_name:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. I test Python vengono scritti allo stesso modo degli altri file Python. Iniziano con un insieme di importazioni, quindi definiscono classi e funzioni.
  2. La dichiarazione di test è nel formato class TestHello(TestCase). Deve essere una classe che eredita da unittest.TestCase.
  3. La classe di test ha metodi, ognuno dei quali deve iniziare con test_, che rappresentano singoli scenari di test.
  4. Ogni scenario di test verifica una delle nostre funzioni simulando il parametro request (ovvero sostituendolo con un oggetto fittizio con i dati specifici richiesti per il test).
  5. Dopo aver richiamato ogni funzione, il test controlla la risposta HTTP per assicurarsi che sia quella che ci aspettavamo.

Poiché main.py dipende da flask, assicurati che il framework Flask sia installato nell'ambiente di test:

pip install flask

L'installazione di Flask restituisce un risultato simile al seguente:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Esegui questi test in locale:

python -m unittest

I tre test delle unità devono essere superati:

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

Successivamente, creerai una nuova funzione che restituisce il logo "Python Powered".

6. Scrivere la funzione Cloud Functions HTTP "Python Powered"

Rendiamo una nuova funzione un po' più divertente restituendo l'immagine "Python Powered" per ogni richiesta:

a7aaf656b78050fd.png

Il seguente elenco mostra il codice per farlo:

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

Esegui il deployment di una nuova funzione python_powered:

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Output comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Per testare la funzione, fai clic sull'URL httpsTrigger.url visualizzato nell'output del comando riportato sopra. Se tutto funziona correttamente, vedrai il logo "Python Powered" in una nuova scheda del browser.

Successivamente, creerai un'app per poter eseguire e provare la funzione localmente prima del deployment.

7. Esecuzione della funzione in locale

Puoi eseguire una funzione HTTP localmente creando un'app web e chiamando la funzione in una route. Puoi aggiungerlo nella stessa directory della funzione. Il file denominato web_app.py ha il seguente contenuto:

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. Questo file crea un'applicazione Flask.
  2. Registra una route all'URL di base gestita con una funzione denominata index().
  3. La funzione index() chiama quindi la funzione python_powered, trasmettendo la richiesta corrente.

Assicurati che il framework Flask sia installato nell'ambiente di sviluppo:

pip install flask

L'installazione di Flask restituisce un risultato simile al seguente:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Per eseguire questa applicazione in locale, esegui questo comando:

python web_app.py

Ora utilizza l'anteprima web di Cloud Shell per testare l'app web nel browser. In Cloud Shell, fai clic sul pulsante "Anteprima web" e seleziona "Anteprima sulla porta 8080":

6c9ff9e5c692c58e.gif

Cloud Shell apre l'URL di anteprima nel suo servizio proxy, in una nuova finestra del browser. L'anteprima web limita l'accesso tramite HTTPS solo al tuo account utente. Se tutto funziona correttamente, dovresti vedere il logo "Python Powered".

8e5c3ead11cfd103.png

8. Complimenti!

b158ce75c3cccd6d.png

Hai eseguito il deployment di Cloud Functions HTTP, utilizzando funzioni idiomatiche che gestiscono le richieste web con il framework Flask.

I prezzi di Cloud Functions si basano sulla frequenza con cui viene chiamata la funzione, incluso un livello senza costi per le funzioni che non vengono eseguite spesso. Una volta terminato il test delle funzioni Cloud Functions, puoi eliminarle utilizzando gcloud:

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Puoi eliminare le funzioni anche dalla console Google Cloud.

Ci auguriamo che l'utilizzo di Cloud Functions in Python sia di tuo gradimento.