有关使用 Workflows 进行无服务器编排的简介

1. 简介

c9b0cc839df0bb8f.png

您可以使用 Workflows 创建无服务器工作流,以便按照您定义的顺序将一系列无服务器任务关联在一起。您可以结合使用 Google Cloud 的 API、Cloud Functions 和 Cloud Run 等无服务器产品以及对外部 API 的调用,以创建灵活的无服务器应用。

Workflows 不需要管理基础架构,可以随需求无缝扩缩,包括缩减至零。由于采用的是按用量付费价格模式,您只需为执行时间付费。

在此 Codelab 中,您将学习如何使用 Workflows 连接各种 Google Cloud 服务和外部 HTTP API。更具体地说,您需要将两项公共 Cloud Functions 服务(一项专用 Cloud Run 服务和外部公共 HTTP API)连接到一个工作流中。

学习内容

  • Workflows 基础知识。
  • 如何将公共 Cloud Functions 与 Workflows 连接。
  • 如何将专用 Cloud Run 服务与 Workflows 连接。
  • 如何将外部 HTTP API 与 Workflows 连接。

2. 设置和要求

自定进度的环境设置

  1. 登录 Cloud 控制台,然后创建一个新项目或重复使用现有项目。 (如果您还没有 Gmail 或 G Suite 账号,则必须创建一个。)

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

请记住项目 ID,它在所有 Google Cloud 项目中都是唯一的名称(上述名称已被占用,您无法使用,抱歉!)。它稍后将在此 Codelab 中被称为 PROJECT_ID

  1. 接下来,您需要在 Cloud 控制台中启用结算功能,才能使用 Google Cloud 资源。

运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。请务必按照“清理”部分部分,其中会指导您如何关停资源,以免产生超出本教程范围的结算费用。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

在 GCP 控制台中,点击右上角工具栏上的 Cloud Shell 图标:

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。只需一个浏览器,即可完成本实验中的所有工作。

3. Workflows 概览

基础知识

工作流由使用基于 Workflows YAML 的语法描述的一系列步骤组成。这是工作流的定义。如需详细了解 Workflows YAML 语法,请参阅语法参考页面。

创建工作流后,系统会对其进行部署,这使得工作流为执行做好准备。执行是指工作流定义中包含的逻辑的单次运行。所有工作流执行都是独立的,并且该产品支持大量并发执行。

启用服务

在此 Codelab 中,您将通过 Workflows 连接 Cloud Functions(Cloud Run 服务)。您还将在服务构建期间使用 Cloud Build 和 Cloud Storage。

启用所有必要的服务:

gcloud services enable \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  workflows.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com

在下一步中,您将在一个工作流中连接两个 Cloud Functions 函数。

4. 部署第一个 Cloud Functions 函数

第一个函数是 Python 中的随机数生成器。

创建并导航到函数代码的目录:

mkdir ~/randomgen
cd ~/randomgen

在目录中创建一个包含以下内容的 main.py 文件:

import random, json
from flask import jsonify

def randomgen(request):
    randomNum = random.randint(1,100)
    output = {"random":randomNum}
    return jsonify(output)

收到 HTTP 请求时,此函数会生成一个介于 1 到 100 之间的随机数字,并以 JSON 格式将该值返回给调用方。

该函数依赖 Flask 进行 HTTP 处理,我们需要将其添加为依赖项。Python 中的依赖项可通过 pip 进行管理,并在名为 requirements.txt 的元数据文件中表示。

在同一目录中创建一个 requirements.txt 文件,其中包含以下内容:

flask>=1.0.2

使用 HTTP 触发器部署该函数,并使用以下命令允许未经身份验证的请求:

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

部署函数后,您可以在控制台或 gcloud functions describe 命令的 httpsTrigger.url 属性下看到函数的网址。

您还可以使用以下 curl 命令访问该函数的网址:

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

该函数已准备就绪,可以用于工作流。

5. 部署第二个 Cloud Functions 函数

第二个函数是乘数。它将收到的输入乘以 2。

创建并导航到函数代码的目录:

mkdir ~/multiply
cd ~/multiply

在目录中创建一个包含以下内容的 main.py 文件:

import random, json
from flask import jsonify

def multiply(request):
    request_json = request.get_json()
    output = {"multiplied":2*request_json['input']}
    return jsonify(output)

收到 HTTP 请求时,此函数会从 JSON 正文中提取 input,将其乘以 2,并以 JSON 格式返回给调用方。

在同一目录中创建相同的 requirements.txt 文件,其中包含以下内容:

flask>=1.0.2

使用 HTTP 触发器部署该函数,并使用以下命令允许未经身份验证的请求:

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

部署函数后,您还可以使用以下 curl 命令访问该函数的该网址:

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

该函数已准备就绪,可以用于工作流。

6. 连接两个 Cloud Functions 函数

在第一个工作流中,将这两个函数关联在一起。

创建一个包含以下内容的 workflow.yaml 文件。

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- returnResult:
    return: ${multiplyResult}

在此工作流中,您将从第一个函数获取一个随机数字,并将其传递给第二个函数。结果就是相乘的随机数。

部署第一个工作流:

gcloud workflows deploy workflow --source=workflow.yaml

执行第一个工作流:

gcloud workflows execute workflow

执行工作流后,您可以通过传入上一步中提供的执行 ID 来查看结果:

gcloud workflows executions describe <your-execution-id> --workflow workflow

输出将包含 resultstate

result: '{"body":{"multiplied":108},"code":200 ... } 

...
state: SUCCEEDED

7. 连接外部 HTTP API

接下来,您将连接 math.js 作为工作流中的外部服务。

math.js 中,您可以按如下所示对数学表达式求值:

curl https://api.mathjs.org/v4/?'expr=log(56)'

这次,您将使用 Cloud 控制台来更新我们的工作流。在 Google Cloud 控制台中找到 Workflows

7608a7991b33bbb0

找到您的工作流,然后点击 Definition 标签页:

f3c8c4d3ffa49b1b.png

修改工作流定义并添加对 math.js 的调用。

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- returnResult:
    return: ${logResult}

工作流现在将乘法函数的输出馈送到 math.js 中的日志函数调用。

界面将引导您修改和部署工作流。部署后,点击 Execute 以执行工作流。您将看到执行详情:

b40c76ee43a1ce65.png

请注意状态代码 200 和包含日志函数输出的 body

您刚刚将一项外部服务集成到了我们的工作流中,太棒了!

8. 部署 Cloud Run 服务

最后,通过调用专用 Cloud Run 服务完成工作流。这意味着工作流需要进行身份验证才能调用 Cloud Run 服务。

Cloud Run 服务返回传入的数值的 math.floor

创建并导航到服务代码的目录:

mkdir ~/floor
cd ~/floor

在目录中创建一个包含以下内容的 app.py 文件:

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

Cloud Run 会部署容器,因此您需要 Dockerfile,并且容器需要绑定到 0.0.0.0PORT 环境变量,因此需要编写上述代码。

收到 HTTP 请求时,此函数会从 JSON 正文中提取 input,调用 math.floor 并将结果返回给调用方。

在同一目录中,创建以下 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

构建容器。

export SERVICE_NAME=floor
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

构建容器后,将其部署到 Cloud Run。请注意 no-allow-unauthenticated 标志。这可确保服务仅接受经过身份验证的调用:

gcloud run deploy ${SERVICE_NAME} \
  --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
  --platform managed \
  --no-allow-unauthenticated

部署后,该服务便可供工作流使用。

9. 连接 Cloud Run 服务

您需要先创建一个服务账号以供 Workflows 使用,然后才能配置 Workflows 以调用专用 Cloud Run 服务:

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

run.invoker 角色授予服务账号。这将允许服务账号调用经过身份验证的 Cloud Run 服务:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role "roles/run.invoker"

更新 workflow.yaml 中的工作流定义,以包含 Cloud Run 服务。请注意,您还添加了 auth 字段,以确保 Workflows 在其对 Cloud Run 服务的调用中传入身份验证令牌:

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- floorFunction:
    call: http.post
    args:
        url: https://floor-<random-hash>.run.app
        auth:
            type: OIDC
        body:
            input: ${logResult.body}
    result: floorResult
- returnResult:
    return: ${floorResult}

更新工作流。这次传入服务账号:

gcloud workflows deploy workflow \
    --source=workflow.yaml \
    --service-account=${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

执行工作流:

gcloud workflows execute workflow

几秒钟后,您可以查看工作流执行情况以了解结果:

gcloud workflows executions describe <your-execution-id> --workflow workflow

输出将包括整数 resultstate

result: '{"body":"5","code":200 ... } 

...
state: SUCCEEDED

10. 恭喜!

恭喜您完成此 Codelab。

所学内容

  • Workflows 基础知识。
  • 如何将公共 Cloud Functions 与 Workflows 连接。
  • 如何将专用 Cloud Run 服务与 Workflows 连接。
  • 如何将外部 HTTP API 与 Workflows 连接。