每日图片:实验 6 - 使用 Workflows 进行编排

1. 概览

在之前的实验中,您构建了 Pic-a-daily 应用的事件驱动版本,该版本使用 Google Cloud Storage 触发的 Cloud Function 作为图片分析服务、通过 Pub/Sub 使用 GCS 触发的 Cloud Run 容器作为缩略图服务,并使用 Eventarc 触发 Cloud Run 上的图片垃圾收集器服务。还有一个由 Cloud Scheduler 触发的 Collage 服务:

d93345bfc235f81e.png

在本实验中,您将创建应用的编排版本。您将使用 Workflows 来编排和调用服务,而不是让不同类型的事件在系统中流动,具体如下所示:

b763efcbf5589747.png

学习内容

  • App Engine
  • Cloud Firestore
  • Cloud Functions
  • Cloud Run
  • Workflows

2. 设置和要求

自定进度的环境设置

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

96a9c957bc475304.pngb9a10ebdf5b5a448.pnga1e3c01a38fa61c2.png

请记住项目 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 图标:

bce75f34b2c53987.png

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

f6ef2b5f13479f3a.png

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

3. 工作流简介

90fcd42d556e310e.jpeg

您可以使用 Workflows 创建无服务器工作流,以按照您定义的顺序将一系列无服务器任务关联起来。您可以搭配使用 Google Cloud API 和无服务器函数(如 Cloud Functions 和 Cloud Run)的强大功能,调用外部 API 以创建灵活的无服务器应用。

正如您对编排器的预期一样,Workflows 允许您使用基于 YAML/JSON 的工作流定义语言定义业务逻辑的流程,并提供 Workflows Execution API 和 Workflows 界面来触发这些流程。

除了编排器之外,它还具有以下内置和可配置的功能:

  • 在各个步骤之间实现灵活的重试和错误处理,以确保可靠地执行步骤。
  • JSON 解析和步骤之间的变量传递,以避免粘合代码。
  • 用于决策的表达式公式支持条件式步骤执行。
  • 用于模块化和可重复使用的工作流的子工作流。
  • 对外部服务的支持可实现对 Google Cloud 之外的服务的编排。
  • 支持对 Google Cloud 和外部服务进行身份验证,以实现安全的步骤执行。
  • 连接器可连接到 Pub/Sub、Firestore、Tasks、Secret Manager 等 Google Cloud 服务,以便更轻松地进行集成。

更不用说,Workflows 是一款全托管式无服务器产品。无需配置或扩缩服务器,只需为实际用量付费。

4. 启用 API

在本实验中,您将使用 Workflows 连接 Cloud Functions 和 Cloud Run 服务。您还将使用 App Engine、Cloud Build、Vision API 和其他服务。

在 Cloud Shell 中,确保所有必要的服务均已启用:

gcloud services enable \
  appengine.googleapis.com \
  cloudbuild.googleapis.com \
  cloudfunctions.googleapis.com \
  compute.googleapis.com \
  firestore.googleapis.com \
  run.googleapis.com \
  vision.googleapis.com \
  workflows.googleapis.com \

过一段时间后,您应该会看到操作成功完成:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

5. 获取代码

获取代码(如果您尚未在之前的 Codelab 中获取):

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

您将拥有以下与本实验相关的文件夹结构:

frontend
 |
workflows
 |
 ├── functions
 ├── |── trigger-workflow
 ├── |── vision-data-transform
 ├── services
 ├── |── collage
 ├── |── thumbnails
 ├── workflows.yaml

以下是相关文件夹:

  • frontend 包含我们将从实验 4 中重复使用的 App Engine 前端。
  • functions 包含为工作流创建的 Cloud Functions 函数。
  • services 包含为工作流修改的 Cloud Run 服务。
  • workflows.yaml 是工作流定义文件。

6. 探索工作流 YAML

workflows.yaml 通过一系列步骤定义了工作流。我们来详细了解一下。

在工作流开始时,系统会传入一些参数。它们将由触发工作流的两个 Cloud Functions 函数传入。我们稍后会介绍这些函数,但工作流就是这样开始的:

