Workflows 教程(第 1 代)


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

目标

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

  1. 部署两项 Cloud Run 函数服务:第一个函数生成一个随机数字,然后将该数字传递给第二个函数(此函数会乘以该数字)。
  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. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the Artifact Registry, Cloud Build, Cloud Run 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. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  10. Make sure that billing is enabled for your Google Cloud project.

  11. Enable the Artifact Registry, Cloud Build, Cloud Run 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. 为 Workflows 创建服务账号,以供使用:

    export SERVICE_ACCOUNT=workflows-sa
    gcloud iam service-accounts create ${SERVICE_ACCOUNT}

  15. 如需允许服务账号调用经过身份验证的 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"

    PROJECT_ID 替换为您的 Google Cloud 项目 ID。

  16. 设置本教程中使用的默认位置:
    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 位置

部署第一项 Cloud Run 函数服务

收到 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. 使用 HTTP 触发器部署函数,并允许未经身份验证的访问:

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

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

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

    gcloud functions describe randomgen
  2. 您可以通过以下 curl 命令来试用该函数:

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

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

部署第二项 Cloud Run 函数服务

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

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

    mkdir ~/multiply
    cd ~/multiply
  2. 创建一个文件名为 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)
  3. 如需支持依赖于 Flask 进行 HTTP 处理,请为 pip 软件包管理器创建一个文本文件。为该文本文件指定文件名 requirements.txt 并添加以下内容:

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

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

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

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

    gcloud functions describe multiply
  2. 您可以通过以下 curl 命令来试用该函数:

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

    系统应会返回数字 10。

在工作流中连接两项 Cloud Run 函数服务

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

  1. 返回到您的主目录:

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

    - randomgen_function:
        call: http.get
        args:
            url: https://REGION-PROJECT_ID.cloudfunctions.net/randomgen
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: https://REGION-PROJECT_ID.cloudfunctions.net/multiply
            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: https://REGION-PROJECT_ID.cloudfunctions.net/randomgen
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: https://REGION-PROJECT_ID.cloudfunctions.net/multiply
            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 Run 函数服务相关联,并返回最终结果。

  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 \
        --platform managed \
        --no-allow-unauthenticated

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

在工作流中连接 Cloud Run 服务

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

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

    - randomgen_function:
        call: http.get
        args:
            url: https://REGION-PROJECT_ID.cloudfunctions.net/randomgen
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: https://REGION-PROJECT_ID.cloudfunctions.net/multiply
            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}
    

    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. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

删除教程资源

后续步骤