Best practice per Workflows

Puoi fare riferimento alle best practice elencate qui per l'orchestrazione dei tuoi servizi utilizzando Workflows.

Questo non è un elenco esaustivo dei suggerimenti e non fornisce le nozioni di base su come utilizzare Workflows. In questo documento si presuppone che tu abbia già una conoscenza generale del panorama complessivo di Google Cloud e di Workflows. Per ulteriori informazioni, consulta il framework dell'architettura Google Cloud e la panoramica dei flussi di lavoro.

Seleziona un modello di comunicazione ottimale

Quando progetti un'architettura di microservizi per il deployment di più servizi, puoi scegliere tra i seguenti pattern di comunicazione:

  • Comunicazione diretta tra servizi

  • Comunicazione indiretta basata su eventi (nota anche come coreografia)

  • Configurazione, coordinamento e gestione automatizzate (nota anche come orchestrazione)

Assicurati di considerare i vantaggi e gli svantaggi di ciascuna delle opzioni precedenti e seleziona un pattern ottimale per il tuo caso d'uso. Ad esempio, la comunicazione diretta tra servizi potrebbe essere più semplice da implementare rispetto ad altre opzioni, ma è strettamente collegata ai tuoi servizi. Al contrario, un'architettura basata su eventi ti consente di accoppiare facilmente i tuoi servizi. Tuttavia, il monitoraggio e il debug potrebbero essere più complicati. Infine, un agente di orchestrazione centrale come Workflows, sebbene meno flessibile, consente di coordinare la comunicazione tra i servizi senza lo stretto accoppiamento della comunicazione diretta tra servizi o la complessità degli eventi coreografati.

Puoi anche combinare modelli di comunicazione. Ad esempio, nell'orchestrazione basata su eventi, i servizi strettamente correlati vengono gestiti in un'orchestrazione attivata da un evento. Analogamente, potresti progettare un sistema in cui un'orchestrazione genera un messaggio Pub/Sub su un altro sistema orchestrato.

Suggerimenti di carattere generale

Una volta deciso di utilizzare Workflows come agente di orchestrazione dei servizi, tieni presente i seguenti suggerimenti utili.

Evita l'impostazione di URL come hardcoded

Puoi supportare flussi di lavoro portabili in più ambienti e più facili da gestire evitando URL impostati come hardcoded. Puoi farlo nei seguenti modi:

  • Definisci gli URL come argomenti di runtime.

    Ciò può essere utile quando il flusso di lavoro viene richiamato tramite una libreria client o l'API. Tuttavia, questa opzione non funziona se il flusso di lavoro viene attivato da un evento da Eventarc e l'unico argomento che può essere passato è il payload dell'evento.

    Esempio

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

    Quando esegui il flusso di lavoro, puoi specificare gli URL. Ad esempio:

    gcloud workflows run multi-env --data='{"urls":{"url1": "URL_ONE", "url2": "URL_TWO"}}'
  • Utilizza le variabili di ambiente e crea un flusso di lavoro che venga configurato dinamicamente a seconda dell'ambiente in cui viene eseguito il deployment. In alternativa, crea un flusso di lavoro che possa essere riutilizzato come modello e configurato in base a variabili di ambiente gestite separatamente.

  • Utilizza una tecnica di sostituzione che consente di creare un singolo file di definizione del flusso di lavoro, ma esegui il deployment delle varianti utilizzando uno strumento che sostituisce i segnaposto nel flusso di lavoro. Ad esempio, puoi utilizzare Cloud Build per eseguire il deployment di un flusso di lavoro e, nel file di configurazione di Cloud Build, aggiungere un passaggio per sostituire gli URL segnaposto nel flusso di lavoro.

    Esempio

    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']

    Puoi quindi sostituire i valori delle variabili al momento della creazione. Ad esempio:

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

    Per maggiori informazioni, consulta Inviare una build tramite interfaccia a riga di comando e API.

    In alternativa, puoi utilizzare Terraform per eseguire il provisioning dell'infrastruttura e definire un file di configurazione che crei flussi di lavoro per ogni ambiente utilizzando variabili di input.

    Esempio

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

    Le variabili dichiarate nel modulo principale della configurazione possono essere assegnate in vari modi. Ad esempio:

    terraform apply -var="project_id=PROJECT_ID" -var="url1=URL_ONE" -var="url2=URL_TWO"
  • Utilizza il connettore Secret Manager per archiviare in modo sicuro gli URL in Secret Manager e recuperarli.

Utilizzare passaggi nidificati

