Introducción a la organización sin servidores con Workflows

1. Introducción

c9b0cc839df0bb8f.png

Puedes usar Flujos de trabajo para crear flujos de trabajo sin servidores que vinculen una serie de tareas sin servidores en el orden que definas. Puedes combinar la potencia de las APIs de Google Cloud, los productos sin servidores, como Cloud Functions y Cloud Run, y las llamadas a las APIs externas para crear aplicaciones flexibles sin servidores.

Workflows no requiere administración de infraestructura y escala sin problemas con la demanda, incluida la reducción vertical a cero. Con su modelo de precios de pago por uso, solo pagas por el tiempo de ejecución.

En este codelab, aprenderás a conectar varios servicios de Google Cloud y APIs de HTTP externas con Workflows. Más específicamente, conectarás dos servicios públicos de Cloud Functions, un servicio privado de Cloud Run y una API de HTTP pública externa en un flujo de trabajo.

Qué aprenderás

  • Conceptos básicos de los flujos de trabajo.
  • Cómo conectar Cloud Functions públicas con Workflows
  • Cómo conectar servicios privados de Cloud Run con Workflows
  • Cómo conectar APIs HTTP externas con Workflows

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a la consola de Cloud y crea un proyecto nuevo o reutiliza uno existente. (Si todavía no tienes una cuenta de Gmail o de G Suite, debes crear una).

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

Recuerde el ID de proyecto, un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID.

  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar los recursos de Google Cloud recursos.

Ejecutar este codelab no debería costar mucho, tal vez nada. Asegúrate de seguir las instrucciones de la sección “Realiza una limpieza”, en la que se indica cómo cerrar los recursos para que no se te facture más allá de este instructivo. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.

Inicia Cloud Shell

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

En GCP Console, haga clic en el ícono de Cloud Shell en la barra de herramientas superior derecha:

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

El aprovisionamiento y la conexión al entorno deberían tomar solo unos minutos. Cuando termine el proceso, debería ver algo como lo siguiente:

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Puedes realizar todo tu trabajo en este lab usando simplemente un navegador.

3. Descripción general de los flujos de trabajo

Conceptos básicos

Un flujo de trabajo está compuesto por una serie de pasos descritos con la sintaxis de Workflows basada en YAML. Esta es la definición del flujo de trabajo. Para obtener una explicación detallada de la sintaxis de YAML de Workflows, consulta la página Referencia de sintaxis.

Cuando se crea un flujo de trabajo, se implementa, lo que lo deja listo para su ejecución. Una ejecución es una ejecución única de la lógica que se incluye en la definición de un flujo de trabajo. Todas las ejecuciones de flujos de trabajo son independientes, y el producto admite una gran cantidad de ejecuciones simultáneas.

Cómo habilitar servicios

En este codelab, conectarás Cloud Functions y los servicios de Cloud Run con flujos de trabajo. También usarás Cloud Build y Cloud Storage durante la compilación de servicios.

Habilita todos los servicios necesarios con el siguiente comando:

gcloud services enable \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  workflows.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com

En el siguiente paso, conectarás dos Cloud Functions en un flujo de trabajo.

4. Implementa la primera Cloud Function

La primera función es un generador de números aleatorios en Python.

Crea un directorio para el código de la función y navega hasta él:

mkdir ~/randomgen
cd ~/randomgen

Crea un archivo main.py en el directorio con el siguiente contenido:

import random, json
from flask import jsonify

def randomgen(request):
    randomNum = random.randint(1,100)
    output = {"random":randomNum}
    return jsonify(output)

Cuando recibe una solicitud HTTP, esta función genera un número aleatorio entre 1 y 100 y lo muestra en formato JSON al llamador.

La función depende de Flask para el procesamiento de HTTP y debemos agregarlo como una dependencia. Las dependencias en Python se administran con pip y se expresan en un archivo de metadatos llamado requirements.txt.

Crea un archivo requirements.txt en el mismo directorio con el siguiente contenido:

flask>=1.0.2

Implementa la función con un activador HTTP y con solicitudes no autenticadas permitidas con este comando:

gcloud functions deploy randomgen \
    --runtime python312 \
    --trigger-http \
    --allow-unauthenticated

Una vez que se implementa la función, puedes ver la URL de la función en la propiedad url que se muestra en la consola o con el comando gcloud functions describe.

También puedes visitar esa URL de la función con el siguiente comando curl:

curl $(gcloud functions describe randomgen --format='value(url)')

La función está lista para el flujo de trabajo.

5. Implementa la segunda Cloud Function

La segunda función es un multiplicador. Multiplica la entrada recibida por 2.

Crea un directorio para el código de la función y navega hasta él:

mkdir ~/multiply
cd ~/multiply

Crea un archivo main.py en el directorio con el siguiente contenido:

import random, json
from flask import jsonify

def multiply(request):
    request_json = request.get_json()
    output = {"multiplied":2*request_json['input']}
    return jsonify(output)

Cuando recibe una solicitud HTTP, esta función extrae el input del cuerpo JSON, lo multiplica por 2 y lo muestra en formato JSON al llamador.

Crea el mismo archivo requirements.txt en el mismo directorio con el siguiente contenido:

flask>=1.0.2

Implementa la función con un activador HTTP y con solicitudes no autenticadas permitidas con este comando:

gcloud functions deploy multiply \
    --runtime python312 \
    --trigger-http \
    --allow-unauthenticated

Una vez que se implemente la función, también podrás visitar esa URL con el siguiente comando curl:

curl $(gcloud functions describe multiply --format='value(url)') \
-X POST \
-H "content-type: application/json" \
-d '{"input": 5}'

La función está lista para el flujo de trabajo.

6. Cómo conectar dos funciones de Cloud Functions

