Cloud Functions HTTP em Python

1. Introdução

b158ce75c3cccd6d.png

O Python é uma linguagem de programação de código aberto muito usada por cientistas de dados, desenvolvedores de aplicativos da Web, administradores de sistemas e muito mais.

O Cloud Functions é uma plataforma de computação sem servidor orientada a eventos. O Cloud Functions permite escrever código sem se preocupar com o provisionamento de recursos ou o escalonamento para lidar com mudanças de requisitos.

Há dois tipos de Funções do Cloud:

  • As funções HTTP respondem a solicitações HTTP. Você criará algumas neste codelab.
  • As funções em segundo plano são acionadas por eventos, como a publicação de uma mensagem no Cloud Pub/Sub ou o upload de um arquivo no Cloud Storage. Isso não será abordado neste laboratório, mas confira mais informações na documentação.

efb3268e3b74ed4f.png

Este codelab orientará você na criação do seu próprio Cloud Functions no Python.

O que você vai criar

Neste codelab, você publicará uma função do Cloud que, quando invocada por HTTP, mostra a "Tecnologia Python" logotipo:

a7aaf656b78050fd.png

O que você vai aprender

  • Como escrever uma função HTTP do Cloud
  • Como escrever uma função do Cloud HTTP que usa argumentos.
  • Como testar uma função HTTP do Cloud
  • Como executar um servidor HTTP local do Python para testar a função.
  • Como criar uma função do Cloud HTTP que retorne uma imagem.

2. Configuração e requisitos

Configuração de ambiente personalizada

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

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • O Nome do projeto é o nome de exibição para os participantes do projeto. É uma string de caracteres não usada pelas APIs do Google e pode ser atualizada quando você quiser.
  • O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser mudado após a definição. O console do Cloud gera automaticamente uma string exclusiva. Em geral, não importa o que seja. Na maioria dos codelabs, é necessário fazer referência ao ID do projeto, normalmente identificado como PROJECT_ID. Se você não gostar do ID gerado, crie outro aleatório. Se preferir, teste o seu e confira se ele está disponível. Ele não pode ser mudado após essa etapa e permanece durante o projeto.
  • Para sua informação, há um terceiro valor, um Número do projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
  1. Em seguida, ative o faturamento no console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não vai ser muito cara, se tiver algum custo. Para encerrar os recursos e evitar cobranças além deste tutorial, exclua os recursos criados ou exclua o projeto. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.

Iniciar o Cloud Shell

Embora o Google Cloud possa ser operado remotamente em um laptop, neste codelab você vai usar o Cloud Shell, um ambiente de linha de comando executado no Cloud.

Ativar o Cloud Shell

  1. No Console do Cloud, clique em Ativar o Cloud Shell853e55310c205094.png.

3c1dabeca90e44e5.png.

Se você estiver iniciando o Cloud Shell pela primeira vez, verá uma tela intermediária com a descrição dele. Se aparecer uma tela intermediária, clique em Continuar.

9c92662c6a846a5c.png

Leva apenas alguns instantes para provisionar e se conectar ao Cloud Shell.

9f0e51b578fecce5.png

Essa máquina virtual tem 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. Grande parte do trabalho neste codelab, se não todo, pode ser feito em um navegador.

Depois de se conectar ao Cloud Shell, você verá sua autenticação e o projeto estará configurado com o ID do seu projeto.

  1. Execute o seguinte comando no Cloud Shell para confirmar se a conta está autenticada:
gcloud auth list

Resposta ao comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Execute o seguinte comando no Cloud Shell para confirmar que o comando gcloud sabe sobre seu projeto:
gcloud config list project

Resposta ao comando

[core]
project = <PROJECT_ID>

Se o projeto não estiver configurado, configure-o usando este comando:

gcloud config set project <PROJECT_ID>

Resposta ao comando

Updated property [core/project].

Verifique se as APIs Cloud Functions e Cloud Build estão ativadas

Execute o comando a seguir no Cloud Shell para verificar se as APIs Cloud Functions e Cloud Build estão ativadas:

gcloud services enable \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com

Observação: o Cloud Build será chamado pelo comando gcloud functions deploy e vai criar seu código automaticamente em uma imagem de contêiner.

Faça o download do código-fonte

