Prácticas recomendadas para los flujos de trabajo

Puedes consultar las prácticas recomendadas que se indican aquí para orquestar tus servicios con Workflows.

Esta lista de recomendaciones no es exhaustiva y no te enseña los conceptos básicos de cómo usar Workflows. En este documento se presupone que ya tienes una idea general del panorama de Google Cloud y de Workflows. Para obtener más información, consulta el Google Cloud marco de trabajo Well-Architected y la descripción general de los flujos de trabajo.

Seleccionar un patrón de comunicación óptimo

Cuando diseñes una arquitectura de microservicios para desplegar varios servicios, puedes elegir entre los siguientes patrones de comunicación:

  • Comunicación directa entre servicios

  • Comunicación indirecta basada en eventos (también conocida como coreografía)

  • Configuración, coordinación y gestión automatizadas (también conocidas como orquestación)

Ten en cuenta las ventajas y desventajas de cada una de las opciones anteriores y selecciona el patrón óptimo para tu caso práctico. Por ejemplo, la comunicación directa entre servicios puede ser más sencilla de implementar que otras opciones, pero acopla estrechamente tus servicios. Por el contrario, una arquitectura basada en eventos te permite desacoplar tus servicios, pero la monitorización y la depuración pueden ser más complicadas. Por último, un orquestador central como Workflows, aunque es menos flexible, te permite coordinar la comunicación entre servicios sin el acoplamiento estrecho de la comunicación directa entre servicios ni la complejidad de los eventos coreografiados.

También puedes combinar patrones de comunicación. Por ejemplo, en la orquestación basada en eventos, los servicios estrechamente relacionados se gestionan en una orquestación que se activa mediante un evento. Del mismo modo, puedes diseñar un sistema en el que una orquestación dé como resultado un mensaje de Pub/Sub a otro sistema orquestado.

Consejos generales

Una vez que hayas decidido usar Workflows como orquestador de servicios, ten en cuenta los siguientes consejos útiles.

Evitar las URLs predefinidas

Para admitir flujos de trabajo que se puedan usar en varios entornos y que sean más fáciles de mantener, evite usar URLs preprogramadas. Puede hacerlo de las siguientes formas:

  • Define las URLs como argumentos de tiempo de ejecución.

    Esto puede ser útil cuando se invoca tu flujo de trabajo a través de una biblioteca de cliente o de la API. Sin embargo, esto no funcionará si tu flujo de trabajo se activa por un evento de Eventarc y el único argumento que se puede transferir es la carga útil del evento.

    Ejemplo

    main:
      params: [args]
      steps:
        - init:
            assign:
              - url1: ${args.urls.url1}
              - url2: ${args.urls.url2}

    Cuando ejecutes el flujo de trabajo, podrás especificar las URLs. Por ejemplo:

    gcloud workflows run multi-env --data='{"urls":{"url1": "URL_ONE", "url2": "URL_TWO"}}'
  • Usa variables de entorno y crea un flujo de trabajo que se configure de forma dinámica en función del entorno en el que se implemente. También puedes crear un flujo de trabajo que se pueda reutilizar como plantilla y configurar según las variables de entorno que se mantengan por separado.

  • Usa una técnica de sustitución que te permita crear un único archivo de definición de flujo de trabajo, pero implementa variantes con una herramienta que sustituya los marcadores de posición en tu flujo de trabajo. Por ejemplo, puedes usar Cloud Build para desplegar un flujo de trabajo y, en el archivo de configuración de Cloud Build, añadir un paso para sustituir las URLs de marcador de posición del flujo de trabajo.

    Ejemplo

    steps: id: 'replace-urls'
      name: 'gcr.io/cloud-builders/gcloud'
      entrypoint: bash
      args:
        - -c
        - |
          sed -i -e "s~REPLACE_url1~$_URL1~" workflow.yaml
          sed -i -e "s~REPLACE_url2~$_URL2~" workflow.yaml id: 'deploy-workflow'
      name: 'gcr.io/cloud-builders/gcloud'
      args: ['workflows', 'deploy', 'multi-env-$_ENV', '--source', 'workflow.yaml']

    Después, puedes sustituir los valores de las variables en tiempo de compilación. Por ejemplo:

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions=_ENV=staging,_URL1="URL_ONE",_URL2="URL_TWO"

    Para obtener más información, consulta Enviar una compilación mediante la CLI y la API.

    También puedes usar Terraform para aprovisionar tu infraestructura y definir un archivo de configuración que cree flujos de trabajo para cada entorno mediante variables de entrada.

    Ejemplo

    variable "project_id" {
      type = string
    }
    
    variable "url1" {
      type = string
    }
    
    variable "url2" {
      type = string
    }
    
    locals {
      env = ["staging", "prod"]
    }
    
    # Define and deploy staging and production workflows
    resource "google_workflows_workflow" "multi-env-workflows" {
      for_each = toset(local.env)
    
      name            = "multi-env-${each.key}"
      project         = var.project_id
      region          = "us-central1"
      source_contents = templatefile("${path.module}/workflow.yaml", { url1 : "${var.url1}-${each.key}", url2 : "${var.url2}-${each.key}" })
    }

    Cuando las variables se declaran en el módulo raíz de tu configuración, se les pueden asignar valores de varias formas. por ejemplo:

    terraform apply -var="project_id=PROJECT_ID" -var="url1=URL_ONE" -var="url2=URL_TWO"
  • Usa el conector Secret Manager para almacenar URLs de forma segura en Secret Manager y recuperarlas.

