Workflows 的最佳实践 (original) (raw)

使用 Workflows 编排服务时,您可以参考此处列出的最佳实践。

本文并未详尽列出所有建议,也不会教您如何使用 Workflows 的基础知识。本文档假定您已大致了解整体情况和 Workflows。 Google Cloud 如需了解详情,请参阅Google Cloud Well-Architected FrameworkWorkflows 概览

选择最佳通信模式

在设计用于部署多项服务的微服务架构时,您可以从以下通信模式中进行选择:

请务必考虑上述每个选项的优缺点,并为您的应用场景选择最佳模式。例如,与其他选项相比,服务到服务的直接通信可能更易于实现,但它会紧密耦合您的服务。相比之下,事件驱动型架构可让您松散耦合服务;不过,监控和调试可能会更加复杂。最后,像 Workflows 这样的中央编排器虽然灵活性较低,但可让您协调服务之间的通信,而无需服务到服务的直接通信的紧密耦合,也无需编排事件的复杂性。

您还可以组合使用通信模式。例如,在事件驱动的 编排中,密切相关的服务在由事件触发的编排中进行管理。同样, 您也可以设计一个系统,其中一个编排会生成发送给另一个 编排系统的 Pub/Sub 消息

常规提示

决定使用 Workflows 作为服务编排器后,请注意以下实用提示。

避免对网址进行硬编码

您可以避免对网址进行硬编码,从而支持可在多个环境中移植且更易于维护的工作流。您可以通过以下方式实现此目的:

示例

main:
params: [args]
steps:
- init:
assign:
- url1: ${args.urls.url1}
- url2: ${args.urls.url2}
运行工作流时,您可以指定网址。例如:
gcloud workflows run multi-env --data='{"urls":{"url1": "URL_ONE", "url2": "URL_TWO"}}'

示例

steps:
‐ id: 'replace-urls'
name: 'gcr.io/cloud-builders/gcloud'
entrypoint: bash
args:
- -c
- |
sed -i -e "sREPLACE_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']
然后,您可以在构建时替换变量值。例如:
gcloud builds submit --config cloudbuild.yaml \
--substitutions=_ENV=staging,_URL1="URL_ONE",_URL2="URL_TWO"
如需了解详情,请参阅通过 CLI 和 API 提交构建
或者,您可以使用 Terraform 来预配基础架构,并定义一个配置文件,该文件使用 输入变量为每个环境创建工作流。

示例

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}" })
}
在配置的根模块中声明变量后, 可以通过多种方式为其赋值。例如
terraform apply -var="project_id=PROJECT_ID" -var="url1=URL_ONE" -var="url2=URL_TWO"

使用嵌套步骤

每个工作流都必须至少包含一个步骤。 默认情况下,Workflows 会将步骤视为有序列表并逐个执行,直到所有步骤都运行完毕。从逻辑上讲,某些步骤应分组在一起,您可以使用 steps 块来嵌套一系列步骤。这很方便,因为它可以让您指向正确的原子步骤来处理一组步骤。

示例

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]}

封装表达式

所有 表达式 都必须以 a $ 开头,并用花括号括起来:

为避免 YAML 解析问题,您可以使用引号将表达式括起来。例如,当英文冒号被解读为定义某个映射时,包含英文冒号的表达式可能会导致意外行为。 要解决此问题,您可以使用英文单引号将 YAML 表达式括起来:

'${"Name: " + myVar}'

您还可以使用跨多行的表达式。例如,在使用 Workflows BigQuery 连接器时,您可能需要使用引号将 SQL 查询括起来。

示例

如需查看完整的工作流定义,请参阅并行运行多个 BigQuery 作业

使用声明式调用

使用 Workflows 从工作流本身调用服务并 处理结果,以及执行发出 HTTP 调用等简单任务,例如发出 HTTP 调用。Workflows 可以调用服务、解析响应以及为其他连接的服务构建输入。 调用服务可以避免产生额外调用、额外依赖项和调用服务的服务复杂性。考虑将不包含业务逻辑的服务替换为声明式 API 调用,并使用 Workflows 来抽象复杂性。

不过,您应该创建服务来完成任何对 Workflows 来说过于复杂 的工作;例如,实现 Workflows 表达式及其标准库不支持的 可重用业务逻辑、 复杂计算或转换。 与使用 YAML 或 JSON 和 Workflows 语法相比,在代码中实现复杂情况通常更容易。

仅存储所需内容