d44a5e18aa9d4660.png

在 YAML 中,您可以看到这些参数已分配给 init 步骤中的变量,例如触发事件的文件和存储桶名称,以及工作流将调用的某些 Cloud Functions 和 Cloud Run 服务的网址:

main:
  params: [args]
  steps:
    - init:
        assign:
          - file: ${args.file}
          - bucket: ${args.bucket}
          - gsUri: ${"gs://" + bucket + "/" + file}
          - projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
          - urls: ${args.urls}

接下来,工作流会检查事件类型。系统支持 2 种事件类型:object.finalize(在云存储桶中保存文件时发出)和 object.delete(在删除文件时发出)。如果不是上述任何一种情况,则会引发“不支持的事件”异常。

dd1f450983655619.png

以下是 YAML 工作流定义中用于检查文件存储事件类型的步骤:

    - eventTypeSwitch:
        switch:
            - condition: ${args.eventType == "google.storage.object.finalize"}
              next: imageAnalysisCall
            - condition: ${args.eventType == "google.storage.object.delete"}
              next: pictureGarbageCollectionGCS
    - eventTypeNotSupported:
        raise: ${"eventType " + args.eventType + " is not supported"}
        next: end

请注意,Workflows 如何通过 switch 指令和各种条件支持 switch 语句和异常处理,以及如何通过 raise 指令在无法识别事件时引发错误。

接下来,我们来看看 imageAnalysisCall。这是一系列从工作流到 Vision API 的调用,用于分析图片、转换 Vision API 响应数据以对图片中识别的事物进行标签排序、选择主色调、检查图片是否可以安全显示,然后将元数据保存到 Cloud Firestore。

请注意,除了 Vision Transform Cloud Functions(我们稍后会部署)之外,所有操作都在 Workflows 中完成:

ca2ad16b9cbb436.png

以下是 YAML 格式的步骤:

    - imageAnalysisCall:
        call: http.post
        args:
          url: https://vision.googleapis.com/v1/images:annotate
          headers:
            Content-Type: application/json
          auth:
            type: OAuth2
          body:
            requests:
            - image:
                source:
                  gcsImageUri: ${gsUri}
              features:
              - type: LABEL_DETECTION
              - type: SAFE_SEARCH_DETECTION
              - type: IMAGE_PROPERTIES
        result: imageAnalysisResponse
    - transformImageAnalysisData:
        call: http.post
        args:
          url: ${urls.VISION_DATA_TRANSFORM_URL}
          auth:
            type: OIDC
          body: ${imageAnalysisResponse.body}
        result: imageMetadata
    - checkSafety:
        switch:
          - condition: ${imageMetadata.body.safe == true}
            next: storeMetadata
        next: end
    - storeMetadata:
        call: http.request
        args:
          url: ${"https://firestore.googleapis.com/v1/projects/" + projectId + "/databases/(default)/documents/pictures/" + file + "?updateMask.fieldPaths=color&updateMask.fieldPaths=labels&updateMask.fieldPaths=created"}
          auth:
            type: OAuth2
          method: PATCH
          body:
            name: ${"projects/" + projectId + "/databases/(default)/documents/pictures/" + file}
            fields:
              color:
                stringValue: ${imageMetadata.body.color}
              created:
                timestampValue: ${imageMetadata.body.created}
              labels:
                arrayValue:
                  values: ${imageMetadata.body.labels}
        result: storeMetadataResponse

分析完图片后,接下来的两个步骤是创建图片的缩略图和最新图片的拼贴。为此,请部署 2 个 Cloud Run 服务,并从 thumbnailCall 和 collageCall 步骤中调用这些服务:

76f9179323c3144.png

YAML 中的步骤:

   - thumbnailCall:
        call: http.post
        args:
          url: ${urls.THUMBNAILS_URL}
          auth:
            type: OIDC
          body:
              gcsImageUri: ${gsUri}
        result: thumbnailResponse
    - collageCall:
        call: http.get
        args:
          url: ${urls.COLLAGE_URL}
          auth:
            type: OIDC
        result: collageResponse