Usar pasos anidados

Todos los flujos de trabajo deben tener al menos un paso. De forma predeterminada, Workflows trata los pasos como si estuvieran en una lista ordenada y los ejecuta uno a uno hasta que se hayan completado todos. Lógicamente, algunos pasos deben agruparse y puedes usar un bloque steps para anidar una serie de pasos. Esto es muy útil, ya que te permite señalar el paso atómico correcto para procesar un conjunto de pasos.

Ejemplo

main:
    params: [input]
    steps:
    - callWikipedia:
        steps:
        - checkSearchTermInInput:
            switch:
                - condition: ${"searchTerm" in input}
                  assign:
                    - searchTerm: ${input.searchTerm}
                  next: readWikipedia
        - getCurrentDate:
            call: http.get
            args:
                url: https://timeapi.io/api/Time/current/zone?timeZone=Europe/Amsterdam
            result: currentDate
        - setFromCallResult:
            assign:
                - searchTerm: ${currentDate.body.dayOfWeek}
        - readWikipedia:
            call: http.get
            args:
                url: https://en.wikipedia.org/w/api.php
                query:
                    action: opensearch
                    search: ${searchTerm}
            result: wikiResult
    - returnOutput:
            return: ${wikiResult.body[1]}

Envolver expresiones

Todas las expresiones deben empezar por un $ y estar entre llaves:

${EXPRESSION}

Para evitar problemas de análisis de YAML, puede incluir las expresiones entre comillas. Por ejemplo, las expresiones que contienen dos puntos pueden provocar un comportamiento inesperado cuando los dos puntos se interpretan como la definición de un mapa. Para solucionar este problema, encierra la expresión YAML entre comillas simples:

'${"Name: " + myVar}'

También puedes usar expresiones que ocupen varias líneas. Por ejemplo, es posible que tengas que incluir una consulta de SQL entre comillas al usar el conector de BigQuery de Workflows.

Ejemplo

- runQuery:
    call: googleapis.bigquery.v2.jobs.query
    args:
        projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
        body:
            useLegacySql: false
            useQueryCache: false
            timeoutMs: 30000
            # Find top 100 titles with most views on Wikipedia
            query: ${
                "SELECT TITLE, SUM(views)
                FROM `bigquery-samples.wikipedia_pageviews." + table + "`
                WHERE LENGTH(TITLE) > 10
                GROUP BY TITLE
                ORDER BY SUM(VIEWS) DESC
                LIMIT 100"
                }
    result: queryResult

Para ver la definición completa del flujo de trabajo, consulta Ejecutar varias tareas de BigQuery en paralelo.

Usar llamadas declarativas

Usa Workflows para llamar a servicios desde el propio flujo de trabajo y gestionar los resultados, así como para ejecutar tareas sencillas, como hacer una llamada HTTP. Los flujos de trabajo pueden invocar servicios, analizar respuestas y crear entradas para otros servicios conectados. Llamar a un servicio te permite evitar las complicaciones de las invocaciones adicionales, las dependencias adicionales y los servicios que llaman a otros servicios. Te recomendamos que sustituyas los servicios que no tengan lógica empresarial por llamadas a APIs declarativas y que uses Workflows para abstraer la complejidad.

Sin embargo, debes crear servicios para realizar cualquier trabajo que sea demasiado complejo para Workflows, como implementar lógica empresarial reutilizable, cálculos complejos o transformaciones que no sean compatibles con las expresiones de Workflows y su biblioteca estándar. Normalmente, es más fácil implementar un caso complejo en código que usar YAML o JSON y la sintaxis de Workflows.

Almacena solo lo que necesites

Controla el consumo de memoria para no superar los límites de recursos ni recibir un error que lo indique, como ResourceLimitError, MemoryLimitExceededError o ResultSizeLimitExceededError.

