工作流程最佳做法

Workflows 可讓您在Google Cloud協調及自動化服務。採用建議的架構做法,有助於提高可靠性、最佳化執行成本,以及建構具備韌性且易於維護的自動化調度管理。

這份清單並未列出所有建議,也不會說明 Workflows 的基本使用方式。本文假設您已大致瞭解 Google Cloud 整體環境和 Workflows。詳情請參閱Google Cloud Well-Architected Framework和 Workflows 總覽。

選取最佳通訊模式

設計微服務架構來部署多項服務時,您可以從下列通訊模式中選擇:

  • 直接服務對服務通訊

  • 間接事件導向通訊 (也稱為編排)

  • 自動設定、協調及管理 (也稱為自動化調度管理)

請務必考量上述各選項的優缺點,並根據您的用途選取最佳模式。舉例來說,直接服務對服務通訊可能比其他選項更容易實作,但會緊密結合您的服務。相較之下,事件導向架構可讓您鬆散耦合服務,但監控和偵錯可能會更複雜。最後,Workflows 等中央自動化調度管理工具雖然彈性較低,但可讓您協調服務之間的通訊,不必緊密結合直接服務對服務通訊,或複雜的編排事件。

您也可以結合使用多種通訊模式。舉例來說,在事件導向的自動化調度管理中,密切相關的服務會由事件觸發的自動化調度管理作業管理。同樣地,您也可以設計一個系統,讓一個自動化調度管理作業產生 Pub/Sub 訊息,傳送至另一個自動化調度管理系統。

一般提示

決定使用 Workflows 做為服務自動化調度管理工具後,請留意下列實用提示。

避免硬式編碼網址

您可以避免使用硬式編碼網址,支援可在多個環境中移植且更容易維護的工作流程。您可以透過下列方式達成此目的:

  • 將網址定義為執行階段引數。

    透過用戶端程式庫或 API 叫用工作流程時,這項功能就派得上用場。(不過,如果工作流程是由 Eventarc 事件觸發,且唯一可傳遞的引數是事件酬載,則無法使用這種方法)。

    範例

    main:
      params: [args]
      steps:
        - init:
            assign:
              - url1: ${args.urls.url1}
              - url2: ${args.urls.url2}

    執行工作流程時,您可以指定網址。例如:

    gcloud workflows run multi-env --data='{"urls":{"url1": "URL_ONE", "url2": "URL_TWO"}}'
  • 使用環境變數,並建立會根據部署環境動態設定的工作流程。或者,您也可以建立可重複使用的範本工作流程,並根據個別維護的環境變數進行設定。

  • 使用替代技術,建立單一工作流程定義檔案,但使用工具取代工作流程中的預留位置,部署變體。例如,您可以使用 Cloud Build 部署工作流程,並在 Cloud Build 建構設定檔中新增步驟,取代工作流程中的預留位置網址。

    範例

    steps:
    ‐ id: 'replace-urls'
      name: 'gcr.io/cloud-builders/gcloud'
      entrypoint: bash
      args:
        - -c
        - |
          sed -i -e "s~REPLACE_url1~$_URL1~" workflow.yaml
          sed -i -e "s~REPLACE_url2~$_URL2~" workflow.yaml
    ‐ id: 'deploy-workflow'
      name: 'gcr.io/cloud-builders/gcloud'
      args: ['workflows', 'deploy', 'multi-env-$_ENV', '--source', 'workflow.yaml']

    接著,您可以在建構時替換變數值。例如:

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions=_ENV=staging,_URL1="URL_ONE",_URL2="URL_TWO"

    詳情請參閱「透過 CLI 和 API 提交建構作業」。

    或者,您也可以使用 Terraform 佈建基礎架構,並定義設定檔,使用輸入變數為每個環境建立工作流程。

    範例

    variable "project_id" {
      type = string
    }
    
    variable "url1" {
      type = string
    }
    
    variable "url2" {
      type = string
    }
    
    locals {
      env = ["staging", "prod"]
    }
    
    # Define and deploy staging and production workflows
    resource "google_workflows_workflow" "multi-env-workflows" {
      for_each = toset(local.env)
    
      name            = "multi-env-${each.key}"
      project         = var.project_id
      region          = "us-central1"
      source_contents = templatefile("${path.module}/workflow.yaml", { url1 : "${var.url1}-${each.key}", url2 : "${var.url2}-${each.key}" })
    }

    在設定的根模組中宣告變數時,可以透過多種方式指派值。例如

    terraform apply -var="project_id=PROJECT_ID" -var="url1=URL_ONE" -var="url2=URL_TWO"
  • 使用 Secret Manager 連接器,在 Secret Manager 中安全地儲存及擷取網址。