此执行分支通过返回 finalizeCompleted 步骤中每个服务的状态代码来结束:

    - finalizeCompleted:
        return:
          imageAnalysis: ${imageAnalysisResponse.code}
          storeMetadata: ${storeMetadataResponse.code}
          thumbnail: ${thumbnailResponse.code}
          collage: ${collageResponse.code}

执行的另一个分支是当从主存储桶(包含图片的高分辨率版本)中删除文件时。在此分支中,我们希望删除包含缩略图的存储桶中的图片缩略图,并从 Firestore 中删除其元数据。这两项操作均通过 Workflows 发出的 HTTP 调用完成:

f172379274dcb3c2.png

YAML 中的步骤:

    - pictureGarbageCollectionGCS:
        try:
          call: http.request
          args:
            url: ${"https://storage.googleapis.com/storage/v1/b/thumbnails-" + projectId + "/o/" + file}
            auth:
              type: OAuth2
            method: DELETE
          result: gcsDeletionResult
        except:
          as: e
          steps:
              - dummyResultInOutVar:
                  assign:
                      - gcsDeletionResult:
                          code: 200
                          body: "Workaround for empty body response"
    - pictureGarbageCollectionFirestore:
        call: http.request
        args:
          url: ${"https://firestore.googleapis.com/v1/projects/" + projectId + "/databases/(default)/documents/pictures/" + file}
          auth:
            type: OAuth2
          method: DELETE
        result: firestoreDeletionResult

删除分支通过返回每个步骤的结果 / 代码来结束:

    - deleteCompleted:
        return:
          gcsDeletion: ${gcsDeletionResult}
          firestoreDeletion: ${firestoreDeletionResult.code}

在以下步骤中,我们将创建工作流的所有外部依赖项:存储桶、Cloud Functions 函数、Cloud Run 服务和 Firestore 数据库。

7. 创建存储桶

您需要 2 个存储桶来存储图片:一个用于保存原始高分辨率图片,另一个用于保存图片的缩略图。

使用 gsutil 工具创建一个具有统一访问权限的公共区域(在本例中为欧洲)存储桶,供用户向其中上传图片:

export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
gsutil mb -l EU gs://${BUCKET_PICTURES}
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}

再创建一个公开的区域级存储桶来存放缩略图:

export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
gsutil mb -l EU gs://${BUCKET_THUMBNAILS}
gsutil uniformbucketlevelaccess set on gs://${BUCKET_THUMBNAILS}
gsutil iam ch allUsers:objectViewer gs://${BUCKET_THUMBNAILS}

您可以访问 Cloud 控制台的 Cloud Storage 部分,仔细检查存储桶是否已创建并设为公开:

15063936edd72f06.png

8. Vision Data Transform(云函数)

Workflows.yaml 以 init、eventTypeSwitch、eventTypeNotSupported 步骤开头。这些变量可确保来自分桶的事件路由到正确的步骤。

对于 object.finalize 事件,imageAnalysisCall 步骤会调用 Vision API 来提取所创建图片的元数据。所有这些步骤都在工作流中完成:

daaed43a22d2b0d3.png

接下来,我们需要转换从 Vision API 返回的数据,然后才能将其保存到 Firestore。更具体地说,我们需要:

  • 列出为图片返回的标签。
  • 检索图片的主色。
  • 确定图片是否安全。

此操作在 Cloud Functions 中的代码中完成,而工作流只需调用此函数:

5e120e70c67779cd.png

探索代码

Cloud Functions 函数被调用 vision-data-transform。您可以在 index.js 中查看其完整代码。如您所见,此函数的唯一目的是执行 JSON 到 JSON 的转换,以便在 Firestore 中方便地存储图片元数据。

部署到 Cloud Functions

前往相应文件夹:

cd workflows/functions/vision-data-transform/nodejs

设置您选择的区域:

export REGION=europe-west1
gcloud config set functions/region ${REGION}

