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 sistema e altri professionisti.

Cloud Functions è una piattaforma di serverless computing basata sugli 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 affronteremo questo argomento in questo lab, ma puoi scoprire di più nella documentazione.

efb3268e3b74ed4f.png

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

Cosa creerai

In questo codelab, pubblicherai una funzione Cloud Functions che, se richiamata tramite HTTP, mostra 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 Functions 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.pnga99b7ace416376c4.png5e3ff691252acf41.png

  • Il Nome progetto è il nome visualizzato per i partecipanti di questo progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarla.
  • L'ID progetto è univoco per 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 è importante sapere qual è. Nella maggior parte dei codelab, dovrai fare riferimento al tuo ID progetto (in genere identificato come PROJECT_ID). Se l'ID generato non ti piace, puoi generarne un altro casuale. In alternativa, puoi provare a inserire un nome di dominio di tua scelta e verificare se è disponibile. Non può essere modificato dopo questo passaggio e rimane invariato 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, dovrai abilitare la fatturazione nella console Cloud per utilizzare le risorse/API Cloud. L'esecuzione di questo codelab non costerà molto, se non addirittura nulla. Per arrestare le risorse ed evitare che ti vengano addebitati costi oltre a quelli di questo tutorial, puoi eliminare le risorse che hai creato o eliminare il progetto. I nuovi utenti Google Cloud possono usufruire del programma di 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à. In questo caso, 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 per sviluppatori 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 riconosca 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 creerà 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 alle funzioni Cloud Functions HTTP

Le funzioni Cloud Functions HTTP in Python sono scritte come normali funzioni Python. La funzione deve accettare un singolo argomento flask.Request, che di solito è denominato 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 l'editor della riga di comando che preferisci (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 del linguaggio. Per Python, 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 l'accesso a tutti i chiamanti, 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 comando riportato sopra. Puoi anche recuperare l'URL in modo programmatico e chiamare la funzione 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. Scrivi 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 comando riportato sopra. Puoi anche recuperare l'URL in modo programmatico e chiamare la funzione 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, basta un normale codice Python.

Ecco un esempio di 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 nello stesso modo degli altri file Python. Iniziano con un insieme di importazioni, quindi definiscono classi e funzioni.
  2. La dichiarazione del 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 prevista.

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

pip install flask

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

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

Esegui questi test localmente:

python -m unittest

I tre test delle unità dovrebbero essere superati:

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

OK

A questo punto, creerai una nuova funzione che restituisce il logo "Python Powered".

6. Scrittura della 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 comando riportato sopra. Se tutto funziona correttamente, vedrai il logo "Python Powered" in una nuova scheda del browser.

Ora creerai un'app per 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 i seguenti contenuti:

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 nell'URL di base che viene gestita con una funzione denominata index().
  3. La funzione index() chiama quindi la nostra funzione python_powered, trasmettendole la richiesta corrente.

Assicurati che il framework Flask sia installato nel tuo ambiente di sviluppo:

pip install flask

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

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

Per eseguire l'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 di 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 richiamata la funzione, incluso un Livello senza costi per le funzioni che non vengono eseguite spesso. Al termine del test di 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.