Ogni flusso di lavoro deve includere almeno un passaggio. Per impostazione predefinita, Workflows tratta i passaggi come se fossero in un elenco ordinato e li esegue uno alla volta fino a quando non sono stati eseguiti tutti i passaggi. A livello logico, alcuni passaggi dovrebbero essere raggruppati e puoi utilizzare un blocco steps per nidificare una serie di passaggi. Ciò è pratico perché ti consente di puntare al passaggio atomico corretto per elaborare una serie di passaggi.

Esempio

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]}

Aggrega espressioni

Tutte le espressioni devono iniziare con $ ed essere racchiuse tra parentesi graffe:

${EXPRESSION}

Per evitare problemi di analisi YAML, puoi racchiudere le espressioni tra virgolette. Ad esempio, le espressioni contenenti due punti possono causare comportamenti imprevisti quando questi ultimi vengono interpretati come definizione di una mappa. Puoi risolvere questo problema racchiudendo l'espressione YAML tra virgolette singole:

'${"Name: " + myVar}'

Puoi anche utilizzare espressioni che si estendono su più righe. Ad esempio, potresti dover racchiudere una query SQL tra virgolette quando utilizzi il connettore BigQuery di Workflows.

Esempio

- 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

Per la definizione completa del flusso di lavoro, consulta Eseguire più job BigQuery in parallelo.

Utilizza chiamate dichiarative

Utilizzare Workflows per chiamare i servizi dal flusso di lavoro stesso e gestire i risultati, nonché per eseguire attività semplici come effettuare una chiamata HTTP. Workflows possono richiamare servizi, analizzare le risposte e creare input per altri servizi connessi. La chiamata a un servizio consente di evitare le complicazioni dovute a chiamate extra, dipendenze aggiuntive e servizi di chiamata di servizi. Valuta la possibilità di sostituire i servizi privi di logica di business con chiamate API dichiarative e utilizza Workflows per astrarre la complessità.

Tuttavia, è necessario creare servizi per svolgere le attività troppo complesse per Workflows, ad esempio implementare logica di business riutilizzabile, calcoli complessi o trasformazioni non supportate dalle espressioni di Workflows e dalla relativa libreria standard. Un caso complicato è in genere più facile da implementare nel codice, anziché utilizzare YAML o JSON e la sintassi di Workflows.

Archivia solo ciò che ti serve

Tieni sotto controllo il consumo della memoria in modo da non riscontrare limiti delle risorse o un errore che lo indica, ad esempio ResourceLimitError, MemoryLimitExceededError o ResultSizeLimitExceededError.

Seleziona con cura ciò che memorizzi nelle variabili, filtrando e memorizzando solo ciò di cui hai bisogno. Se un servizio restituisce un payload che è troppo grande, utilizza una funzione separata per effettuare la chiamata per te e restituire solo ciò che è necessario.

Puoi liberare memoria cancellando le variabili. Ad esempio, puoi liberare la memoria necessaria per i passaggi successivi. Oppure potresti avere chiamate con risultati che non ti interessano e ometterli del tutto.

Puoi cancellare una variabile assegnando null. In YAML, puoi anche assegnare un valore vuoto o ~ a una variabile. Identifica la memoria che può essere recuperata in sicurezza.

Esempio

  - step:
      assign:
        - bigVar:

Utilizzare flussi di lavoro secondari e esterni

Puoi utilizzare i flussi di lavoro secondari per definire un elemento della logica o un insieme di passaggi da chiamare più volte, semplificando la definizione del flusso di lavoro. I flussi di lavoro secondari sono simili a una funzione o una routine in un linguaggio Possono accettare parametri e valori restituiti, consentendoti di creare flussi di lavoro più complessi con una gamma più ampia di applicazioni.

Tieni presente che i flussi di lavoro secondari sono locali rispetto alla definizione del flusso di lavoro e non possono essere riutilizzati in altri flussi di lavoro. Tuttavia, puoi chiamare flussi di lavoro da altri flussi di lavoro. I connettori Workflows possono aiutarti. Per ulteriori informazioni, consulta le panoramiche dei connettori per l'API Workflow Executions e l'API Workflows.

Utilizzo dei connettori di Workflows

Workflows fornisce una serie di connectors che semplificano l'accesso ad altri prodotti Google Cloud all'interno di un flusso di lavoro. I connettori semplificano le chiamate ai servizi perché gestiscono la formattazione delle richieste per conto tuo, fornendo metodi e argomenti in modo che non sia necessario conoscere i dettagli di un'API Google Cloud. I connettori hanno inoltre un comportamento integrato per la gestione dei tentativi e delle operazioni a lunga esecuzione, in modo da evitare l'iterazione e l'attesa del completamento delle chiamate. I connettori si occupano di questo aspetto.

