Cloud Functions de HTTP en Python

1. Introducción

b158ce75c3cccd6d.png

Python es un lenguaje de programación de código abierto popular que usan científicos de datos, desarrolladores de aplicaciones web, administradores de sistemas y mucho más.

Cloud Functions es una plataforma de procesamiento sin servidores controlada por eventos. Con Cloud Functions, puedes escribir tu código sin preocuparte por aprovisionar recursos ni escalar para manejar los requisitos cambiantes.

Existen dos tipos de funciones de Cloud Functions:

  • Las funciones de HTTP responden a las solicitudes HTTP. Crearás un par en este codelab.
  • Las funciones en segundo plano se activan mediante eventos, como la publicación de un mensaje en Cloud Pub/Sub o la carga de un archivo a Cloud Storage. Este tema no se aborda en este lab, pero puedes leer más en la documentación.

efb3268e3b74ed4f.png

En este codelab, te ayudaremos a crear tus propias funciones de Cloud Functions en Python.

Qué compilarás

En este codelab, publicarás una Cloud Function que, cuando se invoca a través de HTTP, muestra el estado "Python Powered" Logotipo:

a7aaf656b78050fd.png

Qué aprenderás

  • Cómo escribir una Cloud Function de HTTP
  • Cómo escribir una Cloud Function de HTTP que tome argumentos
  • Cómo probar una Cloud Function de HTTP
  • Cómo ejecutar un servidor HTTP local de Python para probar la función
  • Cómo escribir una Cloud Function de HTTP que muestre una imagen

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a Google Cloud Console y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • El Nombre del proyecto es el nombre visible de los participantes de este proyecto. Es una cadena de caracteres que no se utiliza en las APIs de Google. Puedes actualizarla cuando quieras.
  • El ID del proyecto es único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Cloud genera automáticamente una cadena única. Por lo general, no importa cuál sea. En la mayoría de los codelabs, deberás hacer referencia al ID de tu proyecto (suele identificarse como PROJECT_ID). Si no te gusta el ID que se generó, podrías generar otro aleatorio. También puedes probar uno propio y ver si está disponible. No se puede cambiar después de este paso y se usa el mismo durante todo el proyecto.
  • Recuerda que hay un tercer valor, un número de proyecto, que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar las APIs o los recursos de Cloud. Ejecutar este codelab no costará mucho, tal vez nada. Para cerrar recursos y evitar que se generen cobros más allá de este instructivo, puedes borrar los recursos que creaste o borrar el proyecto. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.

Inicie Cloud Shell

Si bien Google Cloud se puede operar de manera remota desde tu laptop, en este codelab usarás Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shell853e55310c205094.png.

8c1dabeca90e44e5.png

Si es la primera vez que inicias Cloud Shell, verás una pantalla intermedia que describe en qué consiste. Si apareció una pantalla intermedia, haz clic en Continuar.

9c92662c6a846a5c.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

9f0e51b578fecce5.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo necesarias. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que mejora considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer con un navegador.

Una vez que te conectes a Cloud Shell, deberías ver que estás autenticado y que el proyecto está configurado con tu ID del proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que tienes la autenticación:
gcloud auth list

Resultado del comando

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Ejecuta el siguiente comando en Cloud Shell para confirmar que el comando de gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

Asegúrate de que las APIs de Cloud Functions y Cloud Build estén habilitadas

Ejecuta el siguiente comando desde Cloud Shell para asegurarte de que las APIs de Cloud Functions y Cloud Build estén habilitadas:

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

Nota: El comando gcloud functions deploy llamará a Cloud Build, que compilará automáticamente tu código en una imagen de contenedor.

Descargue el código fuente

En la terminal de Cloud Shell, ejecuta los siguientes comandos:

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

Revisa el contenido del directorio del código fuente:

ls

Deberías tener los siguientes archivos:

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

3. Introducción a Cloud Functions de HTTP

Las funciones de HTTP de Cloud Functions en Python se escriben como funciones normales de Python. La función debe aceptar un solo argumento flask.Request, que suele llamarse 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")

# ...

Puedes abrir el archivo con tu editor de línea de comandos preferido (nano, vim o emacs). También puedes abrirlo en el Editor de Cloud Shell después de configurar el directorio del código fuente como lugar de trabajo:

cloudshell open-workspace .

Implementaremos esta función como una Cloud Function de HTTP con el comando gcloud functions deploy:

FUNCTION_NAME="hello_world"

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

Resultado del 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
...

Notas sobre las opciones de gcloud functions deploy:

  • --runtime: Especifica el entorno de ejecución del lenguaje. Para Python, actualmente puede ser python37, python38, python39, python310 o python312. Consulta Tiempos de ejecución.
  • --trigger-http: Se asignará un extremo a la función. Las solicitudes HTTP (POST, PUT, GET, DELETE y OPTIONS) al extremo activarán la ejecución de la función.
  • --allow-unauthenticated: La función será pública, lo que permitirá todos los emisores, sin verificar la autenticación.
  • Para obtener más información, consulta gcloud functions deploy.