En el primer flujo de trabajo, conecta las dos funciones.

Crea un archivo workflow.yaml con el siguiente contenido.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- returnResult:
    return: ${multiplyResult}

En este flujo de trabajo, obtienes un número aleatorio de la primera función y lo pasas a la segunda. El resultado es el número aleatorio multiplicado.

Implementa el primer flujo de trabajo:

gcloud workflows deploy workflow --source=workflow.yaml

Ejecuta el primer flujo de trabajo:

gcloud workflows execute workflow

Una vez que se ejecute el flujo de trabajo, puedes pasar el ID de ejecución que se proporcionó en el paso anterior para ver el resultado:

gcloud workflows executions describe <your-execution-id> --workflow workflow

El resultado incluirá result y state:

result: '{"body":{"multiplied":108},"code":200 ... } 

...
state: SUCCEEDED

7. Cómo conectar una API de HTTP externa

A continuación, conectarás math.js como un servicio externo en el flujo de trabajo.

En math.js, puedes evaluar expresiones matemáticas como esta:

curl https://api.mathjs.org/v4/?'expr=log(56)'

Esta vez, usarás la consola de Cloud para actualizar nuestro flujo de trabajo. Busca Workflows en la consola de Google Cloud:

7608a7991b33bbb0.png

Busca tu flujo de trabajo y haz clic en la pestaña Definition:

f3c8c4d3ffa49b1b.png

Edita la definición del flujo de trabajo y, luego, incluye una llamada a math.js.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- returnResult:
    return: ${logResult}

El flujo de trabajo ahora alimenta el resultado de la función multiplicar a una llamada de función de registro en math.js.

La IU te guiará para editar e implementar el flujo de trabajo. Una vez que se implemente, haz clic en Execute para ejecutar el flujo de trabajo. Verás los detalles de la ejecución:

b40c76ee43a1ce65.png

Observa el código de estado 200 y un body con el resultado de la función de registro.

Acabas de integrar un servicio externo en nuestro flujo de trabajo, ¡genial!

8. Implementa un servicio de Cloud Run

En la última parte, finaliza el flujo de trabajo con una llamada a un servicio privado de Cloud Run. Esto significa que el flujo de trabajo debe autenticarse para llamar al servicio de Cloud Run.

El servicio de Cloud Run muestra el math.floor del número pasado.

Crea un directorio para el código de servicio y navega hasta él:

mkdir ~/floor
cd ~/floor

Crea un archivo app.py en el directorio con el siguiente contenido:

import json
import logging
import os
import math

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_post():
    content = json.loads(request.data)
    input = float(content['input'])
    return f"{math.floor(input)}", 200

if __name__ != '__main__':
    # Redirect Flask logs to Gunicorn logs
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
    app.logger.info('Service started...')
else:
    app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

Cloud Run implementa contenedores, por lo que necesitas un Dockerfile y tu contenedor debe vincularse a la variable de entorno 0.0.0.0 y PORT, de ahí el código anterior.

Cuando recibe una solicitud HTTP, esta función extrae el input del cuerpo JSON, llama a math.floor y muestra el resultado al llamador.

En el mismo directorio, crea el siguiente Dockerfile:

# Use an official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.7-slim

# Install production dependencies.
RUN pip install Flask gunicorn

# Copy local code to the container image.
WORKDIR /app
COPY . .

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
CMD exec gunicorn --bind 0.0.0.0:8080 --workers 1 --threads 8 app:app

Compila el contenedor:

export SERVICE_NAME=floor
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

Una vez que se compile el contenedor, impleméntalo en Cloud Run. Observa la marca no-allow-unauthenticated. Esto garantiza que el servicio solo acepte llamadas autenticadas:

gcloud run deploy ${SERVICE_NAME} \
  --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
  --platform managed \
  --no-allow-unauthenticated

Una vez implementado, el servicio está listo para el flujo de trabajo.

9. Conecta el servicio de Cloud Run

Para poder configurar Workflows para que llame al servicio privado de Cloud Run, debes crear una cuenta de servicio para que la use Workflows:

export SERVICE_ACCOUNT=workflows-sa
gcloud iam service-accounts create ${SERVICE_ACCOUNT}

Otorga el rol run.invoker a la cuenta de servicio. Esto permitirá que la cuenta de servicio llame a los servicios autenticados de Cloud Run:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role "roles/run.invoker"

Actualiza la definición del flujo de trabajo en workflow.yaml para incluir el servicio de Cloud Run. Observa que también incluyes el campo auth para asegurarte de que Workflows pase el token de autenticación en sus llamadas al servicio de Cloud Run:

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- floorFunction:
    call: http.post
    args:
        url: https://floor-<random-hash>.run.app
        auth:
            type: OIDC
        body:
            input: ${logResult.body}
    result: floorResult
- returnResult:
    return: ${floorResult}

Actualiza el flujo de trabajo. Esta vez, pasa la cuenta de servicio:

gcloud workflows deploy workflow \
    --source=workflow.yaml \
    --service-account=${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

Ejecuta el flujo de trabajo:

gcloud workflows execute workflow

En unos segundos, puedes consultar la ejecución del flujo de trabajo para ver el resultado:

gcloud workflows executions describe <your-execution-id> --workflow workflow

El resultado incluirá un número entero result y state:

result: '{"body":"5","code":200 ... } 

...
state: SUCCEEDED

10. ¡Felicitaciones!

Felicitaciones por completar el codelab.

Temas abordados

  • Conceptos básicos de los flujos de trabajo.
  • Cómo conectar Cloud Functions públicas con Workflows
  • Cómo conectar servicios privados de Cloud Run con Workflows
  • Cómo conectar APIs HTTP externas con Workflows