Introdução à orquestração sem servidor com o Workflows

1. Introdução

c9b0cc839df0bb8f.png

É possível usar fluxos de trabalho para criar fluxos de trabalho sem servidor que vinculam uma série de tarefas sem servidor na ordem que você definir. É possível combinar o poder das APIs do Google Cloud, produtos sem servidor, como o Cloud Functions e o Cloud Run, e chamadas para APIs externas para criar aplicativos flexíveis sem servidor.

Os fluxos de trabalho não exigem gerenciamento de infraestrutura e são escalonados perfeitamente de acordo com a demanda, incluindo redução da escala a zero. Com o modelo de preços de pagamento por uso, você paga apenas pelo tempo de execução.

Neste codelab, você vai aprender a conectar vários serviços do Google Cloud e APIs HTTP externas com o Workflows. Mais especificamente, você vai conectar dois serviços públicos do Cloud Functions, um serviço particular do Cloud Run e uma API HTTP pública externa a um fluxo de trabalho.

O que você vai aprender

  • Noções básicas do Workflows.
  • Como conectar o Cloud Functions público com o Workflows.
  • Como conectar serviços particulares do Cloud Run com o Workflows.
  • Como conectar APIs HTTP externas ao Workflows.

2. Configuração e requisitos

Configuração de ambiente autoguiada

  1. Faça login no Console do Cloud e crie um novo projeto ou reutilize um existente. Crie uma se você ainda não tiver uma conta do Gmail ou do G Suite.

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

Lembre-se do código do projeto, um nome exclusivo em todos os projetos do Google Cloud. O nome acima já foi escolhido e não servirá para você. Faremos referência a ele mais adiante neste codelab como PROJECT_ID.

  1. Em seguida, será necessário ativar o faturamento no Console do Cloud para usar os recursos do Google Cloud.

A execução deste codelab não será muito cara, se for o caso. Siga todas as instruções na seção "Limpeza", que orienta você sobre como encerrar recursos para não incorrer em cobranças além deste tutorial. Novos usuários do Google Cloud estão qualificados para o programa de avaliação gratuita de US$ 300.

Inicie o Cloud Shell

Embora o Google Cloud e o Spanner possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.

No Console do GCP, clique no ícone do Cloud Shell na barra de ferramentas localizada no canto superior direito:

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

O provisionamento e a conexão com o ambiente levarão apenas alguns instantes para serem concluídos: Quando o processamento for concluído, você verá algo como:

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

Essa máquina virtual contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Todo o trabalho neste laboratório pode ser feito apenas com um navegador.

3. Visão geral dos fluxos de trabalho

Noções básicas

Um fluxo de trabalho é composto por uma série de etapas descritas usando a sintaxe baseada em YAML do Workflows. Essa é a definição do fluxo de trabalho. Para uma explicação detalhada sobre a sintaxe YAML do Workflows, consulte a página Referência de sintaxe.

Quando um fluxo de trabalho é criado, ele é implantado e fica pronto para execução. Uma execução é uma única execução da lógica contida na definição de um fluxo de trabalho. Todas as execuções do fluxo de trabalho são independentes, e o produto aceita um alto número de execuções simultâneas.

Ativar serviços

Neste codelab, você vai conectar os serviços do Cloud Functions e do Cloud Run com o Workflows. Você também vai usar o Cloud Build e o Cloud Storage durante a criação de serviços.

Ative todos os serviços necessários:

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

Na próxima etapa, você vai conectar duas funções do Cloud em um fluxo de trabalho.

4. Implantar a primeira função do Cloud

A primeira função é um gerador de números aleatórios em Python.

Crie e navegue até um diretório para o código da função:

mkdir ~/randomgen
cd ~/randomgen

Crie um arquivo main.py no diretório com o seguinte conteúdo:

import random, json
from flask import jsonify

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

Quando recebe uma solicitação HTTP, essa função gera um número aleatório entre 1 e 100 e retorna no formato JSON de volta ao autor da chamada.

A função depende do Flask para o processamento HTTP, e precisamos adicionar isso como uma dependência. Dependências no Python são gerenciadas com pip e expressas em um arquivo de metadados chamado requirements.txt.

Crie um arquivo requirements.txt no mesmo diretório com o seguinte conteúdo:

flask>=1.0.2

Implante a função com um gatilho HTTP e com solicitações não autenticadas permitidas com este comando:

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

Depois que a função for implantada, você verá o URL dela na propriedade httpsTrigger.url exibida no console ou com o comando gcloud functions describe.

Você também pode acessar esse URL da função com o seguinte comando curl:

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

A função está pronta para o fluxo de trabalho.

5. Implantar a segunda função do Cloud

A segunda função é um multiplicador. Ela multiplica a entrada recebida por 2.