Para probar la función, puedes hacer clic en la URL httpsTrigger.url que se muestra en el resultado del comando anterior. También puedes recuperar la URL de manera programática y llamar a la función con los siguientes comandos:

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

Deberías obtener el siguiente resultado:

Hello World! 👋

4. Escribe una Cloud Function de HTTP que tome argumentos

Las funciones son más versátiles cuando aceptan argumentos. Definamos una nueva función hello_name que admita un parámetro 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")

# ...

Implementemos esta nueva función:

FUNCTION_NAME="hello_name"

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

Resultado del 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
...

Para probar la función, puedes hacer clic en la URL httpsTrigger.url que se muestra en el resultado del comando anterior. También puedes recuperar la URL de manera programática y llamar a la función con los siguientes comandos:

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

Deberías obtener el resultado predeterminado:

Hello World! 🚀

Obtendrás el resultado predeterminado porque no se estableció el argumento name. Agrega un parámetro a la URL:

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

Esta vez, obtendrás tu respuesta personalizada:

Hello YOUR NAME! 🚀

El siguiente paso es agregar pruebas de unidades para garantizar que las funciones sigan funcionando según lo previsto cuando se actualice el código fuente.

5. Escribir pruebas

Las funciones de HTTP de Cloud Functions en Python se prueban con el módulo unittest de la biblioteca estándar. No es necesario que ejecutes un emulador ni otra simulación para probar tu función; solo es código normal de Python.

Así se ve una prueba para las funciones hello_world y 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. Las pruebas de Python se escriben de la misma manera que otros archivos de Python. Empieza con un conjunto de importaciones y, luego, define clases y funciones.
  2. La declaración de prueba tiene el formato class TestHello(TestCase). Debe ser una clase que se herede de unittest.TestCase.
  3. La clase de prueba tiene métodos, cada uno de los cuales debe comenzar con test_, que representan casos de prueba individuales.
  4. Cada caso de prueba prueba una de nuestras funciones simulando el parámetro request (es decir, reemplazándolo por un objeto falso con los datos específicos requeridos para la prueba).
  5. Después de invocar cada función, la prueba verifica la respuesta HTTP para asegurarse de que sea lo que esperábamos.

Como main.py depende de flask, asegúrate de que el framework de Flask esté instalado en tu entorno de pruebas:

pip install flask

La instalación de Flask genera un resultado similar al siguiente:

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

Ejecuta estas pruebas de manera local:

python -m unittest

Las tres pruebas de unidades deben aprobarse:

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

OK

A continuación, crearás una nueva función que devuelva el comando “Python Powered” logotipo.

6. Escribir el curso “Python Powered” Cloud Function de HTTP

Hagamos que una nueva función sea un poco más entretenida devolviendo el código “Python Powered” imagen para cada solicitud:

a7aaf656b78050fd.png

En la siguiente lista, se muestra el código necesario para realizar esta acción:

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")

Implementa una nueva función python_powered:

FUNCTION_NAME="python_powered"

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

Resultado del 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
...

Para probar la función, haz clic en la URL httpsTrigger.url que se muestra en el resultado del comando anterior. Si todo funciona correctamente, verás el mensaje “Python Powered” en una nueva pestaña del navegador.

A continuación, crearás una app para poder ejecutar y probar tu función de manera local antes de implementarla.

7. Ejecuta la función de manera local

Puedes ejecutar una función HTTP de forma local si creas una app web y llamas a tu función en una ruta. Puedes agregarla en el mismo directorio que tu función. El archivo llamado web_app.py tiene el siguiente contenido:

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. Este archivo crea una aplicación Flask.
  2. Registra una ruta en la URL base que se controla con una función llamada index().
  3. Luego, la función index() llama a nuestra función python_powered y le pasa la solicitud actual.

Asegúrate de que el framework de Flask esté instalado en tu entorno de desarrollo:

pip install flask

La instalación de Flask genera un resultado similar al siguiente:

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

Para ejecutar esta aplicación de manera local, ejecuta el siguiente comando:

python web_app.py

Ahora, usa la vista previa web de Cloud Shell para probar la app web en tu navegador. En Cloud Shell, haz clic en “Vista previa en la Web”. y selecciona "Vista previa en el puerto 8080":

6c9ff9e5c692c58e.gif

Cloud Shell abre la URL de vista previa en una ventana nueva del navegador, en su servicio de proxy. La vista previa en la Web restringe el acceso a través de HTTPS solo a tu cuenta de usuario. Si todo funciona correctamente, deberías ver el mensaje "Python Powered" ¡logotipo!

8e5c3ead11cfd103.png

8. ¡Felicitaciones!

b158ce75c3cccd6d.png

Implementaste Cloud Functions de HTTP con funciones idiomáticas que controlan solicitudes web con el framework de Flask.

Los precios de Cloud Functions se basan en la frecuencia con la que se invoca tu función, lo que incluye un nivel gratuito para las funciones que no se ejecutan con frecuencia. Una vez que termines de probar tus Cloud Functions, puedes borrarlas con gcloud:

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

También puedes borrar las funciones desde la consola de Google Cloud.

Esperamos que disfrutes el uso de Cloud Functions en Python.