使用巢狀步驟

每個工作流程都必須至少有一個步驟。根據預設,Workflows 會將步驟視為已排序的清單,並逐一執行,直到所有步驟都執行完畢。在邏輯上,某些步驟應分組在一起,您可以使用 steps 區塊巢狀結構一系列步驟。這項功能很方便,可讓您指向正確的基本步驟,處理一組步驟。

範例

main:
    params: [input]
    steps:
    - callWikipedia:
        steps:
        - checkSearchTermInInput:
            switch:
                - condition: ${"searchTerm" in input}
                  assign:
                    - searchTerm: ${input.searchTerm}
                  next: readWikipedia
        - getCurrentDate:
            call: http.get
            args:
                url: https://timeapi.io/api/Time/current/zone?timeZone=Europe/Amsterdam
            result: currentDate
        - setFromCallResult:
            assign:
                - searchTerm: ${currentDate.body.dayOfWeek}
        - readWikipedia:
            call: http.get
            args:
                url: https://en.wikipedia.org/w/api.php
                query:
                    action: opensearch
                    search: ${searchTerm}
            result: wikiResult
    - returnOutput:
            return: ${wikiResult.body[1]}

Wrap 運算式

所有運算式都必須以 $ 開頭,並以大括號括住:

${EXPRESSION}

為避免 YAML 剖析問題,請用引號括住運算式。舉例來說,如果冒號被解讀為定義對應,包含冒號的運算式可能會導致非預期的行為。如要解決這個問題,請用單引號括住 YAML 運算式:

'${"Name: " + myVar}'

您也可以使用跨多行的運算式。例如,使用 Workflows BigQuery 連接器時,您可能需要用引號括住 SQL 查詢。

範例

- runQuery:
    call: googleapis.bigquery.v2.jobs.query
    args:
        projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
        body:
            useLegacySql: false
            useQueryCache: false
            timeoutMs: 30000
            # Find top 100 titles with most views on Wikipedia
            query: ${
                "SELECT TITLE, SUM(views)
                FROM `bigquery-samples.wikipedia_pageviews." + table + "`
                WHERE LENGTH(TITLE) > 10
                GROUP BY TITLE
                ORDER BY SUM(VIEWS) DESC
                LIMIT 100"
                }
    result: queryResult

如需完整的工作流程定義,請參閱「平行執行多項 BigQuery 工作」。

使用宣告式呼叫

使用 Workflows 從工作流程本身呼叫服務並處理結果,以及執行發出 HTTP 呼叫等簡單工作。Workflows 可叫用服務、剖析回應,並為其他連結的服務建構輸入內容。呼叫服務可避免額外叫用、額外依附元件和服務呼叫服務等複雜情況。建議您將不含商業邏輯的服務替換為宣告式 API 呼叫,並使用 Workflows 簡化複雜度。

不過,如果工作太過複雜,不適合由 Workflows 處理,建議您建立服務來執行,例如實作可重複使用的商業邏輯、複雜的運算,或 Workflows 運算式和標準程式庫不支援的轉換。複雜的情況通常較容易在程式碼中實作,而不是使用 YAML 或 JSON 和 Workflows 語法。