Se devi chiamare un'API Google Cloud, verifica innanzitutto se esiste un connettore Workflows. Se non visualizzi un connettore per un prodotto Google Cloud, puoi richiederlo.

Scopri come utilizzare un connettore e, per un riferimento dettagliato sui connettori disponibili, consulta la documentazione di riferimento sui connettori.

Esegui i passaggi del flusso di lavoro in parallelo

Sebbene Workflows possa eseguire i passaggi in sequenza, puoi anche eseguire passaggi indipendenti in parallelo. In alcuni casi, questo può accelerare notevolmente l'esecuzione del flusso di lavoro. Per maggiori informazioni, consulta Eseguire i passaggi del flusso di lavoro in parallelo.

Applicare i nuovi tentativi e il pattern saga

Progettare flussi di lavoro resilienti e in grado di gestire errori di servizio sia temporanei che permanenti. Gli errori relativi a Workflows potrebbero essere generati, ad esempio, da richieste HTTP, funzioni o connettori non riusciti, oppure generati dal codice del tuo flusso di lavoro. Aggiungi la gestione degli errori e i nuovi tentativi in modo che un errore in un passaggio non causi l'errore dell'intero flusso di lavoro.

Alcune transazioni aziendali comprendono più servizi, quindi è necessario un meccanismo per implementare le transazioni che coprono i servizi. Un pattern di progettazione "Saga" è un modo per gestire la coerenza dei dati tra microservizi in scenari di transazioni distribuite. Una saga è una sequenza di transazioni che pubblica un evento per ogni transazione e attiva la transazione successiva. Se una transazione ha esito negativo, la saga esegue transazioni di compensazione che contrastano gli errori precedenti nella sequenza. Prova il tutorial per nuovi tentativi e pattern Saga in Workflows su GitHub.

Utilizza i callback per attendere

I callback consentono alle esecuzioni del flusso di lavoro di attendere che un altro servizio effettui una richiesta all'endpoint di callback. Questa richiesta riprende l'esecuzione del flusso di lavoro.

Con i callback, puoi segnalare al tuo flusso di lavoro che si è verificato un evento specifico e attendere che si sia verificato senza eseguire il polling. Ad esempio, puoi creare un flusso di lavoro che ti avvisa quando un prodotto è di nuovo disponibile o quando un articolo è stato spedito o che in attesa di consentire l'interazione umana, come la revisione di un ordine o la convalida di una traduzione. Puoi anche attendere gli eventi utilizzando callback e trigger Eventarc.

Orchestra job a lunga esecuzione

Se devi eseguire carichi di lavoro dell'elaborazione batch a lunga esecuzione, puoi utilizzare i job Batch o Cloud Run e puoi utilizzare Workflows per gestire i servizi. Ciò consente di combinare i vantaggi e di eseguire il provisioning e l'orchestrazione in modo efficiente dell'intero processo.

Batch è un servizio completamente gestito che consente di pianificare, inserire in coda ed eseguire carichi di lavoro batch su istanze di macchine virtuali (VM) Compute Engine. Puoi utilizzare il connettore Workflows per Batch per pianificare ed eseguire un job batch. Per maggiori dettagli, prova il tutorial.

I job Cloud Run vengono utilizzati per eseguire il codice che esegue un lavoro (un job) e si chiude al termine del lavoro. Workflows consente di eseguire job Cloud Run come parte di un flusso di lavoro per eseguire elaborazioni di dati più complesse o orchestrare un sistema di job esistenti. Prova il tutorial che mostra come utilizzare Workflows per eseguire un job Cloud Run.

Containerizza le attività di lunga durata

Puoi automatizzare l'esecuzione di un container a lunga esecuzione utilizzando Workflows e Compute Engine. Ad esempio, puoi containerizzare un'attività a lunga esecuzione in modo che possa essere eseguita ovunque, quindi puoi eseguire il container su una VM di Compute Engine per la durata massima dell'esecuzione di un flusso di lavoro (un anno).

Utilizzando Workflows, puoi automatizzare la creazione della VM, l'esecuzione del container sulla VM e l'eliminazione della VM. Ciò consente di utilizzare un server ed eseguire un container, ma elimina la complessità della gestione di entrambi e può essere utile in caso di limiti di tempo quando si utilizza un servizio come Cloud Functions o Cloud Run. Prova il tutorial Container a lunga esecuzione con Workflows e Compute Engine su GitHub.

Esecuzione di strumenti a riga di comando da Workflows

