Funkcje HTTP w Cloud Functions w Pythonie

1. Wprowadzenie

b158ce75c3cccd6d.png

Python to popularny język programowania open source, z którego korzystają m.in. analitycy danych, deweloperzy aplikacji internetowych i administratorzy systemów.

Cloud Functions to bezserwerowa platforma obliczeniowa oparta na zdarzeniach. Cloud Functions umożliwia pisanie kodu bez martwienia się o udostępnianie zasobów czy skalowanie w celu obsługi zmieniających się wymagań.

Istnieją 2 rodzaje funkcji Cloud Functions:

  • Funkcje HTTP odpowiadają na żądania HTTP. W tym ćwiczeniu utworzysz kilka takich funkcji.
  • Funkcje działające w tle są aktywowane przez zdarzenia, takie jak opublikowanie wiadomości w usłudze Cloud Pub/Sub lub przesłanie pliku do Cloud Storage. Nie zajmujemy się tym w tym laboratorium, ale więcej informacji znajdziesz w dokumentacji.

efb3268e3b74ed4f.png

W tym samouczku dowiesz się, jak tworzyć własne funkcje w Cloud Functions w Pythonie.

Co utworzysz

W tym laboratorium opublikujesz funkcję Cloud Functions, która po wywołaniu przez HTTP wyświetla logo „Python Powered”:

a7aaf656b78050fd.png

Czego się nauczysz

  • Jak napisać funkcję HTTP w Cloud Functions.
  • Jak napisać funkcję HTTP w Cloud Functions, która przyjmuje argumenty.
  • Jak przetestować funkcję HTTP w Cloud Functions.
  • Jak uruchomić lokalny serwer HTTP w Pythonie, aby przetestować funkcję.
  • Jak napisać funkcję HTTP w Cloud Functions, która zwraca obraz.

2. Konfiguracja i wymagania

Samodzielne konfigurowanie środowiska

  1. Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Zawsze możesz ją zaktualizować.
  • Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić po ustawieniu. Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie musisz się nim przejmować. W większości ćwiczeń z programowania musisz odwoływać się do identyfikatora projektu (zwykle oznaczanego jako PROJECT_ID). Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować inny losowy identyfikator. Możesz też spróbować własnej nazwy i sprawdzić, czy jest dostępna. Po tym kroku nie można go zmienić i pozostaje on taki przez cały czas trwania projektu.
  • Warto wiedzieć, że istnieje też trzecia wartość, numer projektu, której używają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
  1. Następnie musisz włączyć płatności w konsoli Cloud, aby korzystać z zasobów i interfejsów API Google Cloud. Wykonanie tego laboratorium nie będzie kosztować dużo, a może nawet nic. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.

Uruchamianie Cloud Shell

Z Google Cloud można korzystać zdalnie na laptopie, ale w tym module użyjemy Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.

Aktywowanie Cloud Shell

  1. W konsoli Cloud kliknij Aktywuj Cloud Shell 853e55310c205094.png.

3c1dabeca90e44e5.png

Jeśli uruchamiasz Cloud Shell po raz pierwszy, zobaczysz ekran pośredni z opisem tego środowiska. Jeśli pojawił się ekran pośredni, kliknij Dalej.

9c92662c6a846a5c.png

Uzyskanie dostępu do środowiska Cloud Shell i połączenie się z nim powinno zająć tylko kilka chwil.

9f0e51b578fecce5.png

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Większość zadań w tym module, a być może wszystkie, możesz wykonać w przeglądarce.

Po połączeniu z Cloud Shell zobaczysz, że uwierzytelnianie zostało już przeprowadzone, a projekt jest już ustawiony na Twój identyfikator projektu.

  1. Aby potwierdzić, że uwierzytelnianie zostało przeprowadzone, uruchom w Cloud Shell to polecenie:
gcloud auth list

Wynik polecenia

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Aby potwierdzić, że polecenie gcloud zna Twój projekt, uruchom w Cloud Shell to polecenie:
gcloud config list project

Wynik polecenia

[core]
project = <PROJECT_ID>

