Introduzione all'orchestrazione serverless con Workflows

1. Introduzione

c9b0cc839df0bb8f.png

Puoi utilizzare Workflows per creare flussi di lavoro serverless che collegano una serie di attività serverless in un ordine definito da te. Puoi combinare la potenza delle API di Google Cloud, dei prodotti serverless come Cloud Functions e Cloud Run e delle chiamate ad API esterne per creare applicazioni serverless flessibili.

Workflows non richiede la gestione dell'infrastruttura e scala senza interruzioni di pari passo con la domanda, compresa la scalabilità verso il basso. Grazie al modello di prezzi a consumo, paghi solo in base al tempo di esecuzione.

In questo codelab, imparerai a connettere vari servizi Google Cloud e API HTTP esterne a Workflows. In particolare, collegherai a un flusso di lavoro due servizi Cloud Functions pubblici, un servizio Cloud Run privato e un'API HTTP pubblica esterna.

Obiettivi didattici

  • Nozioni di base di Workflows
  • Come connettere le funzioni Cloud Functions pubbliche a Workflows.
  • Come connettere i servizi Cloud Run privati a Workflows.
  • Come connettere le API HTTP esterne con Workflows.

2. Configurazione e requisiti

Configurazione dell'ambiente da seguire in modo autonomo

  1. Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o G Suite, devi crearne uno.

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

Ricorda l'ID progetto, un nome univoco in tutti i progetti Google Cloud (il nome precedente è già stato utilizzato e non funzionerà correttamente). Verrà indicato più avanti in questo codelab come PROJECT_ID.

  1. Successivamente, dovrai abilitare la fatturazione in Cloud Console per utilizzare le risorse Google Cloud.

Eseguire questo codelab non dovrebbe costare molto. Assicurati di seguire le istruzioni nella sezione "Pulizia" in cui viene spiegato come arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

Avvia Cloud Shell

Anche se Google Cloud può essere utilizzato da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Dalla console di Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

Dovrebbe richiedere solo qualche istante per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere una schermata simile al seguente:

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

Questa macchina virtuale viene caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Tutto il lavoro in questo lab può essere svolto semplicemente con un browser.

3. Panoramica di Workflows

Nozioni di base

Un flusso di lavoro è costituito da una serie di passaggi descritti utilizzando la sintassi basata su YAML di Workflows. Questa è la definizione del flusso di lavoro. Per una spiegazione dettagliata della sintassi YAML di Workflows, consulta la pagina Riferimento per la sintassi.

Quando viene creato un flusso di lavoro, ne viene eseguito il deployment, in modo che il flusso di lavoro sia pronto per l'esecuzione. Un'esecuzione è una singola esecuzione della logica contenuta nella definizione di un flusso di lavoro. Tutte le esecuzioni dei flussi di lavoro sono indipendenti e il prodotto supporta un numero elevato di esecuzioni simultanee.

Attivare i servizi

In questo codelab, collegherai Cloud Functions, i servizi Cloud Run a Workflows. Utilizzerai Cloud Build e Cloud Storage anche durante la creazione dei servizi.

Abilita tutti i servizi necessari:

gcloud services enable \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  workflows.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com

Nel passaggio successivo collegherai due funzioni Cloud Functions in un flusso di lavoro.

4. Esegui il deployment della prima funzione Cloud Functions

La prima funzione è un generatore di numeri casuali in Python.

Crea e passa a una directory per il codice della funzione:

mkdir ~/randomgen
cd ~/randomgen

Crea un file main.py nella directory con il seguente contenuto:

import random, json
from flask import jsonify

def randomgen(request):
    randomNum = random.randint(1,100)
    output = {"random":randomNum}
    return jsonify(output)

Quando riceve una richiesta HTTP, questa funzione genera un numero casuale compreso tra 1 e 100 e restituisce in formato JSON al chiamante.

La funzione si basa su Flask per l'elaborazione HTTP e dobbiamo aggiungerla come dipendenza. Le dipendenze in Python sono gestite con pip ed espresse in un file di metadati chiamato requirements.txt.

