工作流程最佳做法

使用 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 中安全地儲存及擷取網址。

使用巢狀步驟

每個工作流程都必須至少有一個步驟。 根據預設,工作流程會將步驟視為有序清單,並逐一執行,直到所有步驟都執行完畢為止。從邏輯上來看,部分步驟應歸為一組,您可以使用 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]}

包裝運算式

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

${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 呼叫。工作流程可以叫用服務、剖析回應,以及為其他連結的服務建構輸入內容。呼叫服務可避免額外呼叫、其他依附元件,以及服務呼叫服務等複雜情況。考慮以宣告式 API 呼叫取代不含商業邏輯的服務,並使用 Workflows 簡化複雜度。

不過,如果工作太過複雜,不適合使用 Workflows,您就應該建立服務來處理,例如實作可重複使用的商業邏輯、複雜的運算,或 Workflows 運算式及其標準程式庫不支援的轉換。如果案例較為複雜,通常會以程式碼實作,而不是使用 YAML 或 JSON 和 Workflows 語法

只儲存必要內容

請控管記憶體用量,以免遇到資源限制,或出現 ResourceLimitErrorMemoryLimitExceededErrorResultSizeLimitExceededError 等指出此問題的錯誤。

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

清除變數即可釋放記憶體。舉例來說,您可能想釋出後續步驟所需的記憶體。或者,您可能不希望系統顯示某些通話的結果,因此可以完全省略這些結果。

您可以指派 null 來清除變數。在 YAML 中,您也可以將空值或 ~ 指派給變數。這會找出可安全回收的記憶體。

範例

  - step:
      assign:
        - bigVar:

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

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

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

使用 Workflows 連接器

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

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

瞭解如何使用連接器,如需可用連接器的詳細參考資料,請參閱連接器參考資料

並行執行工作流程步驟

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

套用重試和 Saga 模式

設計可容錯的工作流程,以處理暫時和永久的服務故障。舉例來說,如果 HTTP 要求、函式或連接器失敗,或是您自己的工作流程程式碼產生錯誤,就可能會引發 Workflows 錯誤。新增錯誤處理和重試機制,避免某個步驟失敗時,整個工作流程也跟著失敗。

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

使用回呼功能等待

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

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

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

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

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

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

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

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

您可以使用工作流程自動建立 VM、在 VM 上執行容器,以及刪除 VM。這樣一來,您就能使用伺服器並執行容器,但不必管理兩者,因此可省下複雜的作業。如果您在使用 Cloud Run 函式或 Cloud Run 等服務時遇到時間限制,這項功能就非常實用。請試試 GitHub 上的使用 Workflows 和 Compute Engine 執行長時間執行的容器教學課程。

從工作流程執行指令列工具

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

Google Cloud CLI 包含 gcloudbqkubectl 指令列工具,但無法直接從 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 或其他標準程式庫函式、其他工作流程或連接器
    • 並行:產生執行緒和並行執行
    • 錯誤處理:每個 raisetryretryexcept 區塊都算做一個獨立步驟,即使其他作業屬於同一個較大的步驟也一樣。

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

    不計入步驟

最佳做法摘要

下表摘要說明本文建議的一般提示和最佳做法。

一般提示
最佳做法

後續步驟