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

1. Introducción

c9b0cc839df0bb8f.png

Puedes usar Workflows 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, productos sin servidores como Cloud Functions y Cloud Run, y llamadas a APIs externas para crear aplicaciones flexibles sin servidores.

Los flujos de trabajo no requieren administración de infraestructura y escalan sin problemas con la demanda, incluida la reducción de escala 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. Específicamente, conectarás dos servicios públicos de Cloud Functions, uno privado de Cloud Run y una API de HTTP externa y pública, a un flujo de trabajo.

Qué aprenderás

  • Conceptos básicos de Workflows
  • Cómo conectar Cloud Functions públicas con Workflows
  • Cómo conectar servicios privados de Cloud Run con Workflows.
  • Cómo conectar APIs de 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 aconseja cómo cerrar recursos para no incurrir en facturación 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 consta de una serie de pasos descritos mediante la sintaxis basada en YAML de flujos de trabajo. Esta es la definición del flujo de trabajo. Para obtener una explicación detallada de la sintaxis de YAML de los flujos de trabajo, consulta la página Referencia de la sintaxis.

Cuando se crea un flujo de trabajo, se implementa, lo que hace que el flujo de trabajo esté listo para ejecutarse. 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 los flujos de trabajo son independientes y el producto admite una gran cantidad de ejecuciones simultáneas.

Habilitar servicios

En este codelab, conectarás Cloud Functions y servicios de Cloud Run con Workflows. 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 al azar 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 le muestra al emisor en formato JSON.

La función se basa en Flask para el procesamiento de HTTP y debemos agregarlo como 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 las solicitudes no autenticadas permitidas con este comando:

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

Una vez que se implemente la función, podrás ver su URL en la propiedad httpsTrigger.url que se muestra en la consola o con el comando gcloud functions describe.

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

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

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

5. Implementa una 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 al emisor en formato JSON.

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 las solicitudes no autenticadas permitidas con este comando:

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

Una vez que se implementa la función, también puedes visitar esa URL de la función con el siguiente comando curl:

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

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

6. Conecta dos 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 al azar de la primera función y lo pasas a la segunda función. 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 ejecutado el flujo de trabajo, puedes ver el resultado si pasas el ID de ejecución proporcionado en el paso anterior:

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

El resultado incluirá result y state:

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

...
state: SUCCEEDED

7. Conecta 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 de la siguiente manera:

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 e 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 envía el resultado de la función de multiplicación a una llamada a función de registro en math.js.

La IU te guiará para editar e implementar el flujo de trabajo. Una vez implementada, 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 a 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 que se pasó.

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 las variables de entorno 0.0.0.0 y PORT. Por eso, se usa el código anterior.

Cuando recibe una solicitud HTTP, esta función extrae el objeto input del cuerpo JSON, llama a math.floor y le 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 compilado 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

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

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}

Actualizar 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, podrás observar 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 Workflows
  • Cómo conectar Cloud Functions públicas con Workflows
  • Cómo conectar servicios privados de Cloud Run con Workflows.
  • Cómo conectar APIs de HTTP externas con Workflows.