Crea un file requirements.txt nella stessa directory con il seguente contenuto:

flask>=1.0.2

Esegui il deployment della funzione con un trigger HTTP e con le richieste non autenticate consentite con questo comando:

gcloud functions deploy randomgen \
    --runtime python37 \
    --trigger-http \
    --allow-unauthenticated

Una volta eseguito il deployment della funzione, puoi vedere l'URL della funzione nella proprietà httpsTrigger.url visualizzato nella console o con il comando gcloud functions describe.

Puoi anche visitare l'URL della funzione con il seguente comando curl:

curl $(gcloud functions describe randomgen --format='value(httpsTrigger.url)')

La funzione è pronta per il flusso di lavoro.

5. Esegui il deployment della seconda funzione Cloud Functions

La seconda funzione è un moltiplicatore. Moltiplica l'input ricevuto per 2.

Crea e passa a una directory per il codice della funzione:

mkdir ~/multiply
cd ~/multiply

Crea un file main.py nella directory con il seguente contenuto:

import random, json
from flask import jsonify

def multiply(request):
    request_json = request.get_json()
    output = {"multiplied":2*request_json['input']}
    return jsonify(output)

Quando riceve una richiesta HTTP, questa funzione estrae input dal corpo JSON, lo moltiplica per 2 e restituisce in formato JSON al chiamante.

Crea lo stesso file requirements.txt nella stessa directory con il seguente contenuto:

flask>=1.0.2

Esegui il deployment della funzione con un trigger HTTP e con le richieste non autenticate consentite con questo comando:

gcloud functions deploy multiply \
    --runtime python37 \
    --trigger-http \
    --allow-unauthenticated

Una volta eseguito il deployment della funzione, puoi anche visitare l'URL della funzione con il seguente comando curl:

curl $(gcloud functions describe multiply --format='value(httpsTrigger.url)') \
-X POST \
-H "content-type: application/json" \
-d '{"input": 5}'

La funzione è pronta per il flusso di lavoro.

6. Connetti due funzioni Cloud Functions

Nel primo flusso di lavoro, collega le due funzioni.

Crea un file workflow.yaml con i seguenti contenuti.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- returnResult:
    return: ${multiplyResult}

In questo flusso di lavoro, si ottiene un numero casuale dalla prima funzione e lo si passa alla seconda funzione. Il risultato è il numero casuale moltiplicato.

Esegui il deployment del primo flusso di lavoro:

gcloud workflows deploy workflow --source=workflow.yaml

Esegui il primo flusso di lavoro:

gcloud workflows execute workflow

Una volta eseguito il flusso di lavoro, puoi vedere il risultato passando l'ID esecuzione fornito nel passaggio precedente:

gcloud workflows executions describe <your-execution-id> --workflow workflow

L'output includerà result e state:

result: '{"body":{"multiplied":108},"code":200 ... } 

...
state: SUCCEEDED

7. Connetti un'API HTTP esterna

Successivamente, collegherai math.js come servizio esterno nel flusso di lavoro.

In math.js puoi valutare espressioni matematiche come la seguente:

curl https://api.mathjs.org/v4/?'expr=log(56)'

Questa volta utilizzerai la console Cloud per aggiornare il flusso di lavoro. Trova Workflows nella console Google Cloud:

7608a7991b33bbb0.png

Individua il flusso di lavoro e fai clic sulla scheda Definition:

f3c8c4d3ffa49b1b.png

Modifica la definizione del flusso di lavoro e includi una chiamata a math.js.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- returnResult:
    return: ${logResult}

Il flusso di lavoro ora invia l'output della funzione di moltiplicazione in una chiamata di funzione di log in math.js.

La UI ti guiderà nella modifica e nel deployment del flusso di lavoro. Una volta eseguito il deployment, fai clic su Execute per eseguire il flusso di lavoro. Verranno visualizzati i dettagli dell'esecuzione:

b40c76ee43a1ce65.png

Osserva il codice di stato 200 e body con l'output della funzione di log.

Hai appena integrato un servizio esterno nel nostro flusso di lavoro, fantastico!

8. Esegui il deployment di un servizio Cloud Run

Nell'ultima parte, finalizza il flusso di lavoro con una chiamata a un servizio Cloud Run privato. Ciò significa che il flusso di lavoro deve essere autenticato per chiamare il servizio Cloud Run.

Il servizio Cloud Run restituisce il valore math.floor del numero passato.

Crea e passa a una directory per il codice del servizio:

mkdir ~/floor
cd ~/floor

Crea un file app.py nella directory con il seguente contenuto:

import json
import logging
import os
import math

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_post():
    content = json.loads(request.data)
    input = float(content['input'])
    return f"{math.floor(input)}", 200

if __name__ != '__main__':
    # Redirect Flask logs to Gunicorn logs
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
    app.logger.info('Service started...')
else:
    app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

Cloud Run esegue il deployment dei container, quindi hai bisogno di un'istruzione Dockerfile e il container deve essere associato alle variabili env 0.0.0.0 e PORT, da cui deriva il codice riportato sopra.

Quando riceve una richiesta HTTP, questa funzione estrae input dal corpo JSON, chiama math.floor e restituisce il risultato al chiamante.

Nella stessa directory, crea il seguente Dockerfile:

# Use an official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.7-slim

# Install production dependencies.
RUN pip install Flask gunicorn

# Copy local code to the container image.
WORKDIR /app
COPY . .

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
CMD exec gunicorn --bind 0.0.0.0:8080 --workers 1 --threads 8 app:app

Crea il container:

export SERVICE_NAME=floor
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

Dopo aver creato il container, esegui il deployment in Cloud Run. Nota il flag no-allow-unauthenticated. In questo modo il servizio accetta solo le chiamate autenticate:

gcloud run deploy ${SERVICE_NAME} \
  --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
  --platform managed \
  --no-allow-unauthenticated

Una volta eseguito il deployment, il servizio è pronto per il flusso di lavoro.

9. Connetti il servizio Cloud Run

Prima di poter configurare Workflows in modo che chiami il servizio Cloud Run privato, devi creare un account di servizio da utilizzare:

export SERVICE_ACCOUNT=workflows-sa
gcloud iam service-accounts create ${SERVICE_ACCOUNT}

Concedi il ruolo run.invoker all'account di servizio. Ciò consentirà all'account di servizio di chiamare i servizi Cloud Run autenticati:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role "roles/run.invoker"

Aggiorna la definizione del flusso di lavoro in workflow.yaml per includere il servizio Cloud Run. Nota che stai includendo anche il campo auth per assicurarti che Workflows passi il token di autenticazione nelle sue chiamate al servizio Cloud Run:

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- floorFunction:
    call: http.post
    args:
        url: https://floor-<random-hash>.run.app
        auth:
            type: OIDC
        body:
            input: ${logResult.body}
    result: floorResult
- returnResult:
    return: ${floorResult}

Aggiorna il flusso di lavoro. Questa volta che passa nell'account di servizio:

gcloud workflows deploy workflow \
    --source=workflow.yaml \
    --service-account=${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

Esegui il flusso di lavoro:

gcloud workflows execute workflow

In pochi secondi, puoi esaminare l'esecuzione del flusso di lavoro per vedere il risultato:

gcloud workflows executions describe <your-execution-id> --workflow workflow

L'output includerà un numero intero result e state:

result: '{"body":"5","code":200 ... } 

...
state: SUCCEEDED

10. Complimenti

Complimenti per aver completato il codelab.

Argomenti trattati

  • Nozioni di base di Workflows
  • Come connettere le funzioni Cloud Functions pubbliche a Workflows.
  • Come connettere i servizi Cloud Run privati a Workflows.
  • Come connettere le API HTTP esterne con Workflows.