Tutorial sobre como usar o Workflows com o Cloud Run e o Cloud Functions

Neste tutorial, você aprende a usar fluxos de trabalho para vincular uma série de serviços. Ao conectar dois serviços HTTP públicos usando funções do Cloud Run, uma API REST externa e um serviço particular do Cloud Run, é possível criar um aplicativo flexível e sem servidor.

Implantar a primeira função do Cloud Run

Depois de receber uma solicitação HTTP, essa função HTTP gera um número aleatório entre 1 e 100 e, em seguida, retorna o número no formato JSON.

  1. Crie um diretório chamado randomgen e mude para ele:

    mkdir ~/randomgen
    cd ~/randomgen
  2. Crie um arquivo de texto com o nome de arquivo main.py que contenha o seguinte código Python:

    import functions_framework
    import random
    from flask import jsonify
    
    
    @functions_framework.http
    def randomgen(request):
        randomNum = random.randint(1, 100)
        output = {"random": randomNum}
        return jsonify(output)
  3. Para aceitar uma dependência do Flask para processamento HTTP, crie um arquivo de texto para o gerenciador de pacotes pip. Nomeie o arquivo como requirements.txt e adicione o seguinte:

    flask>=1.0.2
    functions-framework==3.0.0
  4. Implante a função com um gatilho HTTP e permita acesso não autenticado:

    gcloud functions deploy randomgen-function \
        --gen2 \
        --runtime python310 \
        --entry-point=randomgen \
        --trigger-http \
        --allow-unauthenticated

    A função pode levar alguns minutos para ser implantada. Se preferir, use a interface de funções do Cloud Run no console Google Cloud para implantar a função.

  5. Depois que a função randomgen for implantada, confirme a propriedade httpsTrigger.url:

    gcloud functions describe randomgen-function \
        --gen2 \
        --format="value(serviceConfig.uri)"
  6. Salve o URL. É necessário adicioná-lo ao arquivo de origem do fluxo de trabalho em exercícios posteriores.

  7. Teste a função com o seguinte comando curl:

    curl $(gcloud functions describe randomgen-function \
        --gen2 \
        --format="value(serviceConfig.uri)")

    Um número é gerado e retornado aleatoriamente.

Implantar a segunda função do Cloud Run

Depois de receber uma solicitação HTTP, essa função HTTP extrai o input do corpo JSON, multiplica-o por dois e retorna o resultado no formato JSON.

  1. Volte ao diretório principal.

    cd ~
  2. Crie um diretório chamado multiply e mude para ele:

    mkdir ~/multiply
    cd ~/multiply
  3. Crie um arquivo de texto com o nome de arquivo main.py que contenha o seguinte código Python:

    import functions_framework
    from flask import jsonify
    
    
    @functions_framework.http
    def multiply(request):
        request_json = request.get_json()
        output = {"multiplied": 2 * request_json['input']}
        return jsonify(output)
  4. Para aceitar uma dependência do Flask para processamento HTTP, crie um arquivo de texto para o gerenciador de pacotes pip. Nomeie o arquivo como requirements.txt e adicione o seguinte:

    flask>=1.0.2
    functions-framework==3.0.0
  5. Implante a função com um gatilho HTTP e permita acesso não autenticado:

    gcloud functions deploy multiply-function \
        --gen2 \
        --runtime python310 \
        --entry-point=multiply \
        --trigger-http \
        --allow-unauthenticated

    A função pode levar alguns minutos para ser implantada. Se preferir, use a interface de funções do Cloud Run no console Google Cloud para implantar a função.

  6. Depois que a função multiply for implantada, confirme a propriedade httpsTrigger.url:

    gcloud functions describe multiply-function \
        --gen2\
        --format="value(serviceConfig.uri)"
  7. Salve o URL. É necessário adicioná-lo ao arquivo de origem do fluxo de trabalho em exercícios posteriores.

  8. Teste a função com o seguinte comando curl:

    curl -X POST MULTIPLY_FUNCTION_URL \
        -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
        -H "Content-Type: application/json" \
        -d '{"input": 5}'

    O número 10 deve ser retornado.

Conectar as duas funções do Cloud Run em um fluxo de trabalho

Um fluxo de trabalho é composto por uma série de etapas descritas usando a sintaxe dos fluxos de trabalho, que pode ser escrita em formato YAML ou JSON. Essa é a definição do fluxo de trabalho. Para uma explicação detalhada, consulte a página Referência de sintaxe.

  1. Volte ao diretório principal.

    cd ~
  2. Crie um arquivo de texto com o nome workflow.yaml que contenha o conteúdo a seguir:

    - randomgen_function:
        call: http.get
        args:
            url: RANDOMGEN_FUNCTION_URL
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: MULTIPLY_FUNCTION_URL
            body:
                input: ${randomgen_result.body.random}
        result: multiply_result
    - return_result:
        return: ${multiply_result}
    

    Esse arquivo de origem vincula as duas funções HTTP e retorna um resultado final.

  3. Depois de criar o fluxo de trabalho, é possível implantá-lo, o que o prepara para a execução.

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

    Substitua WORKFLOW_NAME por um nome para o fluxo de trabalho.

  4. Execute o fluxo de trabalho:

    gcloud workflows run WORKFLOW_NAME

    Uma execução é uma única execução da lógica contida na definição de um fluxo de trabalho. Todas as execuções de fluxo de trabalho são independentes, e o rápido escalonamento do Workflows permite um grande número de execuções simultâneas.

    Depois que o fluxo de trabalho for executado, o resultado será semelhante a este:

    result: '{"body":{"multiplied":120},"code":200,"headers":{"Alt-Svc":"h3-29=\":443\";
    ...
    startTime: '2021-05-05T14:17:39.135251700Z'
    state: SUCCEEDED
    ...
    