只儲存所需資料

請控管記憶體用量,以免達到資源限制或發生錯誤,例如 ResourceLimitError、MemoryLimitExceededError 或 ResultSizeLimitExceededError。

請謹慎選擇要儲存在變數中的內容,只篩選並儲存所需內容。如果服務傳回的酬載過大,請使用獨立函式呼叫,並只傳回必要內容。

您可以清除變數來釋出記憶體。例如,您可能需要釋出後續步驟所需的記憶體。或者,您可能不關心某些呼叫的結果,因此可以完全省略這些結果。

您可以指派 null 來清除變數。在 YAML 中,您也可以為變數指派空值或 ~。這項資訊可指出哪些記憶體可以安全回收。

範例

  - step:
      assign:
        - bigVar:

使用子工作流程和外部工作流程

您可以使用子工作流程定義要多次呼叫的邏輯或一組步驟,簡化工作流程定義。子工作流程類似於程式設計語言中的函式或常式。可接受參數並傳回值,讓您建立更複雜的工作流程,適用於更廣泛的應用。

請注意,子工作流程僅適用於工作流程定義,無法在其他工作流程中重複使用。不過,您可以從其他工作流程呼叫工作流程。Workflows 連接器可協助您完成這項作業。詳情請參閱 Workflow Executions API 和 Workflows API 的連接器總覽。

使用 Workflows 連接器

Workflows 提供多種連接器,可讓您在工作流程中更輕鬆地存取其他 Google Cloud 產品。連接器會處理要求的格式化作業,並提供方法和引數,因此您不必瞭解Google Cloud API 的詳細資料,就能輕鬆呼叫服務。連接器也內建處理重試和長時間執行的作業的行為,因此您不必反覆執行及等待呼叫完成,連接器會為您處理。

如需呼叫 Google Cloud API,請先確認是否有適用的 Workflows 連接器。如果找不到 Google Cloud 產品的連接器,可以提出要求。

瞭解如何使用連接器,並參閱連接器參考資料,取得可用連接器的詳細參考資料。

同時執行工作流程步驟

Workflows 可以依序執行步驟,也可以同時執行獨立步驟。在某些情況下,這可以大幅加快工作流程執行速度。詳情請參閱「平行執行工作流程步驟」。

套用重試和 Saga 模式

設計具備彈性的工作流程,可處理暫時性和永久性的服務故障。Workflows 可能會發生錯誤,例如 HTTP 要求、函式、連接器失敗,或由您自己的工作流程程式碼產生。新增錯誤處理和重試機制,避免單一步驟失敗導致整個工作流程失敗。

部分業務交易會跨越多項服務,因此您需要一種機制來實作跨服務的交易。Saga 設計模式是在分散式交易情境中,管理微服務之間資料一致性的方法。Saga 是一連串的交易,會為每筆交易發布事件,並觸發下一筆交易。如果交易失敗,Saga 會執行補償交易,抵銷序列中先前的失敗。試用 GitHub 上的「Workflows 中的重試和 Saga 模式」教學課程。

使用回呼等待

回呼可讓工作流程執行作業等待其他服務向回呼端點提出要求,該要求會繼續執行工作流程。

透過回呼,您可以向工作流程發出信號,指出指定事件已發生,並等待該事件,而無需輪詢。舉例來說,您可以建立工作流程,在產品有現貨或商品出貨時通知您,或等待人工互動,例如審查訂單或驗證翻譯。您也可以使用回呼和 Eventarc 觸發條件等待事件。

自動調度管理長時間執行的工作

如需執行長時間執行的批次處理工作負載,可以使用 Batch 或 Cloud Run 工作,並使用 Workflows 管理服務。這樣一來,您就能結合各項優勢,有效率地佈建及調度整個程序。

