Workflows te permite coordinar y automatizar servicios en Google Cloud. Aplicar las prácticas de arquitectura recomendadas te ayuda a mejorar la confiabilidad, optimizar los costos de ejecución y compilar organizaciones resilientes y fáciles de mantener.
Esta no es una lista exhaustiva de recomendaciones y no te enseña los conceptos básicos para usar Workflows. En este documento, se supone que ya tienes un conocimiento general del panorama de Google Cloud y de Workflows. Para obtener más información, consulta el Google Cloud Well-Architected Framework y la descripción general de Workflows.
Selecciona un patrón de comunicación óptimo
Cuando diseñes una arquitectura de microservicios para implementar varios servicios, puedes seleccionar 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 administración automatizadas (también conocidas como organización)
Asegúrate de considerar los beneficios y las desventajas de cada una de las opciones anteriores y selecciona un patrón óptimo para tu caso de uso. Por ejemplo, la comunicación directa entre servicios puede ser más sencilla de implementar que otras opciones, pero acopla de forma estrecha tus servicios. En cambio, una arquitectura basada en eventos te permite acoplar tus servicios con acoplamiento bajo; sin embargo, la supervisión y la depuración pueden ser más complicadas. Por último, un organizador central como Workflows, aunque es menos flexible, te permite coordinar la comunicación entre servicios sin el acoplamiento alto de la comunicación directa entre servicios o la complejidad de los eventos coreografiados.
También puedes combinar patrones de comunicación. Por ejemplo, en la organización basada en eventos, los servicios estrechamente relacionados se administran en una organización que se activa por un evento. Del mismo modo, puedes diseñar un sistema en el que una organización genere un mensaje de Pub/Sub para otro sistema organizado.
Sugerencias generales
Una vez que hayas decidido usar Workflows como tu organizador de servicios, ten en cuenta las siguientes sugerencias útiles.
Evita codificar URLs de forma fija
Puedes admitir flujos de trabajo que sean portátiles en varios entornos y más fáciles de mantener si evitas las URLs codificadas. Puedes lograrlo de las siguientes maneras:
Define las URLs como argumentos del entorno de ejecución.
Esto puede ser útil cuando se invoca tu flujo de trabajo a través de una biblioteca cliente o la API. (Sin embargo, esto no funcionará si tu flujo de trabajo se activa con un evento de Eventarc y el único argumento que se puede pasar 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, puedes 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 según el entorno en el que se implemente. O bien, crea un flujo de trabajo que se pueda reutilizar como una plantilla y configurar según las variables de entorno que se mantienen por separado.
Usa una técnica de sustitución que te permita crear un solo archivo de definición de flujo de trabajo, pero implementa variantes con una herramienta que reemplace los marcadores de posición en tu flujo de trabajo. Por ejemplo, puedes usar Cloud Build para implementar un flujo de trabajo y, en el archivo de configuración de Cloud Build, agregar un paso para reemplazar las URLs de marcador de posición en el 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']
Luego, puedes sustituir los valores de las variables en el momento de la 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 Envía una compilación a través de 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 con 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 se declaran variables en el módulo raíz de tu configuración, se les pueden asignar valores de varias maneras. Por ejemplo:
terraform apply -var="project_id=PROJECT_ID" -var="url1=URL_ONE" -var="url2=URL_TWO"
Usa el conector de Secret Manager para almacenar URLs de forma segura en Secret Manager y recuperarlas.
Usa pasos anidados
Cada flujo de trabajo debe tener al menos un paso.
De forma predeterminada, Workflows trata los pasos como si estuvieran en una lista ordenada
y los ejecuta uno por uno hasta que se hayan ejecutado todos. Lógicamente,
algunos pasos deben agruparse y puedes usar un bloque steps para anidar una
serie de pasos. Esto es conveniente, ya que te permite apuntar al 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]}
Expresiones de conclusión
Todas las expresiones deben comenzar con
un $ y estar entre llaves:
${EXPRESSION}Para evitar problemas de análisis de YAML, puedes encerrar las expresiones entre comillas. Por ejemplo, las expresiones que contienen dos puntos pueden causar un comportamiento inesperado cuando los dos puntos se interpretan como la definición de un mapa. Para resolver este problema, une la expresión YAML entre comillas simples:
'${"Name: " + myVar}'
También puedes usar expresiones que abarquen varias líneas. Por ejemplo, es posible que debas unir una consulta en SQL entre comillas cuando uses 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 Ejecuta varios trabajos de BigQuery en paralelo.
Usa llamadas declarativas
Usa Workflows para llamar a servicios desde el flujo de trabajo y controlar los resultados, y para ejecutar tareas simples, como realizar una llamada HTTP. Workflows puede invocar servicios, analizar respuestas y construir 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. Considera reemplazar los servicios que no tienen lógica empresarial por llamadas a la API declarativas y usa Workflows para abstraer la complejidad.
Sin embargo, debes crear servicios para realizar cualquier trabajo que sea demasiado complejo para Workflows, por ejemplo, implementar lógica empresarial reutilizable, procesamientos complejos o transformaciones que no sean compatibles con las expresiones de Workflows y su biblioteca estándar. Por lo general, un caso complicado es más fácil de implementar en el código, en lugar de usar YAML o JSON y la sintaxis de Workflows.
Almacena solo lo que necesitas
Mantén el consumo de memoria bajo control para no alcanzar los
límites de recursos ni recibir un error que lo indique,
como ResourceLimitError, MemoryLimitExceededError o
ResultSizeLimitExceededError.
Sé selectivo con lo que almacenas en las variables, filtrando y almacenando solo lo que necesitas. Si un servicio devuelve una carga útil demasiado grande, usa una función independiente para realizar la llamada y devolver solo lo que se requiere.
Puedes liberar memoria borrando variables. Por ejemplo, es posible que quieras liberar memoria que se necesita para los pasos posteriores. O bien, puedes tener llamadas con resultados que no te interesen y omitirlos por completo.
Puedes borrar una variable asignando null. En YAML, también puedes asignar un
valor vacío o ~ a una variable. Esto identifica la memoria que se puede recuperar de forma segura.
Ejemplo
- step: assign: - bigVar:
Usa flujos de trabajo secundarios y 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 rutina en 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 para 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 de Workflow Executions y la API de Workflows.
Usa conectores de Workflows
Workflows proporciona una serie de conectores que facilitan el acceso a otros productos de Google Cloud dentro de un flujo de trabajo. Los conectores simplifican las llamadas a los servicios, ya que controlan el formato de las solicitudes por ti, proporcionan métodos y argumentos para que no necesites conocer los detalles de una API deGoogle Cloud . Los conectores también tienen un comportamiento integrado para controlar los reintentos y las operaciones de larga duración, de modo que puedas evitar iterar y esperar a que se completen las llamadas. Los conectores se encargan de esto por ti.
Si necesitas llamar a una API de Google Cloud , primero verifica si existe un conector de Workflows para ella. Si no ves un conector para un producto de Google Cloud , puedes solicitarlo.
Obtén información para usar un conector y, para obtener una referencia detallada de los conectores disponibles, consulta la Referencia de conectores.
Ejecuta pasos de flujo de trabajo en paralelo
Si bien Workflows puede ejecutar pasos de forma secuencial, también puedes ejecutar pasos independientes en paralelo. En algunos casos, esto puede acelerar de forma significativa la ejecución de tu flujo de trabajo. Para obtener más información, consulta Ejecuta pasos del flujo de trabajo en paralelo.
Aplica reintentos y el patrón saga
Diseña flujos de trabajo que sean resilientes y puedan controlar las fallas de servicio transitorias y permanentes. Los errores de Workflows pueden generarse, por ejemplo, por solicitudes HTTP, funciones o conectores con errores, o bien por tu propio código de flujo de trabajo. Agrega manejo de errores y reintentos para que una falla en un paso no cause que falle todo el flujo de trabajo.
- Puedes generar errores personalizados
con la sintaxis
raise. - Puedes detectar errores con
un bloque
try/except. - Puedes reintentar pasos con un
bloque
try/retryy definir la cantidad máxima de intentos.
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 administrar la coherencia de los datos en los microservicios en situaciones de transacciones distribuidas. Una saga es una secuencia de transacciones que publica un evento para cada transacción y que activa la siguiente transacción. Si una transacción falla, la saga ejecuta transacciones de compensación que contrarrestan las fallas anteriores en la secuencia. Prueba el instructivo sobre reintentos y el patrón Saga en Workflows en GitHub.
Usa devoluciones de llamada para esperar
Las devoluciones de llamada permiten que las ejecuciones de flujos de trabajo esperen a que otro servicio realice una solicitud al extremo de devolución de llamada. Esa solicitud reanuda la ejecución del flujo de trabajo.
Con las devoluciones de llamada, puedes indicarle a tu flujo de trabajo que se produjo un evento específico y esperar ese evento sin sondeo. Por ejemplo, puedes crear un flujo de trabajo que te notifique cuando un producto vuelva a estar en stock o cuando se envíe 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 con devoluciones de llamada y activadores de Eventarc.
Organiza trabajos 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 administrar los servicios. Esto te permite combinar ventajas y aprovisionar y organizar de manera eficiente todo el proceso.
Batch es un servicio completamente administrado que te permite programar, poner en cola y ejecutar cargas de trabajo por lotes en instancias de máquinas virtuales (VM) de Compute Engine. Puedes usar el conector de Workflows para Batch con el objetivo de programar y ejecutar un trabajo por lotes. Para obtener más detalles, prueba el instructivo.
Los trabajos de Cloud Run se usan para ejecutar código que realiza un trabajo y se cierra cuando este se completa. Workflows te permite ejecutar trabajos de Cloud Run como parte de un flujo de trabajo para realizar procesamientos de datos más complejos, o bien organizar un sistema de trabajos existentes. Prueba el instructivo en el que se muestra cómo usar Workflows para ejecutar un trabajo de Cloud Run.
Alinea los tiempos de espera y los tamaños de las cargas útiles en todos los servicios
Cuando Workflows llama a servicios de Google Cloud descendentes,
las diferencias en los tiempos de espera de las solicitudes, los límites de respuesta HTTP y los límites de memoria pueden
provocar fallas de TimeoutError, ValueError o MemoryLimitExceededError.
Compara los
límites de recursos de Workflows
con los límites del servicio de destino y elige un patrón de integración que
coincida con la duración y el tamaño de la carga útil de tu carga de trabajo. Por ejemplo:
| Situación de integración | Límites de Workflows | Límites del servicio de destino | Patrón recomendado |
|---|---|---|---|
Llamadas HTTP síncronas
(http.get,
http.post,
http.request)
a servicios de Cloud Run o funciones de Cloud Run
|
|
Consulta Configura el tiempo de espera de las solicitudes para los servicios y Límites de recursos para Cloud Run. |
Para las cargas de trabajo que se ejecutan durante más de 30 minutos, no uses llamadas
En su lugar, ejecuta trabajos de Cloud Run con el conector de la API de Cloud Run Admin o usa un extremo de devolución de llamada asíncrono de Workflows. El conector de la API de Cloud Run Admin admite el
sondeo de operaciones de larga duración (LRO), que agota el tiempo de espera después de 30 minutos (1,800 segundos) de forma
predeterminada. Para los trabajos que se ejecutan durante más de 30 minutos, configura el
tiempo de espera de la tarea del trabajo de Cloud Run
(10
minutos de forma predeterminada, configurable hasta 168 horas o 7 días) y
|
Consultas de BigQuery y cargas útiles de API grandes
(googleapis.
y respuestas de REST)
|
|
Los resultados de las consultas de BigQuery y las cargas útiles de la API de REST pueden superar varios megabytes o gigabytes. Consulta Cuotas y límites de trabajos de consulta de BigQuery. |
Evita extraer conjuntos de resultados grandes en variables de flujo de trabajo. Usa la paginación de |
Aloja tareas de larga duración en contenedores
Puedes automatizar la ejecución de un contenedor de larga duración con Workflows y Compute Engine. Por ejemplo, puedes incluir en un contenedor una tarea de larga duración para que se pueda ejecutar en cualquier lugar y, luego, ejecutar el contenedor en una VM de Compute Engine durante la duración máxima de la ejecución de un 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. Esto te permite usar un servidor y ejecutar un contenedor, pero abstrae la complejidad de administrar ambos, y puede ser útil si tienes limitaciones de tiempo cuando usas un servicio como Cloud Run Functions o Cloud Run. Prueba el instructivo Contenedores de larga duración con Workflows y Compute Engine en GitHub.
Ejecuta herramientas de línea de comandos desde Workflows
Cloud Build es un servicio que ejecuta tus compilaciones en Google Cloud como una serie de pasos de compilación, en la que cada paso se ejecuta en un contenedor de Docker. La ejecución de pasos de compilación es similar a la ejecución de comandos en una secuencia de comandos.
Google Cloud CLI incluye las herramientas de línea de comandos de gcloud, bq y
kubectl, pero no hay una forma directa de ejecutar comandos de gcloud CLI
desde Workflows. Sin embargo, Cloud Build
proporciona imágenes de contenedor que incluyen gcloud CLI. Puedes ejecutar
comandos de gcloud CLI en esos contenedores desde un paso de Cloud Build
y puedes crear ese paso en Workflows con el
conector de Cloud Build.
Ejemplo
Ejecuta gcloud en un flujo de trabajo:
Run kubectl in a workflow:
Usa Terraform para crear tu flujo de trabajo
Terraform es una herramienta de infraestructura como código que te permite crear, cambiar y mejorar de forma predecible tu infraestructura de nube con código.
Puedes definir e implementar un flujo de trabajo con el recurso
google_workflows_workflow
de Terraform. Para obtener más información, consulta
Crea un flujo de trabajo con Terraform.
Para ayudarte a administrar y mantener flujos de trabajo grandes, puedes crear tu flujo de trabajo
en un archivo YAML independiente y, luego, importarlo a Terraform con la
función templatefile,
que lee un archivo en una ruta de acceso determinada y renderiza 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 flujos de trabajo secundarios, puedes
definir el flujo de trabajo principal y los secundarios en archivos separados, 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 cuando depuras un flujo de trabajo, todos los archivos YAML importados a través del archivo de configuración de Terraform se combinan y se implementan como un solo flujo de trabajo.
Implementa 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 confirmación nueva a un repositorio o cuando se inicia una solicitud de extracción, y, luego, ejecutar automáticamente una compilación cuando lleguen eventos nuevos.
Puedes usar un activador de Cloud Build para iniciar una compilación y, luego, implementar un flujo de trabajo desde un repositorio de Git de forma automática. Puedes configurar el activador para implementar tu flujo de trabajo en cualquier cambio en el repositorio de código fuente o implementar el flujo de trabajo solo cuando el cambio coincida con criterios específicos.
Este enfoque puede ayudarte a administrar el ciclo de vida de tu implementación. Por ejemplo, puedes implementar cambios en un flujo de trabajo en un entorno de etapa de pruebas, ejecutar pruebas en ese entorno y, luego, lanzar estos cambios de forma incremental en el entorno de producción. Para obtener más información, consulta Implementa un flujo de trabajo desde un repositorio de Git con Cloud Build.
Optimiza el uso
El costo de ejecutar un flujo de trabajo es mínimo. Sin embargo, para el uso de grandes volúmenes, aplica los siguientes lineamientos para optimizar el uso y disminuir el costo:
En lugar de usar dominios personalizados, asegúrate de que todas las llamadas a los servicios de Google Cloud usen
*.appspot.com,*.cloud.goog,*.cloudfunctions.neto*.run.apppara que se te facturen los pasos internos y no los externos.Aplica una política de reintentos personalizada que equilibre tus necesidades de latencia y confiabilidad con los costos. Los reintentos más frecuentes reducen la latencia y aumentan la confiabilidad, pero también pueden aumentar los costos.
Cuando uses conectores que esperan operaciones de larga duración, establece una política de sondeo personalizada que optimice la latencia para el costo. Por ejemplo, si esperas que una operación tarde más de una hora, es posible que quieras una política que, inicialmente, realice un sondeo después de un minuto en caso de una falla inmediata y, luego, cada 15 minutos.
Combina las asignaciones en un solo paso.
Evita el uso excesivo de pasos
sys.log. Considera usar el registro de llamadas en su lugar.Comprender qué operaciones se consideran un paso Las operaciones que no se consideran pasos por sí solas se cuentan cuando se usan dentro de un paso aplicable. Por ejemplo, lo siguiente cuenta como un paso:
- type_check: return: if(get_type((int("6"))) == integer, 1, 2)Las operaciones clave que se tienen en cuenta y las que no para el límite máximo de pasos se clasifican en la siguiente tabla:
Categoría Operación Se cuenta como un paso - Operaciones de datos: asignación y devolución de valores
- Controlar el flujo: saltos (
next), interruptores, iniciar un buclefory cada iteración de un buclefor - Llamadas: Invoca
sys.get_envo alguna otra función de biblioteca estándar, otro flujo de trabajo o un conector - Simultaneidad: Generación de subprocesos y ejecución paralela
- Manejo de errores: Cada bloque
raise,try,retryyexceptcuenta como un paso independiente, incluso si otras operaciones forman parte del mismo paso más grande.Por ejemplo, un paso que incluye un bloque
trycon una operación de llamada cuenta como tres pasos: uno para el paso principal, uno para el intento y uno para la llamada. Agregar un bloqueretryagrega tres pasos más (uno para el reintento, otro para el intento y otro para la llamada), lo que hace un total de seis pasos.
No se considera un paso - Cómo leer y escribir en
listas,
mapas y
variables
Aunque una búsqueda individual no agrega un paso adicional, un paso que contiene la búsqueda, por ejemplo, un paso
assign, cuenta como un paso. - Funciones auxiliares de expresión integradas específicas:
len(),int()yget_type() - Operaciones aritméticas y de comparación
- Concatenación de cadenas
- Operaciones booleanas
Resumen de prácticas recomendadas
En la siguiente tabla, se resumen las sugerencias generales y las prácticas recomendadas de este documento.