Conectar um serviço REST público no fluxo de trabalho

Atualizar seu fluxo de trabalho atual e conectar uma API REST pública (math.js) que pode avaliar expressões matemáticas. Por exemplo, curl https://api.mathjs.org/v4/?'expr=log(56)'.

Como você implantou seu fluxo de trabalho, também pode editá-lo na página "Fluxos de trabalho" no console Google Cloud .

  1. Edite o arquivo de origem do fluxo de trabalho e substitua-o pelo conteúdo a seguir:

    - randomgen_function:
        call: http.get
        args:
            url: RANDOMGEN_FUNCTION_URL
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: MULTIPLY_FUNCTION_URL
            body:
                input: ${randomgen_result.body.random}
        result: multiply_result
    - log_function:
        call: http.get
        args:
            url: https://api.mathjs.org/v4/
            query:
                expr: ${"log(" + string(multiply_result.body.multiplied) + ")"}
        result: log_result
    - return_result:
        return: ${log_result}
    

    Isso vincula o serviço REST externo às funções do Cloud Run e retorna um resultado final.

  2. Implante o fluxo de trabalho modificado:

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

Implantar um serviço do Cloud Run

Implantar um serviço do Cloud Run que, depois de receber uma solicitação HTTP, extrai input do corpo JSON, calcula o math.floor e retorna o resultado.

  1. Crie um diretório chamado floor e mude para ele:

    mkdir ~/floor
    cd ~/floor
  2. Crie um arquivo de texto com o nome de arquivo app.py que contenha o seguinte código Python:

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

  3. No mesmo diretório, crie um Dockerfile com o seguinte conteúdo:

    # 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

  4. Crie um repositório padrão do Artifact Registry para armazenar a imagem do contêiner do Docker:

    gcloud artifacts repositories create REPOSITORY \
        --repository-format=docker \
        --location=${REGION}

    Substitua REPOSITORY por um nome exclusivo para o repositório.

  5. Crie a imagem do contêiner:

    export SERVICE_NAME=floor
    gcloud builds submit --tag ${REGION}-docker.pkg.dev/PROJECT_ID/REPOSITORY/${SERVICE_NAME}
  6. Implante a imagem do contêiner no Cloud Run, garantindo que ele aceite apenas chamadas autenticadas:

    gcloud run deploy ${SERVICE_NAME} \
        --image ${REGION}-docker.pkg.dev/PROJECT_ID/REPOSITORY/${SERVICE_NAME}:latest \
        --no-allow-unauthenticated

Quando você vir o URL do serviço, a implantação estará concluída. Você precisará especificar esse URL ao atualizar a definição do fluxo de trabalho.

Conectar o serviço Cloud Run no fluxo de trabalho

Atualize o fluxo de trabalho atual e especifique o URL do serviço do Cloud Run.

  1. Volte ao diretório principal.

    cd ~
  2. Edite o arquivo de origem do fluxo de trabalho e substitua-o pelo conteúdo a seguir:

    - randomgen_function:
        call: http.get
        args:
            url: RANDOMGEN_FUNCTION_URL
        result: randomgen_result
    - multiply_function:
        call: http.post
        args:
            url: MULTIPLY_FUNCTION_URL
            body:
                input: ${randomgen_result.body.random}
        result: multiply_result
    - log_function:
        call: http.get
        args:
            url: https://api.mathjs.org/v4/
            query:
                expr: ${"log(" + string(multiply_result.body.multiplied) + ")"}
        result: log_result
    - floor_function:
        call: http.post
        args:
            url: CLOUD_RUN_SERVICE_URL
            auth:
                type: OIDC
            body:
                input: ${log_result.body}
        result: floor_result
    - create_output_map:
        assign:
          - outputMap:
              randomResult: ${randomgen_result}
              multiplyResult: ${multiply_result}
              logResult: ${log_result}
              floorResult: ${floor_result}
    - return_output:
        return: ${outputMap}
    
    • Substitua RANDOMGEN_FUNCTION_URL pelo URL da função randomgen.
    • Substitua MULTIPLY_FUNCTION_URL pelo URL da função multiply.
    • Substitua CLOUD_RUN_SERVICE_URL pelo URL do serviço do Cloud Run.

    Isso conecta o serviço Cloud Run ao fluxo de trabalho. A chave auth garante que um token de autenticação seja transmitido na chamada ao serviço do Cloud Run. Para mais informações, consulte Fazer solicitações autenticadas de um fluxo de trabalho.

  3. Implante o fluxo de trabalho modificado:

    gcloud workflows deploy WORKFLOW_NAME \
        --source=workflow.yaml \
        --service-account=${SERVICE_ACCOUNT}@PROJECT_ID.iam.gserviceaccount.com
  4. Executar o fluxo de trabalho final:

    gcloud workflows run WORKFLOW_NAME

    A saída será semelhante a esta:

    result: '{"floorResult":{"body":"4","code":200
      ...
      "logResult":{"body":"4.02535169073515","code":200
      ...
      "multiplyResult":{"body":{"multiplied":56},"code":200
      ...
      "randomResult":{"body":{"random":28},"code":200
      ...
    startTime: '2023-11-13T21:22:56.782669001Z'
    state: SUCCEEDED
    

Parabéns! Você implantou e executou um fluxo de trabalho que conecta uma série de serviços.

Para criar fluxos de trabalho mais complexos usando expressões, saltos condicionais, codificação/decodificação Base64, subfluxos de trabalho e muito mais, consulte a Referência de sintaxe dos fluxos de trabalho e a Visão geral da biblioteca padrão.