使用以下命令部署函数:

export SERVICE_NAME=vision-data-transform
gcloud functions deploy ${SERVICE_NAME} \
  --source=. \
  --runtime nodejs10 \
  --entry-point=vision_data_transform \
  --trigger-http \
  --allow-unauthenticated

部署函数后,工作流的 transformImageAnalysisData 步骤将能够调用此函数来执行 Vision API 数据转换。

9. 准备数据库

工作流中的下一步是从图片数据中检查图片的安全性,然后将 Vision API 返回的图片相关信息存储到 Cloud Firestore 数据库中。Cloud Firestore 是一种快速、全代管式、无服务器、云原生的 NoSQL 文档数据库:

6624c616bc7cd97f.png

这两项操作均在 Workflows 中完成,但您需要先创建 Firestore 数据库,才能存储元数据。

首先,在您希望 Firestore 数据库所在的区域中创建 App Engine 应用(这是使用 Firestore 的前提条件):

export REGION_FIRESTORE=europe-west2
gcloud app create --region=${REGION_FIRESTORE}

接下来,在同一区域中创建 Firestore 数据库:

gcloud firestore databases create --region=${REGION_FIRESTORE}

这些文档将以编程方式在我们的集合中创建,并且将包含 4 个字段:

  • name(字符串):上传的照片的文件名,也是相应文档的键
  • 标签(字符串数组):Vision API 识别出的商品的标签
  • 颜色(字符串):主色的十六进制颜色代码(例如 #ab12ef)
  • created(日期):相应图片元数据的存储时间戳
  • thumbnail(布尔值):一个可选字段,如果已为此图片生成缩略图,则此字段会显示并为 true

由于我们将在 Firestore 中搜索有缩略图的图片,并按创建日期进行排序,因此需要创建搜索索引。您可以使用以下命令创建索引:

gcloud firestore indexes composite create --collection-group=pictures \
  --field-config field-path=thumbnail,order=descending \
  --field-config field-path=created,order=descending

请注意,创建索引可能需要 10 分钟左右。

创建索引后,您可以在 Cloud 控制台中看到它:

43af1f5103bf423.png

工作流 storeMetadata 步骤现在能够将图片元数据存储到 Firestore 中。

10. 缩略图服务 (Cloud Run)

链中的下一个步骤是创建图片缩略图。这是在 Cloud Run 服务中的代码中完成的,而工作流会在 thumbnailCall 步骤中调用此服务:

84d987647f082b53.png

探索代码

Cloud Run 服务名为 thumbnails。您可以在 index.js 中查看其完整代码。

构建并发布容器映像

Cloud Run 会运行容器,但您首先需要构建容器映像(在 Dockerfile 中定义)。Google Cloud Build 可用于构建容器映像,然后将其托管到 Google Container Registry。

前往相应文件夹:

cd workflows/services/thumbnails/nodejs

构建:

export SERVICE_SRC=thumbnails
export SERVICE_NAME=${SERVICE_SRC}-service
gcloud builds submit \
  . \
  --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

一两分钟后,构建应该会成功,并且容器将部署到 Google Container Registry。

部署到 Cloud Run

设置一些所需的变量和配置:

export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
export REGION=europe-west1
gcloud config set run/region ${REGION}
gcloud config set run/platform managed

使用以下命令进行部署:

gcloud run deploy ${SERVICE_NAME} \
    --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
    --no-allow-unauthenticated \
    --memory=1Gi \
    --update-env-vars BUCKET_THUMBNAILS=${BUCKET_THUMBNAILS}

服务部署完毕后,工作流 thumbnailCall 步骤将能够调用该服务。

11. 拼贴服务 (Cloud Run)

接下来,在链中创建近期照片的拼图。这是在 Cloud Run 服务中的代码中完成的,而工作流会在 collageCall 步骤中调用此服务:

591e36149066e1ba.png

探索代码

Cloud Run 服务名为 collage。您可以在 index.js 中查看其完整代码。

构建并发布容器映像

Cloud Run 会运行容器,但您首先需要构建容器映像(在 Dockerfile 中定义)。Google Cloud Build 可用于构建容器映像,然后将其托管到 Google Container Registry。

前往相应文件夹:

cd services/collage/nodejs

构建:

export SERVICE_SRC=collage
export SERVICE_NAME=${SERVICE_SRC}-service
gcloud builds submit \
  . \
  --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

一两分钟后,构建应该会成功,并且容器将部署到 Google Container Registry。

部署到 Cloud Run

设置一些所需的变量和配置:

export BUCKET_THUMBNAILS=thumbnails-${GOOGLE_CLOUD_PROJECT}
export REGION=europe-west1
gcloud config set run/region ${REGION}
gcloud config set run/platform managed

部署:

gcloud run deploy ${SERVICE_NAME} \
    --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
    --no-allow-unauthenticated \
    --memory=1Gi \
    --update-env-vars BUCKET_THUMBNAILS=${BUCKET_THUMBNAILS}

服务部署完成后,您可以在 Cloud Console 的“Cloud Run”部分中查看这两个服务是否都在运行,并且工作流 collageCall 步骤将能够调用此服务:

3ae9873f4cbbf423.png

12. 工作流部署

我们部署了 Workflows 的所有外部依赖项。其余所有步骤(finalizeCompleted、pictureGarbageCollectionGCS、pictureGarbageCollectionFirestore、deleteCompleted)都可以由 Workflows 本身完成。

现在可以部署工作流了!

前往包含 workflows.yaml 文件的文件夹,然后使用以下命令部署该文件:

export WORKFLOW_REGION=europe-west4
export WORKFLOW_NAME=picadaily-workflows
gcloud workflows deploy ${WORKFLOW_NAME} \
  --source=workflows.yaml \
  --location=${WORKFLOW_REGION}

几秒钟后,工作流应该会部署完毕,您可以在 Cloud 控制台的“工作流”部分中看到它:

94a720149e5df9c5.png

您可以点击工作流并根据需要进行修改。在编辑期间,您可以直观地了解工作流:

55441b158f6027f3.png

您还可以使用正确的参数通过 Cloud 控制台手动执行工作流。相反,我们将在下一步中自动执行该函数,以响应 Cloud Storage 事件。

13. 工作流触发器 (Cloud Functions)

工作流已部署并准备就绪。现在,我们需要在 Cloud Storage 存储桶中创建或删除文件时触发工作流。这些分别是 storage.object.finalize 和 storage.object.delete 事件。

Workflows 具有用于创建、管理和执行工作流的 API 和客户端库,您可以根据需要使用。在这种情况下,您将使用 Workflows Execution API,更具体地说,是使用其 Node.js 客户端库来触发工作流。

您将通过 Cloud Functions 触发工作流,而 Cloud Functions 会监听 Cloud Storage 事件。由于 Cloud Functions 函数只能监听一种事件类型,因此您需要部署两个 Cloud Functions 函数,分别监听创建和删除事件:

c4d79646de729e4.png

探索代码

Cloud Functions 函数被调用 trigger-workflow。您可以在 index.js 中查看其完整代码。

部署到 Cloud Functions

前往相应文件夹:

cd workflows/functions/trigger-workflow/nodejs

设置一些所需的变量和配置:

export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
export REGION=europe-west1
export WORKFLOW_NAME=picadaily-workflows
export WORKFLOW_REGION=europe-west4
export COLLAGE_URL=$(gcloud run services describe collage-service --format 'value(status.url)')
export THUMBNAILS_URL=$(gcloud run services describe thumbnails-service --format 'value(status.url)')
export VISION_DATA_TRANSFORM_URL=$(gcloud functions describe vision-data-transform --format 'value(httpsTrigger.url)')
gcloud config set functions/region ${REGION}

部署响应最终确定事件的函数:

export SERVICE_NAME=trigger-workflow-on-finalize
gcloud functions deploy ${SERVICE_NAME} \
  --source=. \
  --runtime nodejs10 \
  --entry-point=trigger_workflow \
  --trigger-resource=${BUCKET_PICTURES} \
  --trigger-event=google.storage.object.finalize \
  --allow-unauthenticated \
  --set-env-vars GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT},WORKFLOW_REGION=${WORKFLOW_REGION},WORKFLOW_NAME=${WORKFLOW_NAME},THUMBNAILS_URL=${THUMBNAILS_URL},COLLAGE_URL=${COLLAGE_URL},VISION_DATA_TRANSFORM_URL=${VISION_DATA_TRANSFORM_URL}