Sé selectivo con lo que almacenes en variables, filtra y almacena solo lo que necesites. Si un servicio devuelve una carga útil demasiado grande, usa una función independiente para hacer la llamada y devuelve solo lo que se necesite.

Puedes liberar memoria borrando variables. Por ejemplo, puede que quieras liberar memoria que se necesite para pasos posteriores. También puedes omitir los resultados de las llamadas que no te interesen.

Puedes borrar una variable asignando null. En YAML, también puedes asignar un valor vacío o ~ a una variable. Identifica la memoria que se puede reclamar de forma segura.

Ejemplo

  - step:
      assign:
        - bigVar:

Usar subflujos de trabajo y flujos de trabajo externos

Puedes usar subflujos de trabajo para definir una parte de la lógica o un conjunto de pasos que quieras llamar varias veces, lo que simplifica la definición del flujo de trabajo. Los subflujos de trabajo son similares a una función o una rutina de un lenguaje de programación. Pueden aceptar parámetros y devolver valores, lo que te permite crear flujos de trabajo más complejos con una gama más amplia de aplicaciones.

Ten en cuenta que los subflujos de trabajo son locales en la definición de tu flujo de trabajo y no se pueden reutilizar en otros flujos de trabajo. Sin embargo, puedes llamar a flujos de trabajo desde otros flujos de trabajo. Los conectores de Workflows pueden ayudarte con esto. Para obtener más información, consulta las descripciones generales de los conectores de la API Workflow Executions y la API Workflows.

Usar conectores de Workflows

Workflows ofrece varios conectores que facilitan el acceso a otros productos de Google Cloud dentro de un flujo de trabajo. Los conectores simplifican las llamadas a servicios, ya que se encargan de dar formato a las solicitudes por ti. Además, proporcionan métodos y argumentos para que no tengas que conocer los detalles de una API deGoogle Cloud . Los conectores también tienen un comportamiento integrado para gestionar los reintentos y las operaciones de larga duración, de modo que no tengas que iterar ni esperar a que se completen las llamadas. Los conectores se encargan de todo.

Si necesitas llamar a una API Google Cloud , primero comprueba si existe un conector de Workflows para ella. Si no ves un conector para un producto, puedes solicitarlo. Google Cloud

Consulta cómo usar un conector y, para obtener una referencia detallada de los conectores disponibles, consulta la referencia de conectores.

Ejecutar pasos de flujo de trabajo en paralelo

Aunque los flujos de trabajo pueden ejecutar pasos de forma secuencial, también puedes ejecutar pasos independientes en paralelo. En algunos casos, esto puede acelerar significativamente la ejecución de tu flujo de trabajo. Para obtener más información, consulta Ejecutar pasos de flujo de trabajo en paralelo.

Aplicar reintentos y el patrón de saga

Diseña flujos de trabajo resilientes que puedan gestionar fallos de servicio transitorios y permanentes. Los errores de los flujos de trabajo pueden producirse, por ejemplo, por solicitudes HTTP fallidas, funciones, conectores o por el código de tu propio flujo de trabajo. Añade la gestión de errores y los reintentos para que un fallo en un paso no provoque que falle todo el flujo de trabajo.

Algunas transacciones empresariales abarcan varios servicios, por lo que necesitas un mecanismo para implementar transacciones que abarquen servicios. El patrón de diseño de saga es una forma de gestionar la coherencia de los datos en microservicios en escenarios de transacciones distribuidas. Una saga es una secuencia de transacciones que publica un evento por cada transacción y que activa la siguiente transacción. Si una transacción falla, el saga ejecuta transacciones de compensación que contrarrestan los errores anteriores de la secuencia. Consulta el tutorial sobre reintentos y el patrón Saga en Workflows en GitHub.

Usar retrollamadas para esperar

Las callbacks permiten que las ejecuciones de flujos de trabajo esperen a que otro servicio envíe una solicitud al endpoint de callback. Esta solicitud reanuda la ejecución del flujo de trabajo.

Con las retrollamadas, puedes indicar a tu flujo de trabajo que se ha producido un evento específico y esperar a que se produzca ese evento sin sondeos. Por ejemplo, puedes crear un flujo de trabajo que te avise cuando un producto vuelva a estar disponible o cuando se haya enviado un artículo, o que espere para permitir la interacción humana, como revisar un pedido o validar una traducción. También puedes esperar eventos mediante devoluciones de llamada y activadores de Eventarc.

Orquestar tareas de larga duración