Cloud Build è un servizio che esegue le build su Google Cloud come una serie di passaggi di build, dove ogni passaggio viene eseguito in un container Docker. L'esecuzione di passaggi di build è analoga all'esecuzione di comandi in uno script.

Google Cloud CLI include gli strumenti a riga di comando gcloud, gsutil, bq e kubectl, ma non esiste un modo diretto per eseguire i comandi gcloud CLI da Workflows. Tuttavia, Cloud Build fornisce immagini container che includono gcloud CLI. Puoi eseguire i comandi dell'interfaccia a riga di comando gcloud CLI in questi container da un passaggio di Cloud Build e creare questo passaggio in Workflows utilizzando il connettore Cloud Build.

Esempio

Esegui gcloud in un flusso di lavoro:

# 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")}

Utilizzare Terraform per creare il flusso di lavoro

Terraform è uno strumento Infrastructure as Code che consente di creare, modificare e migliorare in modo prevedibile la tua infrastruttura cloud mediante l'utilizzo del codice.

Puoi definire ed eseguire il deployment di un flusso di lavoro utilizzando la risorsa Terraform google_workflows_workflow. Per maggiori informazioni, consulta Creare un flusso di lavoro utilizzando Terraform.

Per gestire e mantenere flussi di lavoro di grandi dimensioni, puoi creare il tuo flusso di lavoro in un file YAML separato e importare il file in Terraform utilizzando la funzione templatefile, che legge un file in un determinato percorso e ne esegue il rendering come modello.

Esempio

  # 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",{})
  }

Analogamente, se un tuo flusso di lavoro principale chiama più flussi di lavoro secondari, puoi definire il flusso di lavoro principale e i flussi di lavoro secondari in file separati e utilizzare la funzione templatefile per importarli.

Esempio

  # 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",{}
      )])
  }

Tieni presente che, se fai riferimento ai numeri di riga durante il debug di un flusso di lavoro, tutti i file YAML importati tramite il file di configurazione Terraform vengono uniti e distribuiti come un singolo flusso di lavoro.

Esegui il deployment di un flusso di lavoro da un repository Git

Cloud Build utilizza i trigger di build per abilitare l'automazione CI/CD. Puoi configurare trigger per ascoltare gli eventi in arrivo, ad esempio quando viene eseguito il push di un nuovo commit in un repository o quando viene avviata una richiesta di pull, quindi eseguire automaticamente una build quando si verificano nuovi eventi.

Puoi usare un trigger di Cloud Build per avviare automaticamente una build ed eseguire il deployment di un flusso di lavoro da un repository Git. Puoi configurare il trigger per eseguire il deployment del flusso di lavoro per qualsiasi modifica al repository di origine oppure eseguire il deployment del flusso di lavoro solo quando la modifica corrisponde a criteri specifici.

Questo approccio può aiutarti a gestire il ciclo di vita del deployment. Ad esempio, puoi eseguire il deployment delle modifiche a un flusso di lavoro in un ambiente di gestione temporanea, eseguire test nell'ambiente e quindi avviare le modifiche in modo incrementale nell'ambiente di produzione. Per ulteriori informazioni, consulta Eseguire il deployment di un flusso di lavoro da un repository Git utilizzando Cloud Build.

Ottimizza l'utilizzo

Il costo per l'esecuzione di un flusso di lavoro è minimo. Tuttavia, in caso di utilizzo di volumi elevati, applica le seguenti linee guida per ottimizzare l'utilizzo e ridurre i costi:

  • Anziché utilizzare domini personalizzati, assicurati che le chiamate ai servizi Google Cloud utilizzino *.appspot.com, *.cloud.goog, *.cloudfunctions.net o *.run.app, in modo che ti vengano addebitati i costi per i passaggi interni e non esterni.

  • Applica un criterio personalizzato per i nuovi tentativi per bilanciare le esigenze di latenza e affidabilità con i costi. Ricorsi più frequenti riducono la latenza e aumentano l'affidabilità, ma possono anche aumentare i costi.

  • Se utilizzi connettori in attesa di operazioni a lunga esecuzione, imposta un criterio di polling personalizzato che ottimizzi la latenza in base ai costi. Ad esempio, se prevedi che un'operazione richieda più di un'ora, potresti voler creare un criterio che inizialmente esegue il polling dopo un minuto in caso di errore immediato e in seguito ogni 15 minuti.

  • Combina i compiti in un unico passaggio.

  • Evita l'uso eccessivo di sys.log passaggi. Prendi in considerazione l'utilizzo del logging delle chiamate.

Riepilogo delle best practice

La seguente tabella riassume i suggerimenti generali e le best practice consigliati in questo documento.

Suggerimenti di carattere generale
Best practice

Passaggi successivi