Batch 是一項全代管服務,可讓您在 Compute Engine 虛擬機器 (VM) 執行個體中排定時間、排入佇列和執行批次工作負載。您可以使用 Batch 適用的 Workflows 連接器,排定及執行批次工作。詳情請參閱教學課程。

Cloud Run 工作用於執行特定作業的程式碼,會在完成後自動結束。Workflows 可讓您在工作流程中執行 Cloud Run 工作,以便進行更複雜的資料處理作業,或以系統化的方式自動調度管理現有工作。試用教學課程,瞭解如何使用 Workflows 執行 Cloud Run 工作。

調整各項服務的逾時時間和酬載大小

Workflows 呼叫下游 Google Cloud 服務時,要求逾時、HTTP 回應限制和記憶體限制的差異可能會導致 TimeoutError、ValueError 或 MemoryLimitExceededError 失敗。請比較 Workflows 資源限制與目標服務的限制,並選擇符合工作負載時間長度和酬載大小的整合模式。例如:

整合情境 Workflows 限制 目標服務限制 建議模式
對 Cloud Run 服務或 Cloud Run 函式發出同步 HTTP 呼叫 (http.get、http.post、http.request)
  • HTTP 要求逾時時間上限為 30 分鐘 (1,800 秒),http.* 呼叫的預設值為 300 秒,連接器 HTTP 要求的預設值為 1,800 秒
  • HTTP 回應大小上限為 2 MB,超過就會引發 ValueError
  • 引發 MemoryLimitExceededError 前,變數記憶體累計達 512 KB
  • Cloud Run 服務和 Cloud Run functions (第 2 代) 的要求逾時時間上限為 60 分鐘 (3,600 秒),第 1 代函式則為 9 分鐘 (540 秒)
  • HTTP/1.1 回應大小上限為 32 MB

請參閱「設定服務的要求逾時」和「Cloud Run 的資源限制」。

如果工作負載執行時間超過 30 分鐘,請勿對 Cloud Run 服務進行同步 http.* 呼叫。

請改用 Cloud Run Admin API 連接器執行 Cloud Run 工作,或使用非同步 Workflows 回呼端點。

Cloud Run Admin API 連接器支援長時間執行的作業 (LRO) 輪詢,預設會在 30 分鐘 (1,800 秒) 後逾時。如果工作執行時間超過 30 分鐘,請設定 Cloud Run 工作的工作逾時 (預設為 10 分鐘,最多可設為 168 小時或 7 天),以及連接器呼叫的 connector_params.timeout (最多 1 年或 31536000 秒)。

BigQuery 查詢和大型 API 酬載 (googleapis.bigquery.v2.jobs.query 和 REST 回應)
  • 引發 MemoryLimitExceededError 前,變數記憶體累計達 512 KB
  • UTF-8 字串長度上限為 256 KB
  • HTTP 回應大小上限為 2 MB,超過就會引發 ValueError

BigQuery 查詢結果和 REST API 酬載可能會超過數 MB 或 GB。

請參閱「BigQuery 查詢工作配額與限制」。

避免將大型結果集提取至工作流程變數。

使用 maxResults 分頁功能,透過 EXPORT DATA 將大型查詢輸出內容寫入目的地資料表或 Cloud Storage bucket,或將酬載轉換和篩選作業卸載至 Cloud Run 服務或 Cloud Run 函式,再將精簡摘要傳回 Workflows。

將長時間執行的工作容器化

您可以使用 Workflows 和 Compute Engine,自動執行長時間執行的容器。舉例來說,您可以將長時間執行的工作容器化,以便在任何地方執行,然後在 Compute Engine VM 上執行容器,最長可執行工作流程執行作業 (一年)。

您可以使用 Workflows 自動建立 VM、在 VM 上執行容器,以及刪除 VM。這項服務可讓您使用伺服器並執行容器,但會抽象化管理兩者的複雜性,如果您在使用 Cloud Run functions 或 Cloud Run 等服務時遇到時間限制,這項服務會很有幫助。試用 GitHub 上的透過 Workflows 和 Compute Engine 執行長時間執行的容器教學課程。