Si necesitas ejecutar cargas de trabajo de procesamiento por lotes de larga duración, puedes usar Batch o trabajos de Cloud Run, y puedes usar Workflows para gestionar los servicios. De esta forma, puedes combinar las ventajas y aprovisionar y coordinar todo el proceso de forma eficiente.

Batch es un servicio totalmente gestionado que te permite programar, poner en cola y ejecutar cargas de trabajo por lotes en instancias de máquina virtual de Compute Engine. Puedes usar el conector Workflows para Batch para programar y ejecutar un trabajo de Batch. Para obtener más información, consulta el tutorial.

Las tareas de Cloud Run se usan para ejecutar código que realiza un trabajo y se cierra cuando este finaliza. Workflows te permite ejecutar trabajos de Cloud Run como parte de un flujo de trabajo para realizar un procesamiento de datos más complejo u orquestar un sistema de trabajos. Prueba el tutorial, que muestra cómo usar Workflows para ejecutar un trabajo de Cloud Run.

Crear contenedores para tareas de larga duración

Puedes automatizar la ejecución de un contenedor de larga duración con Workflows y Compute Engine. Por ejemplo, puedes contenerizar una tarea de larga duración para que se pueda ejecutar en cualquier lugar y, a continuación, ejecutar el contenedor en una VM de Compute Engine durante la duración máxima de una ejecución de flujo de trabajo (un año).

Con Workflows, puedes automatizar la creación de la VM, la ejecución del contenedor en la VM y la eliminación de la VM. De esta forma, puedes usar un servidor y ejecutar un contenedor, pero se abstrae la complejidad de gestionar ambos. Esto puede ser útil si tienes limitaciones de tiempo al usar un servicio como las funciones de Cloud Run o Cloud Run. Prueba el tutorial Contenedores de larga duración con Workflows y Compute Engine en GitHub.

Ejecutar herramientas de línea de comandos desde Workflows

Cloud Build es un servicio que ejecuta tus compilaciones Google Cloud como una serie de fases de compilación, donde cada fase se ejecuta en un contenedor Docker. Ejecutar pasos de compilación es análogo a ejecutar comandos en una secuencia de comandos.

La CLI de Google Cloud incluye las herramientas de línea de comandos gcloud, bq y kubectl, pero no hay ninguna forma directa de ejecutar comandos de la CLI de gcloud desde Workflows. Sin embargo, Cloud Build proporciona imágenes de contenedor que incluyen la CLI de gcloud. Puedes ejecutar comandos de la CLI de gcloud en esos contenedores desde un paso de Cloud Build y crear ese paso en Workflows mediante el conector de Cloud Build.

Ejemplo

Ejecuta gcloud en un flujo de trabajo:

# This example shows how to execute gcloud commands from Workflows
# using Cloud Build and returns the output

main:
  steps:
  - execute_command:
      call: gcloud
      args:
          args: "workflows list"
      result: result
  - return_result:
      return: ${result}

gcloud:
  params: [args]
  steps:
  - create_build:
      call: googleapis.cloudbuild.v1.projects.builds.create
      args:
        projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
        parent: ${"projects/" + sys.get_env("GOOGLE_CLOUD_PROJECT_ID") + "/locations/global"}
        body:
          serviceAccount: ${sys.get_env("GOOGLE_CLOUD_SERVICE_ACCOUNT_NAME")}
          options:
            logging: CLOUD_LOGGING_ONLY
          steps:
          - name: gcr.io/google.com/cloudsdktool/cloud-sdk
            entrypoint: /bin/bash
            args: ${["-c", "gcloud " + args + " > $$BUILDER_OUTPUT/output"]}
      result: result_builds_create
  - return_build_result:
      return: ${text.split(text.decode(base64.decode(result_builds_create.metadata.build.results.buildStepOutputs[0])), "\n")}

Run kubectl in a workflow:

# This example shows how to execute kubectl commands from Workflows
# using Cloud Build and returns the output

main:
  steps:
  - execute_command:
      call: kubectl
      args:
          args: "--help"
      result: result
  - return_result:
      return: ${result}

kubectl:
  params: [args]
  steps:
  - create_build:
      call: googleapis.cloudbuild.v1.projects.builds.create
      args:
        projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
        parent: ${"projects/" + sys.get_env("GOOGLE_CLOUD_PROJECT_ID") + "/locations/global"}
        body:
          serviceAccount: ${sys.get_env("GOOGLE_CLOUD_SERVICE_ACCOUNT_NAME")}
          options:
            logging: CLOUD_LOGGING_ONLY
          steps:
          - name: gcr.io/cloud-builders/kubectl
            entrypoint: /bin/bash
            args: ${["-c", "kubectl " + args + " > $$BUILDER_OUTPUT/output"]}
      result: result_builds_create
  - return_build_result:
      return: ${text.split(text.decode(base64.decode(result_builds_create.metadata.build.results.buildStepOutputs[0])), "\n")}