部署第二个响应删除事件的函数:

export SERVICE_NAME=trigger-workflow-on-delete
gcloud functions deploy ${SERVICE_NAME} \
  --source=. \
  --runtime nodejs10 \
  --entry-point=trigger_workflow \
  --trigger-resource=${BUCKET_PICTURES} \
  --trigger-event=google.storage.object.delete \
  --allow-unauthenticated \
  --set-env-vars GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT},WORKFLOW_REGION=${WORKFLOW_REGION},WORKFLOW_NAME=${WORKFLOW_NAME},THUMBNAILS_URL=${THUMBNAILS_URL},COLLAGE_URL=${COLLAGE_URL},VISION_DATA_TRANSFORM_URL=${VISION_DATA_TRANSFORM_URL}

部署完成后,您可以在 Cloud 控制台中看到这两个函数:

7d60c8b7851f39f5.png

14. 前端 (App Engine)

在此步骤中,您将基于 Pic-a-daily:实验 4 - 创建 Web 前端在 Google App Engine 上创建一个 Web 前端,让用户能够从 Web 应用上传图片,并浏览上传的图片及其缩略图。

223fb2281614d053.png

您可以参阅 Pic-a-daily:实验 4 - 创建 Web 前端,详细了解 App Engine 并阅读代码说明。

