教程:将 Workflows 与 Cloud Run 和 Cloud Functions 搭配使用


本教程介绍如何使用 Workflows 将一系列服务关联在一起。通过连接使用 Cloud Functions (第 2 代) 的两项公共 HTTP 服务、外部 REST API 和专用 Cloud Run 服务,您可以创建灵活的无服务器应用。

目标

在本教程中,您将使用 Google Cloud CLI 创建一个单一工作流,一次连接一项服务:

  1. 部署两项 Cloud Functions 函数服务:第一个函数生成一个随机数字,然后将该数字传递给第二个函数(此函数会乘以该数字)。
  2. 使用 Workflows 将两个 HTTP 函数连接在一起。执行工作流并返回结果,然后将其传递给外部 API。
  3. 使用 Workflows 连接外部 HTTP API,该 API 会返回给定编号的 log。执行工作流并返回结果,然后将其传递给 Cloud Run 服务。
  4. 部署 Cloud Run 服务以仅允许经过身份验证的访问。该服务会返回给定数字的 math.floor
  5. 使用 Workflows 连接 Cloud Run 服务,执行整个工作流并返回最终结果。

下图简要展示了该过程并直观呈现了最终工作流:

Workflows 直观呈现

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

您的组织定义的安全限制条件可能会导致您无法完成以下步骤。如需了解相关问题排查信息,请参阅在受限的 Google Cloud 环境中开发应用

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 安装 Google Cloud CLI。
  3. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  4. 创建或选择 Google Cloud 项目

    • 创建 Google Cloud 项目:

      gcloud projects create PROJECT_ID

      PROJECT_ID 替换为您要创建的 Google Cloud 项目的名称。

    • 选择您创建的 Google Cloud 项目:

      gcloud config set project PROJECT_ID

      PROJECT_ID 替换为您的 Google Cloud 项目 名称。

  5. 确保您的 Google Cloud 项目已启用结算功能

  6. Enable the Artifact Registry, Cloud Build, Cloud Functions, Cloud Run, Cloud Storage, and Workflows APIs:

    gcloud services enable artifactregistry.googleapis.com cloudbuild.googleapis.com cloudfunctions.googleapis.com run.googleapis.com storage.googleapis.com workflows.googleapis.com
  7. 安装 Google Cloud CLI。
  8. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  9. 创建或选择 Google Cloud 项目

    • 创建 Google Cloud 项目:

      gcloud projects create PROJECT_ID

      PROJECT_ID 替换为您要创建的 Google Cloud 项目的名称。

    • 选择您创建的 Google Cloud 项目:

      gcloud config set project PROJECT_ID

      PROJECT_ID 替换为您的 Google Cloud 项目 名称。

  10. 确保您的 Google Cloud 项目已启用结算功能

  11. Enable the Artifact Registry, Cloud Build, Cloud Functions, Cloud Run, Cloud Storage, and Workflows APIs:

    gcloud services enable artifactregistry.googleapis.com cloudbuild.googleapis.com cloudfunctions.googleapis.com run.googleapis.com storage.googleapis.com workflows.googleapis.com
  12. 更新 Google Cloud CLI 组件:
    gcloud components update
  13. 如果您在 Cloud Shell 中运行命令,则您已经使用 gcloud CLI 进行了身份验证;否则,请使用您的账号登录:
    gcloud auth login
  14. 设置本教程中使用的默认位置:
    gcloud config set project PROJECT_ID
    export REGION=REGION
    gcloud config set functions/region ${REGION}
    gcloud config set run/region ${REGION}
    gcloud config set workflows/location ${REGION}
    

    REGION 替换为您选择的受支持的 Workflows 位置

  15. 如果您是项目创建者,则会被授予基本 Owner 角色 (roles/owner)。默认情况下,此 Identity and Access Management (IAM) 角色可提供完全访问大多数 Google Cloud 资源所需的权限,您可以跳过此步骤。

    如果您不是项目创建者,则必须向主账号授予项目的必需权限。例如,主账号可以是 Google 账号(针对最终用户)或服务账号(针对应用和计算工作负载)。如需了解详情,请参阅事件目标位置的角色和权限页面。

    所需权限

    如需获得完成本教程所需的权限,请让您的管理员为您授予项目的以下 IAM 角色:

    如需详细了解如何授予角色,请参阅管理访问权限

    您也可以通过自定义角色或其他预定义角色来获取所需的权限。

部署第一项 Cloud Functions 服务