No terminal do Cloud Shell, execute os seguintes comandos:

REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="cloud-functions-python-http"

git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR

Confira o conteúdo do diretório de origem:

ls

Você precisa ter os seguintes arquivos:

main.py  python-powered.png  test_main.py  web_app.py

3. Introdução ao Cloud Functions HTTP

O Cloud Functions HTTP em Python é escrito como funções comuns do Python. A função precisa aceitar um único argumento flask.Request, geralmente denominado request.

main.py

import flask


def hello_world(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello World! 👋"
    """
    response = "Hello World! 👋"

    return flask.Response(response, mimetype="text/plain")

# ...

Abra o arquivo com o editor de linha de comando de sua preferência (nano, vim ou emacs). Também é possível abri-lo no editor do Cloud Shell após definir o diretório de origem como um espaço de trabalho:

cloudshell open-workspace .

Vamos implantar essa função como uma função HTTP do Cloud usando o comando gcloud functions deploy:

FUNCTION_NAME="hello_world"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Saída de comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Observações sobre as opções de gcloud functions deploy:

  • --runtime: especifica o ambiente de execução da linguagem. Para Python, atualmente pode ser python37, python38, python39, python310 ou python312. Consulte Ambientes de execução.
  • --trigger-http: a função receberá um endpoint. Solicitações HTTP (POST, PUT, GET, DELETE e OPTIONS) para o endpoint acionarão a execução da função.
  • --allow-unauthenticated: a função será pública, permitindo todos os autores da chamada, sem verificar a autenticação.
  • Para saber mais, consulte gcloud functions deploy.

Para testar a função, clique no URL httpsTrigger.url exibido na resposta ao comando acima. Você também pode recuperar o URL de maneira programática e chamar a função com os seguintes comandos:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

O seguinte resultado será exibido:

Hello World! 👋

4. Como escrever uma função do Cloud HTTP que usa argumentos

As funções são mais versáteis quando aceitam argumentos. Vamos definir uma nova função hello_name compatível com um parâmetro name:

main.py

# ...

def hello_name(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - "Hello {NAME}! 🚀" if "name=NAME" is defined in the GET request
    - "Hello World! 🚀" otherwise
    """
    name = request.args.get("name", "World")
    response = f"Hello {name}! 🚀"

    return flask.Response(response, mimetype="text/plain")

# ...

Vamos implantar essa nova função:

FUNCTION_NAME="hello_name"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Saída de comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Para testar a função, clique no URL httpsTrigger.url exibido na resposta ao comando acima. Você também pode recuperar o URL de maneira programática e chamar a função com os seguintes comandos:

URL=$(gcloud functions describe $FUNCTION_NAME --format "value(httpsTrigger.url)")
curl -w "\n" $URL

Você vai receber este resultado padrão:

Hello World! 🚀

Você está recebendo o resultado padrão porque o argumento name não está definido. Adicione um parâmetro ao URL:

curl -w "\n" $URL?name=YOUR%20NAME

Desta vez, você receberá uma resposta personalizada:

Hello YOUR NAME! 🚀

A próxima etapa é adicionar testes de unidade para garantir que suas funções continuem funcionando conforme o esperado quando o código-fonte for atualizado.

5. Gravação de testes

O Cloud Functions HTTP em Python é testado usando o módulo unittest da biblioteca padrão. Não é preciso executar um emulador ou outra simulação para testar a função, apenas o código Python normal.

Veja como é um teste para as funções hello_world e hello_name:

test_main.py

import unittest
import unittest.mock

import main


class TestHello(unittest.TestCase):
    def test_hello_world(self):
        request = unittest.mock.Mock()

        response = main.hello_world(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 👋"

    def test_hello_name_no_name(self):
        request = unittest.mock.Mock(args={})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == "Hello World! 🚀"

    def test_hello_name_with_name(self):
        name = "FirstName LastName"
        request = unittest.mock.Mock(args={"name": name})

        response = main.hello_name(request)
        assert response.status_code == 200
        assert response.get_data(as_text=True) == f"Hello {name}! 🚀"
  1. Os testes Python são escritos da mesma forma que outros arquivos Python. Elas começam com um conjunto de importações e depois definem classes e funções.
  2. A declaração de teste está no formato class TestHello(TestCase). Precisa ser uma classe herdada de unittest.TestCase.
  3. A classe de teste tem métodos, e cada um deles precisa começar com test_, que representam casos de teste individuais.
  4. Cada caso de teste testa uma das nossas funções simulando o parâmetro request, ou seja, substituindo-o por um objeto falso pelos dados específicos necessários para o teste.
  5. Depois de invocar cada função, o teste verifica a resposta HTTP para ter certeza de que era o que esperávamos.

Como main.py depende de flask, verifique se o framework Flask está instalado no ambiente de teste:

pip install flask

A instalação do Flask gera um resultado semelhante ao seguinte:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Execute estes testes localmente:

python -m unittest

Os três testes de unidade precisam ser aprovados:

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

Em seguida, você criará uma nova função que retorna a mensagem "Python Powered" logotipo.

6. Como escrever o comando "Python Powered" Função do Cloud HTTP

Vamos tornar uma nova função um pouco mais interessante retornando o código imagem para cada solicitação:

a7aaf656b78050fd.png

A listagem a seguir mostra o código para fazer isso acontecer:

main.py

# ...

def python_powered(request: flask.Request) -> flask.Response:
    """HTTP Cloud Function.

    Returns:
    - The official "Python Powered" logo
    """
    return flask.send_file("python-powered.png")

Implante uma nova função python_powered:

FUNCTION_NAME="python_powered"

gcloud functions deploy $FUNCTION_NAME \
  --runtime python312 \
  --trigger-http \
  --allow-unauthenticated

Saída de comando:

...
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
...
entryPoint: FUNCTION_NAME
httpsTrigger:
  url: https://REGION-PROJECT_ID.cloudfunctions.net/FUNCTION_NAME
...

Para testar a função, clique no URL httpsTrigger.url exibido na resposta ao comando acima. Se tudo estiver funcionando corretamente, será exibida a mensagem "Python Powered" em uma nova guia do navegador.

Em seguida, crie um app para executar e testar a função localmente antes da implantação.

7. Como executar a função localmente

Para executar uma função HTTP localmente, crie um app da Web e chame sua função em uma rota. Você pode adicioná-la no mesmo diretório da função. O arquivo chamado web_app.py tem o seguinte conteúdo:

web_app.py

import flask

import main

app = flask.Flask(__name__)


@app.get("/")
def index():
    return main.python_powered(flask.request)


if __name__ == "__main__":
    # Local development only
    # Run "python web_app.py" and open http://localhost:8080
    app.run(host="localhost", port=8080, debug=True)
  1. Esse arquivo cria um aplicativo Flask.
  2. Ela registra uma rota no URL base, que é processada com uma função chamada index().
  3. Em seguida, a função index() chama a função python_powered, transmitindo a solicitação atual.

Verifique se o framework Flask está instalado no seu ambiente de desenvolvimento:

pip install flask

A instalação do Flask gera um resultado semelhante ao seguinte:

Collecting flask
...
Successfully installed ... flask-3.0.2 ...

Para executar este aplicativo localmente, execute o seguinte comando:

python web_app.py

Agora use a visualização da Web do Cloud Shell para testar o app da Web no seu navegador. No Cloud Shell, clique em "Visualização na Web". e selecione "Visualizar na porta 8080":

6c9ff9e5c692c58e.gif

O Cloud Shell abre o URL de visualização no serviço de proxy dele em uma nova janela do navegador. A visualização da Web restringe o acesso HTTPS apenas à sua conta de usuário. Se tudo estiver funcionando corretamente, você verá a mensagem "Python Powered" logotipo.

8e5c3ead11cfd103.png

8. Parabéns!

b158ce75c3cccd6d.png

Você implantou funções do Cloud HTTP usando funções idiomáticas que processam solicitações da Web com o framework Flask.

Os preços do Cloud Functions são baseados na frequência com que sua função é invocada, incluindo um nível sem custo financeiro para funções que não são executadas com frequência. Depois de testar o Cloud Functions, é possível excluí-lo usando gcloud:

gcloud functions delete hello_world --quiet
gcloud functions delete hello_name --quiet
gcloud functions delete python_powered --quiet

Também é possível excluir as funções no console do Google Cloud.

Esperamos que você goste de usar o Cloud Functions em Python.