控制内存消耗,以免遇到资源限制或指示 此限制的错误,例如 ResourceLimitErrorMemoryLimitExceededErrorResultSizeLimitExceededError

有选择地存储变量中的内容,仅过滤和 存储所需内容。如果服务返回的载荷过大,请使用单独的函数为您进行调用,并仅返回所需内容。

您可以通过清除变量来释放内存。例如,您可能需要释放后续步骤所需的内存。或者,您可能有一些调用,其结果您并不关心,您可以完全省略这些结果。

您可以通过分配 null 来清除变量。在 YAML 中,您还可以为变量分配空值或 ~。这会标识可以安全回收的内存。

示例

使用子工作流和外部工作流

您可以使用 子工作流 来 定义要多次调用的逻辑或一组步骤, 从而简化工作流定义。子工作流类似于编程语言中的函数或例程。它们可以接受参数和返回值,让您能够创建更复杂的工作流,并具有更广泛的应用范围。

请注意,子工作流是工作流定义本地的,无法在其他工作流中重复使用。不过,您可以从其他工作流调用工作流。 Workflows 连接器可以帮助您实现此目的。如需了解详情,请参阅 Workflow Executions API 和 Workflows API 的连接器概览。

使用 Workflows 连接器

Workflows 提供了许多 _连接器_,可让您更轻松地 访问工作流中的其他 Google Cloud 产品。连接器 会为您处理请求的格式,从而为您处理请求的格式设置并提供方法和参数,这样您就无需了解 Google Cloud API 的详细信息。连接器还具有用于处理重试长时间运行的操作的内置行为,因此您可以避免 迭代和等待调用完成;连接器会为您处理此问题。

如果您需要调用 Google Cloud API,请先查看是否存在适用于该 API 的 Workflows 连接器。如果您没有看到适用于某个产品的连接器,可以申请一个。 Google Cloud

了解如何使用连接器如需查看可用连接器的详细参考,请参阅连接器参考

并行运行工作流步骤

虽然 Workflows 可以按顺序运行步骤,但您也可以并行运行独立步骤。在某些情况下,这可以显著加快工作流执行速度。如需了解详情,请参阅并行执行工作流步骤

应用重试和 Saga 模式

设计具有弹性且可以处理暂时性和永久性服务故障的工作流。例如,失败的 HTTP 请求、函数、连接器或您自己的工作流代码可能会引发 Workflows 错误。添加错误处理和重试,以免一个步骤中的故障导致整个工作流失败。

某些业务交易会跨多个服务,因此您需要一种机制来实现跨服务的交易。Saga 设计模式是一种在分布式事务场景中管理微服务之间数据一致性的方法。Saga 是一系列交易,它会为每项交易发布一个事件,并触发下一项交易。如果交易失败,Saga 会执行补偿性交易,以抵消序列中之前的失败。试用 GitHub 上的Workflows 中的重试和 Saga 模式教程

使用回调进行等待

回调允许工作流 执行操作等待其他服务向_回调端点_发出请求;该请求将继续执行工作流。

借助回调,您可以告知工作流指定事件已 发生,然后等待该事件而不进行轮询。例如,您可以创建一个工作流,以便在产品再次 有货或商品发货时向您发出通知;或等待以进行人工互动,例如审核订单或验证翻译。您还可以使用回调和 Eventarc 触发器等待事件

编排长时间运行的作业

如果您需要执行长时间运行的批处理工作负载,可以使用 BatchCloud Run 作业,并使用 Workflows 管理服务。这样,您就可以结合使用各种优势,并高效地预配和编排整个流程。

Batch 是一项全代管式服务,可让您在 Compute Engine 虚拟机 (VM) 实例上安排、将批处理工作负载排入队列并执行批处理工作负载。您可以使用适用于 Batch 的 Workflows 连接器来安排和运行批量作业。如需了解详情,请试用教程

Cloud Run 作业用于运行执行工作(即作业)并在完成后退出的代码。借助 Workflows,您可以在工作流中执行 Cloud Run 作业,以执行更复杂的数据处理或编排现有作业的系统。试用该教程,了解如何使用 Workflows 执行 Cloud Run 作业。

容器化长时间运行的任务

您可以使用 Workflows 和 Compute Engine 自动执行长时间运行的容器。例如,您可以将长时间运行的任务容器化,使其能够在任何位置运行,然后在 Compute Engine 虚拟机上运行该容器,最长持续时间为工作流执行时间(一年)。