收到 HTTP 请求后,此 HTTP 函数会生成一个介于 1 到 100 之间的随机数字,然后以 JSON 格式返回该数字。

  1. 创建名为 randomgen 的目录并切换到该目录:

    mkdir ~/randomgen
    cd ~/randomgen
    
  2. 创建一个文件名为 main.py 且包含以下 Python 代码的文本文件:

    import functions_framework
    import random
    from flask import jsonify
    
    @functions_framework.http
    def randomgen(request):
        randomNum = random.randint(1, 100)
        output = {"random": randomNum}
        return jsonify(output)
  3. 如需支持依赖于 Flask 进行 HTTP 处理,请为 pip 软件包管理器创建一个文本文件。为该文本文件指定文件名 requirements.txt 并添加以下内容:

    flask>=1.0.2
    functions-framework==3.0.0
  4. 为 Workflows 创建服务账号,以供使用:

    export SERVICE_ACCOUNT=workflows-sa
    gcloud iam service-accounts create ${SERVICE_ACCOUNT}
    
  5. 如需允许服务账号调用经过身份验证的 Cloud Run 服务,请向 Workflows 服务账号授予 run.invoker 角色:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member "serviceAccount:${SERVICE_ACCOUNT}@PROJECT_ID.iam.gserviceaccount.com" \
        --role "roles/run.invoker"
    
  6. 使用 HTTP 触发器部署函数,并允许未经身份验证的访问:

    gcloud functions deploy randomgen-function \
        --gen2 \
        --runtime python310 \
        --entry-point=randomgen \
        --trigger-http \
        --allow-unauthenticated

    部署该函数可能需要几分钟的时间。或者,您也可以使用 Google Cloud 控制台中的 Cloud Functions 界面来部署函数。

  7. 部署 randomgen 函数后,您可以确认 httpsTrigger.url 属性:

    gcloud functions describe randomgen-function \
        --gen2 \
        --format="value(serviceConfig.uri)"
  8. 保存网址。 在后续练习中,您需要将它添加到工作流源文件中。

  9. 您可以通过以下 curl 命令来试用该函数:

    curl $(gcloud functions describe randomgen-function \
        --gen2 \
        --format="value(serviceConfig.uri)")

    系统随机会生成一个数字并返回。

部署第二项 Cloud Functions 服务

收到 HTTP 请求后,此 HTTP 函数会从 JSON 正文中提取 input,将此数字乘以 2,然后以 JSON 格式返回结果。

  1. 返回到您的主目录:

    cd ~
    
  2. 创建名为 multiply 的目录并切换到该目录:

    mkdir ~/multiply
    cd ~/multiply
    
  3. 创建一个文件名为 main.py 且包含以下 Python 代码的文本文件:

    import functions_framework
    from flask import jsonify
    
    @functions_framework.http
    def multiply(request):
        request_json = request.get_json()
        output = {"multiplied": 2 * request_json['input']}
        return jsonify(output)
  4. 如需支持依赖于 Flask 进行 HTTP 处理,请为 pip 软件包管理器创建一个文本文件。为该文本文件指定文件名 requirements.txt 并添加以下内容:

    flask>=1.0.2
    functions-framework==3.0.0
  5. 使用 HTTP 触发器部署函数,并允许未经身份验证的访问:

    gcloud functions deploy multiply-function \
        --gen2 \
        --runtime python310 \
        --entry-point=multiply \
        --trigger-http \
        --allow-unauthenticated

    部署函数可能需要几分钟的时间。或者,您也可以使用 Google Cloud 控制台中的 Cloud Functions 界面来部署函数。

  6. 部署 multiply 函数后,您可以确认 httpsTrigger.url 属性:

    gcloud functions describe multiply-function \
        --gen2\
        --format="value(serviceConfig.uri)"
  7. 保存网址。 在后续练习中,您需要将它添加到工作流源文件中。

  8. 您可以通过以下 curl 命令来试用该函数:

    curl -X POST MULTIPLY_FUNCTION_URL \
        -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
        -H "Content-Type: application/json" \
        -d '{"input": 5}'
    

    系统应会返回数字 10。

在工作流中连接两项 Cloud Functions 服务

工作流由一系列使用 Workflows 语法描述的步骤组成,该语法可以采用 YAML 或 JSON 格式编写。这是工作流的定义。如需了解详细说明,请参阅语法参考文档页面。

  1. 返回到您的主目录:

    cd ~
    
  2. 创建一个文件名为 workflow.yaml 且包含以下内容的文本文件:

    - randomgen_function:
        call: http.get
        args:
            url: RANDOMGEN_FUNCTION_URL
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: MULTIPLY_FUNCTION_URL
            body:
                input: ${randomgen_result.body.random}
        result: multiply_result
    - return_result:
        return: ${multiply_result}
    

    此源文件会将两个 HTTP 函数关联在一起,并返回最终结果。

  3. 创建工作流后,可以进行部署,使其可以执行。

    gcloud workflows deploy WORKFLOW_NAME \
        --source=workflow.yaml

    WORKFLOW_NAME 替换为您的工作流的名称。

  4. 执行工作流:

    gcloud workflows run WORKFLOW_NAME
    

    执行是指单次运行工作流定义中包含的逻辑。所有工作流都会独立执行,并且 Workflows 的快速扩缩允许大量并发执行。

    执行工作流后,输出应类似于以下内容:

    result: '{"body":{"multiplied":120},"code":200,"headers":{"Alt-Svc":"h3-29=\":443\";
    ...
    startTime: '2021-05-05T14:17:39.135251700Z'
    state: SUCCEEDED
    ...
    

在工作流中连接公共 REST 服务

更新现有工作流并连接可以用来对数学表达式求值的公共 REST API (math.js)。例如 curl https://api.mathjs.org/v4/?'expr=log(56)'