Jeśli nie, możesz go ustawić za pomocą tego polecenia:

gcloud config set project <PROJECT_ID>

Wynik polecenia

Updated property [core/project].

Sprawdź, czy interfejsy Cloud Functions API i Cloud Build API są włączone.

Aby sprawdzić, czy interfejsy Cloud Functions API i Cloud Build API są włączone, uruchom w Cloud Shell to polecenie:

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

Uwaga: polecenie gcloud functions deploy wywoła Cloud Build, który automatycznie skompiluje Twój kod do obrazu kontenera.

Pobieranie kodu źródłowego

W terminalu Cloud Shell uruchom te polecenia:

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

Sprawdź zawartość katalogu źródłowego:

ls

Powinny pojawić się te pliki:

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

3. Przedstawiamy funkcje HTTP Cloud Functions

Funkcje HTTP w Cloud Functions w Pythonie są pisane jako zwykłe funkcje Pythona. Funkcja musi przyjmować jeden argument flask.Request, który zwykle ma nazwę 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")

# ...

Możesz otworzyć plik w wybranym edytorze wiersza poleceń (nano, vim lub emacs). Możesz też otworzyć go w edytorze Cloud Shell po ustawieniu katalogu źródłowego jako obszaru roboczego:

cloudshell open-workspace .

Wdróżmy tę funkcję jako funkcję HTTP w Cloud Functions za pomocą polecenia gcloud functions deploy:

FUNCTION_NAME="hello_world"

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

Wynik polecenia:

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

Uwagi dotyczące opcji gcloud functions deploy:

  • --runtime: Określa środowisko wykonawcze języka. W przypadku Pythona może to być obecnie python37, python38, python39, python310 lub python312. Zobacz Środowiska wykonawcze.
  • --trigger-http: Funkcja zostanie przypisana do punktu końcowego. Żądania HTTP (POST, PUT, GET, DELETE i OPTIONS) wysyłane do punktu końcowego będą wywoływać wykonanie funkcji.
  • --allow-unauthenticated: funkcja będzie publiczna, co oznacza, że będzie dostępna dla wszystkich wywołujących bez sprawdzania uwierzytelniania.
  • Więcej informacji znajdziesz w artykule gcloud functions deploy.

Aby przetestować funkcję, możesz kliknąć adres URL httpsTrigger.url wyświetlany w wyniku polecenia powyżej. Adres URL możesz też pobrać programowo i wywołać funkcję za pomocą tych poleceń:

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

Powinien pojawić się następujący wynik:

Hello World! 👋

4. Pisanie funkcji HTTP w Cloud Functions, która przyjmuje argumenty

Funkcje są bardziej wszechstronne, gdy akceptują argumenty. Zdefiniujmy nową funkcję hello_name, która obsługuje parametr 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")

# ...

Wdróżmy tę nową funkcję:

FUNCTION_NAME="hello_name"

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

Wynik polecenia:

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

Aby przetestować funkcję, możesz kliknąć adres URL httpsTrigger.url wyświetlany w wyniku polecenia powyżej. Adres URL możesz też pobrać programowo i wywołać funkcję za pomocą tych poleceń:

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

Powinien pojawić się domyślny wynik:

Hello World! 🚀

Otrzymujesz domyślny wynik, ponieważ argument name nie jest ustawiony. Dodaj parametr do adresu URL:

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

Tym razem otrzymasz niestandardową odpowiedź:

Hello YOUR NAME! 🚀

Następnym krokiem jest dodanie testów jednostkowych, aby mieć pewność, że funkcje będą działać zgodnie z przeznaczeniem po zaktualizowaniu kodu źródłowego.

5. Testy pisemne

Funkcje HTTP w Cloud Functions w Pythonie są testowane za pomocą modułu unittest z biblioteki standardowej. Aby przetestować funkcję, nie musisz uruchamiać emulatora ani innej symulacji – wystarczy zwykły kod w Pythonie.