借助 Workflows,您可以自动创建虚拟机、在虚拟机上运行容器以及删除虚拟机。这样,您就可以使用服务器并运行容器,但它会抽象出管理两者的复杂性,如果您在使用 Cloud Run functions 或 Cloud Run 等服务时遇到时间限制,这可能会很有帮助。试用 GitHub 上的使用 Workflows 和 Compute Engine 的长时间运行的容器教程。

Cloud Build 是一项服务,可将构建作为一系列构建步骤执行,其中的每个构建步骤都在 Docker 容器中运行。 Google Cloud 执行构建步骤与在脚本中执行命令非常相似。

The Google Cloud CLI 包括 gcloudbqkubectl 命令行工具,但没有直接从 Workflows 运行 gcloud CLI 命令的方法。不过,Cloud Build 提供了包含 gcloud CLI 的容器映像。您可以从 Cloud Build 步骤在这些容器中运行 gcloud CLI 命令,并且可以使用Cloud Build 连接器在 Workflows 中创建该步骤。

示例

在工作流中运行 gcloud

Run kubectl in a workflow:

使用 Terraform 创建工作流

Terraform 是一种基础架构即代码工具 ,可让您使用代码以可预测的方式创建、更改和改进您的云基础架构 。

您可以使用 Terraformgoogle_workflows_workflow资源定义和部署工作流。如需了解详情,请参阅使用 Terraform 创建工作流

为了帮助您管理和维护大型工作流,您可以在单独的 YAML 文件中创建工作流 ,并使用templatefile函数将该文件导入 Terraform,该函数会读取给定路径中的文件并将其内容呈现为模板。

示例

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",{}) }

同样,如果您有一个调用多个子工作流的主工作流,则可以在单独的文件中定义主工作流和子工作流,并使用 templatefile 函数导入它们。

示例

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",{}
  )])

}

请注意,如果您在调试工作流时引用行号,则通过 Terraform 配置文件导入的所有 YAML 文件都会合并并部署为单个工作流。

从 Git 代码库部署工作流

Cloud Build 使用构建触发器来启用 CI/CD 自动化。您可以配置触发器以侦听传入的事件(例如,将新提交推送到代码库或者启动拉取请求时),然后在收到新事件时自动执行构建。

您可以使用 Cloud Build 构建触发器自动启动构建并从 Git 代码库部署工作流。您可以将触发器配置为在源代码库发生任何更改时部署工作流,或者仅当更改符合特定条件时才部署工作流。

这种方法可以帮助您管理部署生命周期。例如,您可以在暂存环境中部署对工作流的更改,针对该环境运行测试,然后将这些更改逐步发布到生产环境。如需了解详情,请参阅使用 Cloud Build 从 Git 代码库部署工作流

优化数据库用量

运行工作流的费用很 低。不过,对于大量使用,请遵循以下准则来优化使用量并降低费用:

- type_check:  
    return: if(get_type((int("6"))) == integer, 1, 2)  

下表列出了计入和不计入最大步骤数限制的关键操作:

类别 操作
计为一个步骤 数据操作:分配、返回值 控制流:跳转 (next)、切换、启动for 循环以及 for 循环的每次迭代 调用:调用 sys.get_env 或其他标准库 函数、其他工作流或连接器 并发:生成线程和并行执行 错误处理:每个 raise、try、retry 和 except 块都计为一个单独的 步骤,即使其他操作是同一较大步骤的一部分也是如此。例如,包含带有调用操作的 try 块的步骤计为三个步骤:一个用于主要步骤,一个用于 try,一个用于调用。添加 retry 块会再添加三个步骤(每个步骤分别用于重试、try 和调用),总共六个步骤。
不计为一个步骤 读取和写入列表映射变量 虽然单个查找不会添加额外的步骤,但包含查找的步骤 (例如assign步骤)计为一个步骤。 特定 内置表达式帮助函数:len()、int() 和get_type() 比较和算术运算 字符串 串联 布尔运算

最佳实践摘要

下表总结了本文档中建议的常规提示和最佳实践。

常规提示
避免对网址进行硬编码 使用嵌套步骤 封装表达式 使用声明式调用 仅存储所需内容 使用子工作流和外部工作流
最佳实践
使用 Workflows 连接器 并行运行工作流步骤 应用重试和 Saga 模式 使用回调进行等待 编排长时间运行的作业 容器化长时间运行的任务 从 Workflows 运行命令行工具 使用 Terraform 创建工作流 从 Git 代码库部署工作流 优化数据库用量

后续步骤