Crie e navegue até um diretório para o código da função:

mkdir ~/multiply
cd ~/multiply

Crie um arquivo main.py no diretório com o seguinte conteúdo:

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 recebe uma solicitação HTTP, essa função extrai o input do corpo JSON, o multiplica por 2 e retorna no formato JSON de volta para o autor da chamada.

Crie o mesmo arquivo requirements.txt no mesmo diretório com o seguinte conteúdo:

flask>=1.0.2

Implante a função com um gatilho HTTP e com solicitações não autenticadas permitidas com este comando:

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

Depois que a função for implantada, também será possível acessar esse URL da função com o seguinte comando curl:

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

A função está pronta para o fluxo de trabalho.

6. Conecte duas funções do Cloud

No primeiro fluxo de trabalho, conecte as duas funções.

Crie um arquivo workflow.yaml com o seguinte conteúdo.

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

Neste fluxo de trabalho, você recebe um número aleatório da primeira função e o transmite para a segunda função. O resultado é o número aleatório multiplicado.

Implante o primeiro fluxo de trabalho:

gcloud workflows deploy workflow --source=workflow.yaml

Execute o primeiro fluxo de trabalho:

gcloud workflows execute workflow

Depois que o fluxo de trabalho é executado, você pode ver o resultado ao transmitir o ID da execução fornecido na etapa anterior:

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

A saída vai incluir result e state:

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

...
state: SUCCEEDED

7. Conectar uma API HTTP externa

Em seguida, conecte math.js como um serviço externo no fluxo de trabalho.

No math.js, é possível avaliar expressões matemáticas como estas:

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

Desta vez, você vai usar o console do Cloud para atualizar nosso fluxo de trabalho. Encontre Workflows no Console do Google Cloud:

7608a7991b33bbb0.png

Encontre seu fluxo de trabalho e clique na guia Definition:

f3c8c4d3ffa49b1b.png

Edite a definição do fluxo de trabalho e inclua uma chamada para 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}

O fluxo de trabalho agora alimenta a saída da função de multiplicação em uma chamada de função de registro em math.js.

A interface vai guiar você para editar e implantar o fluxo de trabalho. Depois de implantado, clique em Execute para executar o fluxo de trabalho. Você verá os detalhes da execução:

b40c76ee43a1ce65.png

Observe o código de status 200 e um body com a saída da função de registro.

Você integrou um serviço externo ao nosso fluxo de trabalho. Muito legal!

8. Implantar um serviço do Cloud Run

Na última parte, finalize o fluxo de trabalho com uma chamada para um serviço particular do Cloud Run. Isso significa que o fluxo de trabalho precisa ser autenticado para chamar o serviço do Cloud Run.

O serviço do Cloud Run retorna o math.floor do número transmitido.

Crie e navegue até um diretório para o código de serviço:

mkdir ~/floor
cd ~/floor

Crie um arquivo app.py no diretório com o seguinte conteúdo:

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

Como o Cloud Run implanta contêineres, você precisa de um Dockerfile, e o contêiner precisa se vincular às variáveis de ambiente 0.0.0.0 e PORT. Por isso, o código acima é usado.

Quando recebe uma solicitação HTTP, essa função extrai o input do corpo JSON, chama math.floor e retorna o resultado para o autor da chamada.

No mesmo diretório, crie o seguinte 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

Criar o contêiner:

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

Depois que o contêiner for criado, implante no Cloud Run. Observe a sinalização no-allow-unauthenticated. Isso garante que o serviço aceite apenas chamadas autenticadas:

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

Depois de implantado, o serviço estará pronto para o fluxo de trabalho.

9. Conectar o serviço do Cloud Run

Antes de configurar o Workflows para chamar o serviço particular do Cloud Run, você precisa criar uma conta de serviço para o Workflows:

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

Conceda o papel run.invoker à conta de serviço. Assim, a conta de serviço poderá chamar os serviços autenticados do Cloud Run:

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

Atualize a definição do fluxo de trabalho em workflow.yaml para incluir o serviço do Cloud Run. Observe como você também está incluindo o campo auth para garantir que os fluxos de trabalho transmitam o token de autenticação nas chamadas para o serviço do 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}

Atualizar o fluxo de trabalho. Desta vez, passando a conta de serviço:

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

Execute o fluxo de trabalho:

gcloud workflows execute workflow

Em alguns segundos, confira o resultado da execução do fluxo de trabalho:

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

A saída vai incluir um número inteiro result e state:

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

...
state: SUCCEEDED

10. Parabéns!

Parabéns por concluir o codelab.

O que vimos

  • Noções básicas do Workflows.
  • Como conectar o Cloud Functions público com o Workflows.
  • Como conectar serviços particulares do Cloud Run com o Workflows.
  • Como conectar APIs HTTP externas ao Workflows.