Usar Terraform para crear el flujo de trabajo

Terraform es una herramienta de infraestructura como código que te permite crear, cambiar y mejorar tu infraestructura de nube de forma predecible mediante código.

Puedes definir y desplegar un flujo de trabajo con el recurso de Terraform google_workflows_workflow. Para obtener más información, consulta el artículo sobre cómo crear un flujo de trabajo con Terraform.

Para ayudarte a gestionar y mantener flujos de trabajo grandes, puedes crear tu flujo de trabajo en un archivo YAML independiente e importar ese archivo en Terraform mediante la función templatefile, que lee un archivo en una ruta determinada y muestra su contenido como una plantilla.

Ejemplo

  # Define a workflow
  resource "google_workflows_workflow" "workflows_example" {
    name            = "sample-workflow"
    region          = var.region
    description     = "A sample workflow"
    service_account = google_service_account.workflows_service_account.id
    # Import main workflow YAML file
    source_contents = templatefile("${path.module}/workflow.yaml",{})
  }

Del mismo modo, si tienes un flujo de trabajo principal que llama a varios subflujos de trabajo, puedes definir el flujo de trabajo principal y los subflujos de trabajo en archivos independientes y usar la función templatefile para importarlos.

Ejemplo

  # Define a workflow
  resource "google_workflows_workflow" "workflows_example" {
    name            = "sample-workflow"
    region          = var.region
    description     = "A sample workflow"
    service_account = google_service_account.workflows_service_account.id
    # Import main workflow and subworkflow YAML files
    source_contents = join("", [
      templatefile(
        "${path.module}/workflow.yaml",{}
      ),

      templatefile(
        "${path.module}/subworkflow.yaml",{}
      )])
  }

Ten en cuenta que, si haces referencia a números de línea al depurar un flujo de trabajo, todos los archivos YAML importados a través del archivo de configuración de Terraform se combinarán y se implementarán como un único flujo de trabajo.

Desplegar un flujo de trabajo desde un repositorio de Git

Cloud Build usa activadores de compilación para habilitar la automatización de CI/CD. Puedes configurar activadores para que detecten eventos entrantes, como cuando se envía una nueva confirmación a un repositorio o cuando se inicia una solicitud de extracción, y, a continuación, ejecutar automáticamente una compilación cuando se produzcan nuevos eventos.

Puedes usar un activador de Cloud Build para iniciar automáticamente una compilación y desplegar un flujo de trabajo desde un repositorio de Git. Puedes configurar el activador para que despliegue tu flujo de trabajo a partir de cualquier cambio en el repositorio de origen o solo cuando el cambio cumpla determinados criterios.

Este enfoque puede ayudarte a gestionar el ciclo de vida de tu implementación. Por ejemplo, puedes desplegar cambios en un flujo de trabajo en un entorno de staging, ejecutar pruebas en ese entorno y, a continuación, lanzar estos cambios de forma incremental en el entorno de producción. Para obtener más información, consulta Desplegar un flujo de trabajo desde un repositorio de Git con Cloud Build.

Optimizar el uso

El coste de ejecutar un flujo de trabajo es mínimo. Sin embargo, si el volumen de uso es alto, aplica las siguientes directrices para optimizar el uso y reducir los costes:

  • En lugar de usar dominios personalizados, asegúrate de que todas las llamadas a servicios de Google Cloudusen *.appspot.com, *.cloud.goog, *.cloudfunctions.net o *.run.app para que se te cobren los pasos internos y no los externos.

  • Aplica una política de reintentos personalizada que equilibre tus necesidades de latencia y fiabilidad con los costes. Cuanto más frecuentes sean los reintentos, menor será la latencia y mayor la fiabilidad, pero también pueden aumentar los costes.

  • Cuando uses conectores que esperan operaciones de larga duración, define una política de sondeo personalizada que optimice la latencia en función del coste. Por ejemplo, si crees que una operación va a durar más de una hora, puedes usar una política que sondee inicialmente después de un minuto en caso de que se produzca un fallo inmediato y, después, cada 15 minutos.

  • Combina asignaciones en un solo paso.

  • Evita usar demasiados pasos sys.log. Prueba a usar el registro de llamadas.

Resumen de las prácticas recomendadas

En la siguiente tabla se resumen los consejos generales y las prácticas recomendadas que se indican en este documento.

Consejos generales
Prácticas recomendadas

Siguientes pasos