1. 總覽
在先前的研究室中,您建構了事件導向的 Pic-a-daily 應用程式,這個應用程式使用 Google Cloud Storage 觸發了圖片分析服務的 Cloud 函式。GCS 透過 Pub/Sub 觸發了 Cloud Run 容器,用於縮圖服務和 Eventarc,進而在 Cloud Run 上觸發影像垃圾收集服務。您也可以使用 Cloud Scheduler 觸發的美術拼貼服務:
在本研究室中,您將為應用程式建立自動化調度管理版本。您會使用 Workflows 自動化調度管理及呼叫服務,而不是透過系統傳送的不同事件類型:
課程內容
- App Engine
- Cloud Firestore
- Cloud Functions
- Cloud Run
- 工作流程
2. 設定和需求
自修環境設定
提醒您,專案 ID 是所有 Google Cloud 專案的專屬名稱 (已經有人使用上述名稱,很抱歉對您不符!)。稍後在本程式碼研究室中會稱為 PROJECT_ID
。
- 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Google Cloud 資源。
執行這個程式碼研究室並不會產生任何費用,如果有的話。請務必依照「清除所用資源」一節指示本節將說明如何關閉資源,這樣您就不會產生本教學課程結束後產生的費用。Google Cloud 的新使用者符合 $300 美元免費試用計畫的資格。
啟動 Cloud Shell
雖然 Google Cloud 可以從筆記型電腦遠端操作,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是一種在 Cloud 中執行的指令列環境。
在 GCP 控制台的右上方,按一下「Cloud Shell」圖示:
佈建並連線至環境的作業只需幾分鐘的時間。完成後,您應該會看到類似下方的內容:
這部虛擬機器都裝載了您需要的所有開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。這個研究室中的所有工作都可以透過瀏覽器完成。
3. Workflows 簡介
您可以使用 Workflows 建立無伺服器工作流程,按照您定義的順序將一系列無伺服器工作連結在一起。您可以結合 Google Cloud API、Cloud Functions 和 Cloud Run 等無伺服器產品和呼叫外部 API 的強大功能,打造彈性的無伺服器應用程式。
如同自動化調度管理工具一樣,您可以透過 Workflows 以 YAML/JSON 型工作流程定義語言定義商業邏輯流動,並提供 Workflows Execution API 和 Workflows UI 來觸發這些流程。
這不僅僅是自動化調度管理服務,還具備以下內建和可設定功能:
- 在步驟之間提供彈性的重試和錯誤處理機制,以利執行可靠的步驟。
- JSON 剖析,並在步驟之間傳遞變數,以避免黏附程式碼。
- 決策的運算式公式允許條件式步驟執行。
- 模組化和可重複使用的 Workflows 子工作流程。
- 支援外部服務之後,您就能自動化調度管理 Google Cloud 以外的服務。
- 提供 Google Cloud 和外部服務的驗證支援,可用於安全步驟執行。
- 連線至 Pub/Sub、Firestore、Tasks 和 Secret Manager 等 Google Cloud 服務,簡化整合作業。
更棒的是,Workflows 是全代管的無伺服器產品。不必設定或擴充伺服器,而且用多少付多少。
4. 啟用 API
在本研究室中,您會將 Cloud Functions 和 Cloud Run 服務連結至 Workflows。您將使用 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. 取得程式碼
取得程式碼 (如果您尚未參加先前的程式碼研究室):
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
您就會具備與本研究室相關的資料夾結構:
frontend | workflows | ├── functions ├── |── trigger-workflow ├── |── vision-data-transform ├── services ├── |── collage ├── |── thumbnails ├── workflows.yaml
以下為相關資料夾:
frontend
包含 App Engine 前端,我們將從研究室 4 重複使用。functions
包含為工作流程建立的 Cloud Functions。services
包含針對 Workflows 修改的 Cloud Run 服務。workflows.yaml
是工作流程定義檔案。
6. 探索 Workflows YAML
workflows.yaml 會透過一系列步驟定義工作流程。讓我們一起來進一步瞭解。
在工作流程開始時,系統會傳入一些參數。這些工作會由觸發 Workflows 的兩個 Cloud Functions 傳入。我們稍後會談到這些函式,但 Workflows 的啟動方式如下:
在 YAML 中,您可以看到這些參數已指派給 init
步驟中的變數,例如觸發事件的檔案和值區名稱,以及 Workflows 會呼叫的部分 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}
接著,Workflows 會檢查事件類型。支援的事件類型有 2 種:object.finalize
(檔案儲存於 Cloud Storage 值區中時發出) 和 object.delete
(檔案刪除時發出)。任何其他情況都會引發不支援的例外狀況。
以下是 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 支援切換陳述式和例外狀況處理、切換指令及其各種條件,以及引發指令,要求在無法辨識事件時引發錯誤。
接著來看看 imageAnalysisCall
。這是 Workflows 發出來的一系列呼叫,作用是呼叫 Vision API 來分析圖片、轉換 Vision API 回應資料以排序相片中辨識的事物標籤、挑選主要顏色、檢查圖片是否安全顯示,並將中繼資料儲存至 Cloud Firestore。
請注意,除 Vision Transform Cloud 函式外,其餘作業會在 Workflows 中完成 (稍後部署):
以下是各步驟在 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
步驟呼叫這些服務:
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 呼叫執行:
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}
在下列步驟中,我們會建立 Workflows 的所有外部依附元件:值區、Cloud Functions、Cloud Run 服務和 Firestore 資料庫。
7. 建立值區
圖片需要 2 個區塊:1 可用於儲存原始高解析度圖片,另外 1 個則儲存圖片縮圖。
使用 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 部分,再次檢查值區是否已建立完成:
8. Vision 資料轉換 (Cloud 函式)
Workflows.yaml 的開頭為 init
、eventTypeSwitch
,eventTypeNotSupported
步驟。確保來自值區的事件會轉送至正確的步驟。
如果是 object.finalize
事件,imageAnalysisCall
步驟會呼叫 Vision API,以擷取所建立圖片的中繼資料。所有步驟都是在 Workflows 中完成:
接下來,我們必須轉換 Vision API 傳回的資料,然後才能儲存至 Firestore。具體而言,我們需要:
- 列出針對圖片傳回的標籤。
- 擷取圖片的主要顏色。
- 判斷相片是否安全。
只要在 Cloud 函式和 Workflows 中的程式碼中完成這項作業,只需呼叫這個函式即可:
探索程式碼
Cloud 函式稱為 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
部署函式後,Workflows transformImageAnalysisData
步驟就能呼叫這個函式來執行 Vision API 資料轉換。
9. 準備資料庫
Workflows 的下一個項目是檢查圖片資料中的圖片是否安全,然後將 Vision API 傳回的相片相關資訊儲存到 Cloud Firestore 資料庫。Cloud Firestore 是速度飛快的全代管無伺服器 NoSQL 文件資料庫:
以上都是在 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 (字串):上傳圖片的檔案名稱,也是文件金鑰
- labels (字串陣列):Vision API 可識別項目的標籤
- color (字串):主色 (即#ab12ef)
- 建立 (日期):此圖片中繼資料儲存時間的時間戳記
- 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 控制台中查看:
Workflows storeMetadata
步驟現在可以將圖片中繼資料儲存至 Firestore。
10. 縮圖服務 (Cloud Run)
鏈結中的下一步是建立圖片縮圖。這是在 Cloud Run 服務中的程式碼完成,Workflows 會在 thumbnailCall
步驟中呼叫這項服務:
探索程式碼
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}
服務部署完畢後,Workflows thumbnailCall
步驟就能呼叫這項服務。
11. 美術拼貼服務 (Cloud Run)
鏈結的下一步是將最近的圖片製作美術拼貼。這是在 Cloud Run 服務中的程式碼完成,Workflows 會在 collageCall
步驟中呼叫這項服務:
探索程式碼
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 控制台的「Cloud Run」部分下方檢查兩項服務是否正在運作,工作流程「collageCall
」步驟都能呼叫這項服務:
12. Workflows 部署
我們部署了 Workflows 的所有外部依附元件。其餘步驟 (finalizeCompleted
、pictureGarbageCollectionGCS
、pictureGarbageCollectionFirestore
、deleteCompleted
) 都可以由 Workflows 自行完成。
現在即可部署 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 控制台的「Workflows」專區查看:
如有需要,可以按一下「工作流程」並進行編輯。在編輯過程中,系統會以視覺化的方式呈現工作流程:
您也可以使用正確的參數,從 Cloud 控制台手動執行工作流程。相反地,我們會改為自動執行這項政策,以回應下一個步驟中的 Cloud Storage 事件。
13. Workflows 觸發條件 (Cloud Functions)
工作流程已部署且準備就緒。現在,當檔案在 Cloud Storage 值區中建立或刪除時,我們必須觸發 Workflows。分別是 storage.object.finalize
和 storage.object.delete
事件。
Workflows 提供 API 和用戶端程式庫,用來建立、管理和執行可用的 Workflows。在這個案例中,您將使用 Workflows Execution API,更具體地使用 Node.js 用戶端程式庫觸發工作流程。
您會透過 Cloud 函式監聽 Cloud Storage 事件,觸發 Workflows。由於 Cloud 函式只能監聽一種事件類型,因此您會部署兩個 Cloud Functions 來監聽建立和刪除事件:
探索程式碼
Cloud 函式稱為 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 控制台中看到下列兩項函式:
14. 前端 (App Engine)
在這個步驟中,您會在 Google App Engine 的「Pic-a-daily: Lab 4—建立網路前端」建立網路前端,讓使用者從網頁應用程式上傳相片,並瀏覽上傳的相片和縮圖。
如要進一步瞭解 App Engine,並閱讀程式碼說明,請參閱「Pic-a-daily: Lab 4—Create a web frontend」。
探索程式碼
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 的功能,例如版本管理和流量分配:
15. 測試工作流程
如要進行測試,請前往應用程式的預設 App Engine 網址 (https://<YOUR_PROJECT_ID>.appspot.com/
),您應該會看到前端 UI 已啟動並開始運作!
上傳圖片。這麼做會觸發 Workflows,且您可以在 Cloud 控制台中以 Active
狀態查看工作流程執行作業:
Workflows 完成後,您可以按一下執行 ID,查看來自不同服務的輸出內容:
再上傳 3 張相片。您也應該會看到 Cloud Storage 值區和 App Engine 前端中的圖片縮圖與美術拼貼:
16. 清除 (選用)
如果不想保留應用程式,可以刪除整個專案來清理資源,以節省成本並成為良好的雲端公民:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
17. 恭喜!
您已透過 Workflows 建立應用程式的自動化調度管理版本,藉此自動化調度管理及呼叫服務。
涵蓋內容
- App Engine
- Cloud Firestore
- Cloud Functions
- Cloud Run
- 工作流程