Oto jak wygląda test funkcji hello_worldhello_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. Testy w Pythonie są pisane w taki sam sposób jak inne pliki w tym języku. Zaczynają się od zestawu importów, a następnie definiują klasy i funkcje.
  2. Deklaracja testu ma postać class TestHello(TestCase). Musi to być klasa dziedzicząca po klasie unittest.TestCase.
  3. Klasa testowa zawiera metody, z których każda musi zaczynać się od test_ i reprezentować pojedynczy element testowania.
  4. Każdy przypadek testowy sprawdza jedną z naszych funkcji, symulując parametr request (czyli zastępując go fałszywym obiektem z danymi wymaganymi do testu).
  5. Po wywołaniu każdej funkcji test sprawdza odpowiedź HTTP, aby upewnić się, że jest zgodna z oczekiwaniami.

Ponieważ main.py zależy od flask, upewnij się, że w środowisku testowym jest zainstalowany framework Flask:

pip install flask

Instalacja Flaska daje wynik podobny do tego:

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

Przeprowadź te testy lokalnie:

python -m unittest

Trzy testy jednostkowe powinny zakończyć się powodzeniem:

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

OK

Następnie utworzysz nową funkcję, która zwraca logo „Python Powered”.

6. Pisanie funkcji HTTP w Cloud Functions „Python Powered”

Sprawmy, aby nowa funkcja była nieco bardziej rozrywkowa, zwracając obraz „Python Powered” w przypadku każdego żądania:

a7aaf656b78050fd.png

Poniżej znajdziesz kod, który to umożliwia:

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

Wdróż nową funkcję python_powered:

FUNCTION_NAME="python_powered"

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

Wynik polecenia:

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

Aby przetestować funkcję, kliknij adres URL httpsTrigger.url wyświetlany w wyniku polecenia powyżej. Jeśli wszystko działa prawidłowo, w nowej karcie przeglądarki zobaczysz logo „Python Powered”.

Następnie utworzysz aplikację, aby móc uruchomić i wypróbować funkcję lokalnie przed wdrożeniem.

7. Lokalne uruchamianie funkcji

Funkcję HTTP możesz uruchomić lokalnie, tworząc aplikację internetową i wywołując funkcję w ścieżce. Możesz go dodać w tym samym katalogu co funkcja. Plik o nazwie web_app.py zawiera te treści:

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. Ten plik tworzy aplikację Flask.
  2. Rejestruje ona ścieżkę w podstawowym adresie URL, która jest obsługiwana przez funkcję o nazwie index().
  3. Funkcja index() wywołuje następnie funkcję python_powered, przekazując jej bieżące żądanie.

Sprawdź, czy w środowisku programistycznym jest zainstalowany framework Flask:

pip install flask

Instalacja Flaska daje wynik podobny do tego:

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

Aby uruchomić tę aplikację lokalnie, wpisz to polecenie:

python web_app.py

Teraz użyj podglądu Cloud Shell w przeglądarce, aby przetestować aplikację internetową. W Cloud Shell kliknij przycisk „Podgląd w przeglądarce” i wybierz „Podejrzyj na porcie 8080”:

6c9ff9e5c692c58e.gif

Cloud Shell otworzy adres URL umożliwiający podgląd w nowym oknie przeglądarki, korzystając z własnej usługi pośredniczącej. Podgląd w przeglądarce ogranicza dostęp przez HTTPS tylko do Twojego konta użytkownika. Jeśli wszystko działa prawidłowo, zobaczysz logo „Python Powered”.

8e5c3ead11cfd103.png

8. Gratulacje!

b158ce75c3cccd6d.png

Wdrożono funkcje HTTP Cloud Functions, używając idiomatycznych funkcji, które obsługują żądania sieciowe za pomocą platformy Flask.

Cennik Cloud Functions zależy od tego, jak często wywoływana jest funkcja, i obejmuje poziom bezpłatny dla funkcji, które nie są często uruchamiane. Po zakończeniu testowania funkcji w Cloud Functions możesz je usunąć, wykonując te czynności:gcloud

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

Funkcje możesz też usunąć z konsoli Google Cloud.

Mamy nadzieję, że dobrze Ci się pracuje z Cloud Functions w Pythonie.