探索代码

App Engine 应用名为 frontend。您可以在 index.js 中查看其完整代码。

部署到 App Engine

前往相应文件夹:

cd frontend

设置您选择的区域,并将 app.yaml 中的 GOOGLE_CLOUD_PROJECT 替换为您的实际项目 ID:

export REGION=europe-west1
gcloud config set compute/region ${REGION}
sed -i -e "s/GOOGLE_CLOUD_PROJECT/${GOOGLE_CLOUD_PROJECT}/" app.yaml

部署:

gcloud app deploy app.yaml -q

一两分钟后,系统会告知您应用正在处理流量:

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 8 files to Google Cloud Storage                ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://GOOGLE_CLOUD_PROJECT.appspot.com]
You can stream logs from the command line by running:
  $ gcloud app logs tail -s default
To view your application in the web browser run:
  $ gcloud app browse

您还可以访问 Cloud 控制台的 App Engine 部分,查看应用是否已部署,并探索 App Engine 的功能,例如版本控制和流量拆分:

f4bd5f4de028bd83.png

15. 测试工作流

如需进行测试,请前往应用的默认 App Engine 网址 (https://<YOUR_PROJECT_ID>.appspot.com/),您应该会看到前端界面已启动并正在运行!

1649ac060441099.png

上传图片。这应会触发工作流,您可以在 Cloud 控制台中看到处于 Active 状态的工作流执行:

b5a2a3d7a2bc094.png

工作流完成后,您可以点击执行 ID,查看来自不同服务的输出:

8959df5098c21548.png

再上传 3 张照片。您还应该看到 Cloud Storage 存储桶和 App Engine 前端中的缩略图和图片拼贴已更新:

d90c786ff664a5dc.png

16. 清理(可选)

如果您不打算保留该应用,可以通过删除整个项目来清理资源,从而节省费用并践行良好的云资源管理实践:

gcloud projects delete ${GOOGLE_CLOUD_PROJECT} 

17. 恭喜!

您使用 Workflows 编排和调用服务,创建了应用的编排版本。

所学内容

  • App Engine
  • Cloud Firestore
  • Cloud Functions
  • Cloud Run
  • Workflows