请注意,您已经部署了工作流,现在您可以通过 Google Cloud 控制台中的“工作流”页面对其进行修改。

  1. 修改工作流的源文件并将其替换为以下内容:

    - randomgen_function:
        call: http.get
        args:
            url: RANDOMGEN_FUNCTION_URL
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: MULTIPLY_FUNCTION_URL
            body:
                input: ${randomgen_result.body.random}
        result: multiply_result
    - log_function:
        call: http.get
        args:
            url: https://api.mathjs.org/v4/
            query:
                expr: ${"log(" + string(multiply_result.body.multiplied) + ")"}
        result: log_result
    - return_result:
        return: ${log_result}
    

    这会将外部 REST 服务与 Cloud Functions 函数服务相关联,并返回最终结果。

  2. 部署修改后的工作流:

    gcloud workflows deploy WORKFLOW_NAME \
        --source=workflow.yaml

部署 Cloud Run 服务

部署 Cloud Run 服务,在收到 HTTP 请求后,该服务会从 JSON 正文中提取 input,计算其 math.floor,并返回结果。

  1. 创建名为 floor 的目录并切换到该目录:

    mkdir ~/floor
    cd ~/floor
    
  2. 创建一个文件名为 app.py 且包含以下 Python 代码的文本文件:

    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)))

  3. 在同一目录中,创建一个包含以下内容的 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

  4. 创建一个 Artifact Registry 标准制品库,您可以在其中存储您的 Docker 容器映像:

    gcloud artifacts repositories create REPOSITORY \
        --repository-format=docker \
        --location=${REGION}

    REPOSITORY 替换为制品库的唯一名称。

  5. 构建容器映像:

    export SERVICE_NAME=floor
    gcloud builds submit --tag ${REGION}-docker.pkg.dev/PROJECT_ID/REPOSITORY/${SERVICE_NAME}
    
  6. 将容器映像部署到 Cloud Run,确保其仅接受经过身份验证的调用:

    gcloud run deploy ${SERVICE_NAME} \
        --image ${REGION}-docker.pkg.dev/PROJECT_ID/REPOSITORY/${SERVICE_NAME}:latest \
        --no-allow-unauthenticated

当您看到服务网址时,表示部署完成。 在更新工作流定义时,您需要指定该网址。

在工作流中连接 Cloud Run 服务

更新现有工作流并指定 Cloud Run 服务的网址。

  1. 修改工作流的源文件并将其替换为以下内容:

    - randomgen_function:
        call: http.get
        args:
            url: RANDOMGEN_FUNCTION_URL
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: MULTIPLY_FUNCTION_URL
            body:
                input: ${randomgen_result.body.random}
        result: multiply_result
    - log_function:
        call: http.get
        args:
            url: https://api.mathjs.org/v4/
            query:
                expr: ${"log(" + string(multiply_result.body.multiplied) + ")"}
        result: log_result
    - floor_function:
        call: http.post
        args:
            url: CLOUD_RUN_SERVICE_URL
            auth:
                type: OIDC
            body:
                input: ${log_result.body}
        result: floor_result
    - create_output_map:
        assign:
          - outputMap:
              randomResult: ${randomgen_result}
              multiplyResult: ${multiply_result}
              logResult: ${log_result}
              floorResult: ${floor_result}
    - return_output:
        return: ${outputMap}
    
    • RANDOMGEN_FUNCTION_URL 替换为您的 randomgen 函数的网址。
    • MULTIPLY_FUNCTION_URL 替换为您的 multiply 函数的网址。
    • CLOUD_RUN_SERVICE_URL 替换为您的 Cloud Run 服务网址。

    这将连接工作流中的 Cloud Run 服务。请注意,auth 密钥可确保在调用 Cloud Run 服务时传递身份验证令牌。如需了解详情,请参阅通过工作流发出经过身份验证的请求

  2. 部署修改后的工作流:

    gcloud workflows deploy WORKFLOW_NAME \
        --source=workflow.yaml
  3. 执行最终工作流:

    gcloud workflows run WORKFLOW_NAME

    您应该会看到类似如下所示的输出:

    result: '{"Floor":{"body":"4","code":200
      ...
      "Log":{"body":"4.02535169073515","code":200
      ...
      "Multiply":{"body":{"multiplied":56},"code":200
      ...
      "Random":{"body":{"random":28},"code":200
      ...
    startTime: '2023-11-13T21:22:56.782669001Z'
    state: SUCCEEDED
    

恭喜!您已部署并执行一个将一系列服务连接在一起的工作流。

如需使用表达式、条件跳转、Base64 编码或解码、子工作流等创建更复杂的工作流,请参阅工作流语法参考文档标准库概览

清理

如果您为本教程创建了一个新项目,请删除项目。 如果您使用的是现有项目,想要保留此项目且不保留本教程中添加的任何更改,请删除为教程创建的资源

删除项目

若要避免产生费用,最简单的方法是删除您为本教程创建的项目。

要删除项目,请执行以下操作:

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除教程资源

后续步骤