透過 Workflows 執行指令列工具

Cloud Build 服務會在 Google Cloud 執行建構作業,並將建構作業拆成一系列步驟,每個步驟都會在 Docker 容器中執行。執行建構步驟與執行指令碼中的指令類似。

Google Cloud CLI 包含 gcloud、bq 和 kubectl 指令列工具,但無法直接從 Workflows 執行 gcloud CLI 指令。不過,Cloud Build 提供的容器映像檔包含 gcloud CLI。您可以在 Cloud Build 步驟中,於這些容器內執行 gcloud CLI 指令,並使用 Cloud Build 連接器在 Workflows 中建立該步驟。

範例

在工作流程中執行 gcloud:

# This example shows how to execute gcloud commands from Workflows
# using Cloud Build and returns the output

main:
  steps:
  - execute_command:
      call: gcloud
      args:
          args: "workflows list"
      result: result
  - return_result:
      return: ${result}

gcloud:
  params: [args]
  steps:
  - create_build:
      call: googleapis.cloudbuild.v1.projects.builds.create
      args:
        projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
        parent: ${"projects/" + sys.get_env("GOOGLE_CLOUD_PROJECT_ID") + "/locations/global"}
        body:
          serviceAccount: ${sys.get_env("GOOGLE_CLOUD_SERVICE_ACCOUNT_NAME")}
          options:
            logging: CLOUD_LOGGING_ONLY
          steps:
          - name: gcr.io/google.com/cloudsdktool/cloud-sdk
            entrypoint: /bin/bash
            args: ${["-c", "gcloud " + args + " > $$BUILDER_OUTPUT/output"]}
      result: result_builds_create
  - return_build_result:
      return: ${text.split(text.decode(base64.decode(result_builds_create.metadata.build.results.buildStepOutputs[0])), "\n")}

Run kubectl in a workflow:

# This example shows how to execute kubectl commands from Workflows
# using Cloud Build and returns the output

main:
  steps:
  - execute_command:
      call: kubectl
      args:
          args: "--help"
      result: result
  - return_result:
      return: ${result}

kubectl:
  params: [args]
  steps:
  - create_build:
      call: googleapis.cloudbuild.v1.projects.builds.create
      args:
        projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
        parent: ${"projects/" + sys.get_env("GOOGLE_CLOUD_PROJECT_ID") + "/locations/global"}
        body:
          serviceAccount: ${sys.get_env("GOOGLE_CLOUD_SERVICE_ACCOUNT_NAME")}
          options:
            logging: CLOUD_LOGGING_ONLY
          steps:
          - name: gcr.io/cloud-builders/kubectl
            entrypoint: /bin/bash
            args: ${["-c", "kubectl " + args + " > $$BUILDER_OUTPUT/output"]}
      result: result_builds_create
  - return_build_result:
      return: ${text.split(text.decode(base64.decode(result_builds_create.metadata.build.results.buildStepOutputs[0])), "\n")}

使用 Terraform 建立工作流程

Terraform 是一種基礎架構即程式碼工具,可讓您透過程式碼,以可預測的方式建立、變更及改善雲端基礎架構。

您可以使用 Terraform google_workflows_workflow 資源定義及部署工作流程。詳情請參閱「使用 Terraform 建立工作流程」。

為方便管理及維護大型工作流程,您可以在獨立的 YAML 檔案中建立工作流程,並使用 templatefile 函式將該檔案匯入 Terraform,該函式會讀取指定路徑的檔案,並將其內容轉譯為範本。

範例

  # Define a workflow
  resource "google_workflows_workflow" "workflows_example" {
    name            = "sample-workflow"
    region          = var.region
    description     = "A sample workflow"
    service_account = google_service_account.workflows_service_account.id
    # Import main workflow YAML file
    source_contents = templatefile("${path.module}/workflow.yaml",{})
  }

同樣地,如果主要工作流程會呼叫多個子工作流程,您可以在不同的檔案中定義主要工作流程和子工作流程,並使用 templatefile 函式匯入。

範例

  # Define a workflow
  resource "google_workflows_workflow" "workflows_example" {
    name            = "sample-workflow"
    region          = var.region
    description     = "A sample workflow"
    service_account = google_service_account.workflows_service_account.id
    # Import main workflow and subworkflow YAML files
    source_contents = join("", [
      templatefile(
        "${path.module}/workflow.yaml",{}
      ),

      templatefile(
        "${path.module}/subworkflow.yaml",{}
      )])
  }

請注意,如果您在偵錯工作流程時參照行號,透過 Terraform 設定檔匯入的所有 YAML 檔案都會合併並部署為單一工作流程。

從 Git 存放區部署工作流程

Cloud Build 使用自動建構觸發條件啟用 CI/CD 自動化功能。您可以設定觸發條件來監聽傳入事件,例如將新修訂版本推送至存放區,或啟動提取要求時,然後在有新事件傳入時自動執行建構作業。

您可以使用 Cloud Build 自動建構觸發條件,自動啟動建構作業,並從 Git 存放區部署工作流程。您可以設定觸發條件,在原始碼存放區發生任何變更時部署工作流程,或只有在變更符合特定條件時才部署工作流程。

這種做法有助於管理部署作業生命週期。例如,您可以將變更部署至預備環境中的工作流程,對該環境執行測試,然後逐步將這些變更發布至正式環境。詳情請參閱「使用 Cloud Build 從 Git 存放區部署工作流程」。

最佳化用量

執行工作流程的費用極低。不過,如果用量較高,請遵循下列規範,盡可能提高使用效率並降低成本:

  • 請勿使用自訂網域,並確保對 Google Cloud服務的所有呼叫都使用*.appspot.com、*.cloud.goog、*.cloudfunctions.net或*.run.app,這樣系統就會針對內部步驟計費,而非外部步驟。

  • 套用自訂重試政策,在延遲時間、可靠性需求和成本之間取得平衡。重試頻率越高,延遲時間越短,可靠性越高,但費用也可能增加。

  • 使用等待長時間執行作業的連接器時,請設定自訂輪詢政策,以最佳化延遲時間和成本。舉例來說,如果您預期作業會超過一小時,可以設定一項政策,在作業開始一分鐘後輪詢,以防立即失敗,之後每 15 分鐘輪詢一次。

  • 將指派合併為一個步驟。

  • 避免過度使用 sys.log 步驟。建議改用通話記錄。

  • 瞭解哪些作業會視為一個步驟。如果作業本身不計為步驟,但在適用步驟中使用,則會計入。例如,以下內容算作一個步驟:

    - type_check:
        return: if(get_type((int("6"))) == integer, 1, 2)
    

    下表列出計入和不計入步驟上限的重要作業:

    類別 作業
    計為一步
    • 資料作業:指派、傳回值
    • 控制流程:跳轉 (next)、切換、啟動 for 迴圈,以及 for 迴圈的每次疊代
    • 呼叫:叫用 sys.get_env 或其他標準程式庫函式、其他工作流程或連接器
    • 並行:產生執行緒和平行執行
    • 錯誤處理:每個 raise、try、retry 和 except 區塊都會計為獨立步驟,即使其他作業屬於同一個較大步驟的一部分也一樣。

      舉例來說,如果步驟包含 try 區塊和呼叫作業,則會計為三個步驟:一個主要步驟、一個 try 步驟和一個呼叫步驟。新增 retry 區塊會增加三個步驟 (重試、嘗試和呼叫各一個),總共六個步驟。

    不計為步驟

最佳做法摘要

下表彙整了本文件建議的一般提示和最佳做法。

一般提示
最佳做法

後續步驟